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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

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

92.73
/src/basic/path-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fnmatch.h>
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <unistd.h>
7

8
#include "alloc-util.h"
9
#include "chase.h"
10
#include "errno-util.h"
11
#include "extract-word.h"
12
#include "fd-util.h"
13
#include "fs-util.h"
14
#include "glob-util.h"
15
#include "log.h"
16
#include "path-util.h"
17
#include "stat-util.h"
18
#include "string-util.h"
19
#include "strv.h"
20
#include "time-util.h"
21

22
bool is_path(const char *p) {
118,582✔
23
        if (!p) /* A NULL pointer is definitely not a path */
118,582✔
24
                return false;
25

26
        return strchr(p, '/');
118,582✔
27
}
28

29
int path_split_and_make_absolute(const char *p, char ***ret) {
1,102✔
30
        _cleanup_strv_free_ char **l = NULL;
1,102✔
31
        int r;
1,102✔
32

33
        assert(p);
1,102✔
34
        assert(ret);
1,102✔
35

36
        l = strv_split(p, ":");
1,102✔
37
        if (!l)
1,102✔
38
                return -ENOMEM;
39

40
        r = path_strv_make_absolute_cwd(l);
1,102✔
41
        if (r < 0)
1,102✔
42
                return r;
43

44
        *ret = TAKE_PTR(l);
1,102✔
45
        return r;
1,102✔
46
}
47

48
char* path_make_absolute(const char *p, const char *prefix) {
2,284✔
49
        assert(p);
2,284✔
50

51
        /* Makes every item in the list an absolute path by prepending
52
         * the prefix, if specified and necessary */
53

54
        if (path_is_absolute(p) || isempty(prefix))
2,284✔
55
                return strdup(p);
312✔
56

57
        return path_join(prefix, p);
1,972✔
58
}
59

60
int safe_getcwd(char **ret) {
5,473✔
61
        _cleanup_free_ char *cwd = NULL;
5,473✔
62

63
        cwd = get_current_dir_name();
5,473✔
64
        if (!cwd)
5,473✔
UNCOV
65
                return negative_errno();
×
66

67
        /* Let's make sure the directory is really absolute, to protect us from the logic behind
68
         * CVE-2018-1000001 */
69
        if (cwd[0] != '/')
5,473✔
70
                return -ENOMEDIUM;
71

72
        if (ret)
5,473✔
73
                *ret = TAKE_PTR(cwd);
5,473✔
74

75
        return 0;
76
}
77

78
int path_make_absolute_cwd(const char *p, char **ret) {
3,231,193✔
79
        char *c;
3,231,193✔
80
        int r;
3,231,193✔
81

82
        assert(p);
3,231,193✔
83
        assert(ret);
3,231,193✔
84

85
        /* Similar to path_make_absolute(), but prefixes with the
86
         * current working directory. */
87

88
        if (path_is_absolute(p))
3,231,193✔
89
                c = strdup(p);
3,230,910✔
90
        else {
91
                _cleanup_free_ char *cwd = NULL;
283✔
92

93
                r = safe_getcwd(&cwd);
283✔
94
                if (r < 0)
283✔
UNCOV
95
                        return r;
×
96

97
                c = path_join(cwd, p);
283✔
98
        }
99
        if (!c)
3,231,193✔
100
                return -ENOMEM;
101

102
        *ret = c;
3,231,193✔
103
        return 0;
3,231,193✔
104
}
105

106
int path_make_relative(const char *from, const char *to, char **ret) {
863,054✔
107
        _cleanup_free_ char *result = NULL;
863,054✔
108
        unsigned n_parents;
863,054✔
109
        const char *f, *t;
863,054✔
110
        int r, k;
863,054✔
111
        char *p;
863,054✔
112

113
        assert(from);
863,054✔
114
        assert(to);
863,054✔
115
        assert(ret);
863,054✔
116

117
        /* Strips the common part, and adds ".." elements as necessary. */
118

119
        if (!path_is_absolute(from) || !path_is_absolute(to))
1,726,104✔
120
                return -EINVAL;
121

122
        for (;;) {
6,169,544✔
123
                r = path_find_first_component(&from, true, &f);
6,169,544✔
124
                if (r < 0)
6,169,544✔
125
                        return r;
126

127
                k = path_find_first_component(&to, true, &t);
6,169,544✔
128
                if (k < 0)
6,169,544✔
129
                        return k;
130

131
                if (r == 0) {
6,169,544✔
132
                        /* end of 'from' */
133
                        if (k == 0) {
709,109✔
134
                                /* from and to are equivalent. */
135
                                result = strdup(".");
4✔
136
                                if (!result)
4✔
137
                                        return -ENOMEM;
138
                        } else {
139
                                /* 'to' is inside of 'from'. */
140
                                r = path_simplify_alloc(t, &result);
709,105✔
141
                                if (r < 0)
709,105✔
142
                                        return r;
143

144
                                if (!path_is_valid(result))
709,105✔
145
                                        return -EINVAL;
146
                        }
147

148
                        *ret = TAKE_PTR(result);
709,109✔
149
                        return 0;
709,109✔
150
                }
151

152
                if (r != k || !strneq(f, t, r))
5,460,435✔
153
                        break;
154
        }
155

156
        /* If we're here, then "from_dir" has one or more elements that need to
157
         * be replaced with "..". */
158

159
        for (n_parents = 1;; n_parents++) {
189,572✔
160
                /* If this includes ".." we can't do a simple series of "..". */
161
                r = path_find_first_component(&from, false, &f);
343,513✔
162
                if (r < 0)
343,513✔
163
                        return r;
164
                if (r == 0)
343,511✔
165
                        break;
166
        }
167

168
        if (isempty(t) && n_parents * 3 > PATH_MAX)
153,939✔
169
                /* PATH_MAX is counted *with* the trailing NUL byte */
170
                return -EINVAL;
171

172
        result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t));
461,813✔
173
        if (!result)
153,939✔
174
                return -ENOMEM;
175

176
        for (p = result; n_parents > 0; n_parents--)
497,450✔
177
                p = mempcpy(p, "../", 3);
343,511✔
178

179
        if (isempty(t)) {
153,939✔
180
                /* Remove trailing slash and terminate string. */
181
                *(--p) = '\0';
2✔
182
                *ret = TAKE_PTR(result);
2✔
183
                return 0;
2✔
184
        }
185

186
        strcpy(p, t);
153,937✔
187

188
        path_simplify(result);
153,937✔
189

190
        if (!path_is_valid(result))
153,937✔
191
                return -EINVAL;
192

193
        *ret = TAKE_PTR(result);
153,937✔
194
        return 0;
153,937✔
195
}
196

197
int path_make_relative_parent(const char *from_child, const char *to, char **ret) {
154,022✔
198
        _cleanup_free_ char *from = NULL;
154,022✔
199
        int r;
154,022✔
200

201
        assert(from_child);
154,022✔
202
        assert(to);
154,022✔
203
        assert(ret);
154,022✔
204

205
        /* Similar to path_make_relative(), but provides the relative path from the parent directory of
206
         * 'from_child'. This may be useful when creating relative symlink.
207
         *
208
         * E.g.
209
         * - from = "/path/to/aaa", to = "/path/to/bbb"
210
         *      path_make_relative(from, to) = "../bbb"
211
         *      path_make_relative_parent(from, to) = "bbb"
212
         *
213
         * - from = "/path/to/aaa/bbb", to = "/path/to/ccc/ddd"
214
         *      path_make_relative(from, to) = "../../ccc/ddd"
215
         *      path_make_relative_parent(from, to) = "../ccc/ddd"
216
         */
217

218
        r = path_extract_directory(from_child, &from);
154,022✔
219
        if (r < 0)
154,022✔
220
                return r;
221

222
        return path_make_relative(from, to, ret);
154,021✔
223
}
224

225
char* path_startswith_strv(const char *p, char * const *strv) {
149,165✔
226
        assert(p);
149,165✔
227

228
        STRV_FOREACH(s, strv) {
556,918✔
229
                char *t;
432,335✔
230

231
                t = path_startswith(p, *s);
432,335✔
232
                if (t)
432,335✔
233
                        return t;
234
        }
235

236
        return NULL;
237
}
238

239
int path_strv_make_absolute_cwd(char **l) {
1,471✔
240
        int r;
1,471✔
241

242
        /* Goes through every item in the string list and makes it
243
         * absolute. This works in place and won't rollback any
244
         * changes on failure. */
245

246
        STRV_FOREACH(s, l) {
8,429✔
247
                char *t;
6,958✔
248

249
                r = path_make_absolute_cwd(*s, &t);
6,958✔
250
                if (r < 0)
6,958✔
UNCOV
251
                        return r;
×
252

253
                path_simplify(t);
6,958✔
254
                free_and_replace(*s, t);
6,958✔
255
        }
256

257
        return 0;
258
}
259

260
char** path_strv_resolve(char **l, const char *root) {
22,757✔
261
        unsigned k = 0;
22,757✔
262
        bool enomem = false;
22,757✔
263
        int r;
22,757✔
264

265
        if (strv_isempty(l))
45,514✔
266
                return l;
267

268
        /* Goes through every item in the string list and canonicalize
269
         * the path. This works in place and won't rollback any
270
         * changes on failure. */
271

272
        STRV_FOREACH(s, l) {
135,923✔
273
                _cleanup_free_ char *orig = NULL;
113,166✔
274
                char *t, *u;
113,166✔
275

276
                if (!path_is_absolute(*s)) {
113,166✔
UNCOV
277
                        free(*s);
×
UNCOV
278
                        continue;
×
279
                }
280

281
                if (root) {
113,166✔
282
                        orig = *s;
7✔
283
                        t = path_join(root, orig);
7✔
284
                        if (!t) {
7✔
UNCOV
285
                                enomem = true;
×
UNCOV
286
                                continue;
×
287
                        }
288
                } else
289
                        t = *s;
290

291
                r = chase(t, root, 0, &u, NULL);
113,166✔
292
                if (r == -ENOENT) {
113,166✔
293
                        if (root) {
94,855✔
294
                                u = TAKE_PTR(orig);
2✔
295
                                free(t);
2✔
296
                        } else
297
                                u = t;
94,853✔
298
                } else if (r < 0) {
18,311✔
UNCOV
299
                        free(t);
×
300

UNCOV
301
                        if (r == -ENOMEM)
×
302
                                enomem = true;
×
303

304
                        continue;
×
305
                } else if (root) {
18,311✔
306
                        char *x;
5✔
307

308
                        free(t);
5✔
309
                        x = path_startswith(u, root);
5✔
310
                        if (x) {
5✔
311
                                /* restore the slash if it was lost */
312
                                if (!startswith(x, "/"))
5✔
313
                                        *(--x) = '/';
5✔
314

315
                                t = strdup(x);
5✔
316
                                free(u);
5✔
317
                                if (!t) {
5✔
UNCOV
318
                                        enomem = true;
×
UNCOV
319
                                        continue;
×
320
                                }
321
                                u = t;
5✔
322
                        } else {
323
                                /* canonicalized path goes outside of
324
                                 * prefix, keep the original path instead */
UNCOV
325
                                free_and_replace(u, orig);
×
326
                        }
327
                } else
328
                        free(t);
18,306✔
329

330
                l[k++] = u;
113,166✔
331
        }
332

333
        l[k] = NULL;
22,757✔
334

335
        if (enomem)
22,757✔
UNCOV
336
                return NULL;
×
337

338
        return l;
339
}
340

341
char** path_strv_resolve_uniq(char **l, const char *root) {
22,756✔
342

343
        if (strv_isempty(l))
22,756✔
344
                return l;
345

346
        if (!path_strv_resolve(l, root))
22,756✔
347
                return NULL;
348

349
        return strv_uniq(l);
22,756✔
350
}
351

352
char* skip_leading_slash(const char *p) {
12✔
353
        return skip_leading_chars(p, "/");
12✔
354
}
355

356
char* path_simplify_full(char *path, PathSimplifyFlags flags) {
8,442,176✔
357
        bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
8,442,176✔
358
        char *f = path;
8,442,176✔
359
        int r;
8,442,176✔
360

361
        /* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
362
         * Modifies the passed string in-place.
363
         *
364
         * ///foo//./bar/.   becomes /foo/bar
365
         * .//./foo//./bar/. becomes foo/bar
366
         * /../foo/bar       becomes /foo/bar
367
         * /../foo/bar/..    becomes /foo/bar/..
368
         */
369

370
        if (isempty(path))
8,442,176✔
371
                return path;
372

373
        keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
8,416,906✔
374

375
        absolute = path_is_absolute(path);
8,416,906✔
376
        f += absolute;  /* Keep leading /, if present. */
8,416,906✔
377

378
        for (const char *p = f;;) {
8,416,906✔
379
                const char *e;
34,051,588✔
380

381
                r = path_find_first_component(&p, true, &e);
34,051,588✔
382
                if (r == 0)
34,051,588✔
383
                        break;
384

385
                if (r > 0 && absolute && beginning && path_startswith(e, ".."))
25,634,686✔
386
                        /* If we're at the beginning of an absolute path, we can safely skip ".." */
387
                        continue;
79✔
388

389
                beginning = false;
25,634,607✔
390

391
                if (add_slash)
25,634,607✔
392
                        *f++ = '/';
17,225,684✔
393

394
                if (r < 0) {
25,634,607✔
395
                        /* if path is invalid, then refuse to simplify the remaining part. */
396
                        memmove(f, p, strlen(p) + 1);
4✔
397
                        return path;
4✔
398
                }
399

400
                memmove(f, e, r);
25,634,603✔
401
                f += r;
25,634,603✔
402

403
                add_slash = true;
25,634,603✔
404
        }
405

406
        /* Special rule, if we stripped everything, we need a "." for the current directory. */
407
        if (f == path)
8,416,902✔
408
                *f++ = '.';
7✔
409

410
        if (*(f-1) != '/' && keep_trailing_slash)
8,416,902✔
411
                *f++ = '/';
12✔
412

413
        *f = '\0';
8,416,902✔
414
        return path;
8,416,902✔
415
}
416

417
int path_simplify_alloc(const char *path, char **ret) {
1,666,562✔
418
        assert(ret);
1,666,562✔
419

420
        if (!path) {
1,666,562✔
421
                *ret = NULL;
13,470✔
422
                return 0;
13,470✔
423
        }
424

425
        char *t = strdup(path);
1,653,092✔
426
        if (!t)
1,653,092✔
427
                return -ENOMEM;
428

429
        *ret = path_simplify(t);
1,653,092✔
430
        return 0;
1,653,092✔
431
}
432

433
char* path_startswith_full(const char *original_path, const char *prefix, PathStartWithFlags flags) {
51,518,422✔
434
        assert(original_path);
51,518,422✔
435
        assert(prefix);
51,518,422✔
436

437
        /* Returns a pointer to the start of the first component after the parts matched by
438
         * the prefix, iff
439
         * - both paths are absolute or both paths are relative,
440
         * and
441
         * - each component in prefix in turn matches a component in path at the same position.
442
         * An empty string will be returned when the prefix and path are equivalent.
443
         *
444
         * Returns NULL otherwise.
445
         */
446

447
        const char *path = original_path;
51,518,422✔
448

449
        if ((path[0] == '/') != (prefix[0] == '/'))
51,518,422✔
450
                return NULL;
51,518,422✔
451

452
        for (;;) {
44,857,871✔
453
                const char *p, *q;
96,303,970✔
454
                int m, n;
96,303,970✔
455

456
                m = path_find_first_component(&path, !FLAGS_SET(flags, PATH_STARTSWITH_REFUSE_DOT_DOT), &p);
96,303,970✔
457
                if (m < 0)
96,303,970✔
458
                        return NULL;
51,446,099✔
459

460
                n = path_find_first_component(&prefix, !FLAGS_SET(flags, PATH_STARTSWITH_REFUSE_DOT_DOT), &q);
96,303,970✔
461
                if (n < 0)
96,303,970✔
462
                        return NULL;
463

464
                if (n == 0) {
96,303,970✔
465
                        if (!p)
37,433,605✔
466
                                p = path;
190,466✔
467

468
                        if (FLAGS_SET(flags, PATH_STARTSWITH_RETURN_LEADING_SLASH)) {
37,433,605✔
469

470
                                if (p <= original_path)
7✔
471
                                        return NULL;
472

473
                                p--;
7✔
474

475
                                if (*p != '/')
7✔
476
                                        return NULL;
477
                        }
478

479
                        return (char*) p;
37,433,604✔
480
                }
481

482
                if (m != n)
58,870,365✔
483
                        return NULL;
484

485
                if (!strneq(p, q, m))
47,703,238✔
486
                        return NULL;
487
        }
488
}
489

490
int path_compare(const char *a, const char *b) {
21,085,399✔
491
        int r;
21,085,399✔
492

493
        /* Order NULL before non-NULL */
494
        r = CMP(!!a, !!b);
21,085,399✔
495
        if (r != 0)
21,064,351✔
496
                return r;
21,051✔
497

498
        /* A relative path and an absolute path must not compare as equal.
499
         * Which one is sorted before the other does not really matter.
500
         * Here a relative path is ordered before an absolute path. */
501
        r = CMP(path_is_absolute(a), path_is_absolute(b));
42,128,694✔
502
        if (r != 0)
20,805,127✔
503
                return r;
301,159✔
504

505
        for (;;) {
18,014,358✔
506
                const char *aa, *bb;
38,777,547✔
507
                int j, k;
38,777,547✔
508

509
                j = path_find_first_component(&a, true, &aa);
38,777,547✔
510
                k = path_find_first_component(&b, true, &bb);
38,777,547✔
511

512
                if (j < 0 || k < 0) {
38,777,547✔
513
                        /* When one of paths is invalid, order invalid path after valid one. */
514
                        r = CMP(j < 0, k < 0);
2✔
UNCOV
515
                        if (r != 0)
×
516
                                return r;
20,763,189✔
517

518
                        /* fallback to use strcmp() if both paths are invalid. */
UNCOV
519
                        return strcmp(a, b);
×
520
                }
521

522
                /* Order prefixes first: "/foo" before "/foo/bar" */
523
                if (j == 0) {
38,777,545✔
524
                        if (k == 0)
3,757,706✔
525
                                return 0;
526
                        return -1;
121,250✔
527
                }
528
                if (k == 0)
35,019,839✔
529
                        return 1;
530

531
                /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
532
                r = memcmp(aa, bb, MIN(j, k));
32,509,135✔
533
                if (r != 0)
32,509,135✔
534
                        return r;
535

536
                /* Sort "/foo/a" before "/foo/aaa" */
537
                r = CMP(j, k);
18,099,924✔
538
                if (r != 0)
18,044,532✔
539
                        return r;
85,566✔
540
        }
541
}
542

543
int path_compare_filename(const char *a, const char *b) {
116,140✔
544
        _cleanup_free_ char *fa = NULL, *fb = NULL;
116,140✔
545
        int r, j, k;
116,140✔
546

547
        /* Order NULL before non-NULL */
548
        r = CMP(!!a, !!b);
116,140✔
549
        if (r != 0)
116,140✔
UNCOV
550
                return r;
×
551

552
        j = path_extract_filename(a, &fa);
116,140✔
553
        k = path_extract_filename(b, &fb);
116,140✔
554

555
        /* When one of paths is "." or root, then order it earlier. */
556
        r = CMP(j != -EADDRNOTAVAIL, k != -EADDRNOTAVAIL);
116,140✔
557
        if (r != 0)
116,136✔
558
                return r;
8✔
559

560
        /* When one of paths is invalid (or we get OOM), order invalid path after valid one. */
561
        r = CMP(j < 0, k < 0);
116,132✔
562
        if (r != 0)
116,132✔
UNCOV
563
                return r;
×
564

565
        /* fallback to use strcmp() if both paths are invalid. */
566
        if (j < 0)
116,132✔
567
                return strcmp(a, b);
30✔
568

569
        return strcmp(fa, fb);
116,102✔
570
}
571

572
int path_equal_or_inode_same_full(const char *a, const char *b, int flags) {
51,826✔
573
        /* Returns true if paths are of the same entry, false if not, <0 on error. */
574

575
        if (path_equal(a, b))
51,826✔
576
                return 1;
577

578
        if (!a || !b)
479✔
579
                return 0;
580

581
        return inode_same(a, b, flags);
479✔
582
}
583

584
char* path_extend_internal(char **x, ...) {
23,502,218✔
585
        size_t sz, old_sz;
23,502,218✔
586
        char *q, *nx;
23,502,218✔
587
        const char *p;
23,502,218✔
588
        va_list ap;
23,502,218✔
589
        bool slash;
23,502,218✔
590

591
        /* Joins all listed strings until the sentinel and places a "/" between them unless the strings
592
         * end/begin already with one so that it is unnecessary. Note that slashes which are already
593
         * duplicate won't be removed. The string returned is hence always equal to or longer than the sum of
594
         * the lengths of the individual strings.
595
         *
596
         * The first argument may be an already allocated string that is extended via realloc() if
597
         * non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the
598
         * first parameter to distinguish the two operations.
599
         *
600
         * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of
601
         * which some are optional.
602
         *
603
         * Examples:
604
         *
605
         * path_join("foo", "bar") → "foo/bar"
606
         * path_join("foo/", "bar") → "foo/bar"
607
         * path_join("", "foo", "", "bar", "") → "foo/bar" */
608

609
        sz = old_sz = x ? strlen_ptr(*x) : 0;
23,502,218✔
610
        va_start(ap, x);
23,502,218✔
611
        while ((p = va_arg(ap, char*)) != POINTER_MAX) {
53,028,597✔
612
                size_t add;
29,526,379✔
613

614
                if (isempty(p))
29,526,379✔
615
                        continue;
617,384✔
616

617
                add = 1 + strlen(p);
28,908,995✔
618
                if (sz > SIZE_MAX - add) { /* overflow check */
28,908,995✔
UNCOV
619
                        va_end(ap);
×
UNCOV
620
                        return NULL;
×
621
                }
622

623
                sz += add;
28,908,995✔
624
        }
625
        va_end(ap);
23,502,218✔
626

627
        nx = realloc(x ? *x : NULL, GREEDY_ALLOC_ROUND_UP(sz+1));
23,502,218✔
628
        if (!nx)
23,502,218✔
629
                return NULL;
630
        if (x)
23,502,218✔
631
                *x = nx;
17,820,576✔
632

633
        if (old_sz > 0)
23,502,218✔
634
                slash = nx[old_sz-1] == '/';
17,749,078✔
635
        else {
636
                nx[old_sz] = 0;
5,753,140✔
637
                slash = true; /* no need to generate a slash anymore */
5,753,140✔
638
        }
639

640
        q = nx + old_sz;
23,502,218✔
641

642
        va_start(ap, x);
23,502,218✔
643
        while ((p = va_arg(ap, char*)) != POINTER_MAX) {
53,028,597✔
644
                if (isempty(p))
29,526,379✔
645
                        continue;
617,384✔
646

647
                if (!slash && p[0] != '/')
28,908,995✔
648
                        *(q++) = '/';
17,943,069✔
649

650
                q = stpcpy(q, p);
28,908,995✔
651
                slash = endswith(p, "/");
28,908,995✔
652
        }
653
        va_end(ap);
23,502,218✔
654

655
        return nx;
23,502,218✔
656
}
657

658
int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd) {
27,846✔
659
        _cleanup_close_ int fd = -EBADF;
27,846✔
660
        _cleanup_free_ char *resolved = NULL;
27,846✔
661
        int r;
27,846✔
662

663
        assert(name);
27,846✔
664

665
        /* Function chase() is invoked only when root is not NULL, as using it regardless of
666
         * root value would alter the behavior of existing callers for example: /bin/sleep would become
667
         * /usr/bin/sleep when find_executables is called. Hence, this function should be invoked when
668
         * needed to avoid unforeseen regression or other complicated changes. */
669
        if (root) {
27,846✔
670
                /* prefix root to name in case full paths are not specified */
671
                r = chase(name, root, CHASE_PREFIX_ROOT, &resolved, &fd);
4✔
672
                if (r < 0)
4✔
673
                        return r;
674

675
                name = resolved;
4✔
676
        } else {
677
                /* We need to use O_PATH because there may be executables for which we have only exec permissions,
678
                 * but not read (usually suid executables). */
679
                fd = open(name, O_PATH|O_CLOEXEC);
27,842✔
680
                if (fd < 0)
27,842✔
681
                        return -errno;
4,319✔
682
        }
683

684
        r = fd_verify_regular(fd);
23,527✔
685
        if (r < 0)
23,527✔
686
                return r;
687

688
        r = access_fd(fd, X_OK);
23,524✔
689
        if (r == -ENOSYS)
23,524✔
690
                /* /proc/ is not mounted. Fall back to access(). */
UNCOV
691
                r = RET_NERRNO(access(name, X_OK));
×
692
        if (r < 0)
23,524✔
693
                return r;
694

695
        if (ret_path) {
23,523✔
696
                if (resolved)
23,167✔
UNCOV
697
                        *ret_path = TAKE_PTR(resolved);
×
698
                else {
699
                        r = path_make_absolute_cwd(name, ret_path);
23,167✔
700
                        if (r < 0)
23,167✔
701
                                return r;
702

703
                        path_simplify(*ret_path);
23,167✔
704
                }
705
        }
706

707
        if (ret_fd)
23,523✔
708
                *ret_fd = TAKE_FD(fd);
21,880✔
709

710
        return 0;
711
}
712

713
int find_executable_full(
23,533✔
714
                const char *name,
715
                const char *root,
716
                char * const *exec_search_path,
717
                bool use_path_envvar,
718
                char **ret_filename,
719
                int *ret_fd) {
720

721
        int last_error = -ENOENT, r = 0;
23,533✔
722

723
        assert(name);
23,533✔
724

725
        if (is_path(name))
23,533✔
726
                return open_and_check_executable(name, root, ret_filename, ret_fd);
23,533✔
727

728
        if (exec_search_path) {
3,370✔
729
                STRV_FOREACH(element, exec_search_path) {
6✔
730
                        _cleanup_free_ char *full_path = NULL;
5✔
731

732
                        if (!path_is_absolute(*element)) {
5✔
UNCOV
733
                                log_debug("Exec search path '%s' isn't absolute, ignoring.", *element);
×
UNCOV
734
                                continue;
×
735
                        }
736

737
                        full_path = path_join(*element, name);
5✔
738
                        if (!full_path)
5✔
739
                                return -ENOMEM;
740

741
                        r = open_and_check_executable(full_path, root, ret_filename, ret_fd);
5✔
742
                        if (r >= 0)
5✔
743
                                return 0;
744
                        if (r != -EACCES)
4✔
745
                                last_error = r;
4✔
746
                }
747
                return last_error;
748
        }
749

750
        const char *p = NULL;
3,368✔
751

752
        if (use_path_envvar)
3,368✔
753
                /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
754
                 * binary. */
755
                p = getenv("PATH");
952✔
756
        if (!p)
3,368✔
757
                p = default_PATH();
2,426✔
758

759
        /* Resolve a single-component name to a full path */
760
        for (;;) {
7,682✔
761
                _cleanup_free_ char *element = NULL;
7,678✔
762

763
                r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
7,682✔
764
                if (r < 0)
7,682✔
765
                        return r;
766
                if (r == 0)
7,682✔
767
                        break;
768

769
                if (!path_is_absolute(element)) {
7,678✔
UNCOV
770
                        log_debug("Exec search path '%s' isn't absolute, ignoring.", element);
×
UNCOV
771
                        continue;
×
772
                }
773

774
                if (!path_extend(&element, name))
7,678✔
775
                        return -ENOMEM;
776

777
                r = open_and_check_executable(element, root, ret_filename, ret_fd);
7,678✔
778
                if (r >= 0) /* Found it! */
7,678✔
779
                        return 0;
780
                /* PATH entries which we don't have access to are ignored, as per tradition. */
781
                if (r != -EACCES)
4,314✔
782
                        last_error = r;
4,314✔
783
        }
784

785
        return last_error;
4✔
786
}
787

UNCOV
788
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
×
UNCOV
789
        bool changed = false, originally_unset;
×
790

791
        assert(timestamp);
×
792

UNCOV
793
        if (!paths)
×
794
                return false;
795

796
        originally_unset = *timestamp == 0;
×
797

UNCOV
798
        STRV_FOREACH(i, paths) {
×
799
                struct stat stats;
×
UNCOV
800
                usec_t u;
×
801

802
                if (stat(*i, &stats) < 0)
×
803
                        continue;
×
804

805
                u = timespec_load(&stats.st_mtim);
×
806

807
                /* check first */
808
                if (*timestamp >= u)
×
UNCOV
809
                        continue;
×
810

811
                log_debug(originally_unset ? "Loaded timestamp for '%s'." : "Timestamp of '%s' changed.", *i);
×
812

813
                /* update timestamp */
814
                if (update) {
×
UNCOV
815
                        *timestamp = u;
×
UNCOV
816
                        changed = true;
×
817
                } else
818
                        return true;
×
819
        }
820

821
        return changed;
822
}
823

824
static int executable_is_good(const char *executable) {
184✔
825
        _cleanup_free_ char *p = NULL, *d = NULL;
184✔
826
        int r;
184✔
827

828
        r = find_executable(executable, &p);
184✔
829
        if (r == -ENOENT)
184✔
830
                return 0;
831
        if (r < 0)
182✔
832
                return r;
833

834
        /* An fsck that is linked to /bin/true is a non-existent fsck */
835

836
        r = readlink_malloc(p, &d);
182✔
837
        if (r == -EINVAL) /* not a symlink */
182✔
838
                return 1;
839
        if (r < 0)
64✔
840
                return r;
841

842
        return !PATH_IN_SET(d, "true"
64✔
843
                               "/bin/true",
844
                               "/usr/bin/true",
845
                               "/dev/null");
846
}
847

848
int fsck_exists(void) {
99✔
849
        return executable_is_good("fsck");
99✔
850
}
851

852
int fsck_exists_for_fstype(const char *fstype) {
85✔
853
        const char *checker;
85✔
854
        int r;
85✔
855

856
        assert(fstype);
85✔
857

858
        if (streq(fstype, "auto"))
85✔
859
                return -EINVAL;
860

861
        r = fsck_exists();
85✔
862
        if (r <= 0)
85✔
863
                return r;
864

865
        checker = strjoina("fsck.", fstype);
425✔
866
        return executable_is_good(checker);
85✔
867
}
868

869
static const char* skip_slash_or_dot(const char *p) {
905,802,087✔
870
        for (; !isempty(p); p++) {
1,398,015,488✔
871
                if (*p == '/')
1,209,863,296✔
872
                        continue;
492,212,604✔
873
                if (startswith(p, "./")) {
717,650,692✔
874
                        p++;
797✔
875
                        continue;
797✔
876
                }
877
                break;
878
        }
879
        return p;
905,802,087✔
880
}
881

882
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret) {
493,765,919✔
883
        const char *q, *first, *end_first, *next;
493,765,919✔
884
        size_t len;
493,765,919✔
885

886
        assert(p);
493,765,919✔
887

888
        /* When a path is input, then returns the pointer to the first component and its length, and
889
         * move the input pointer to the next component or nul. This skips both over any '/'
890
         * immediately *before* and *after* the first component before returning.
891
         *
892
         * Examples
893
         *   Input:  p: "//.//aaa///bbbbb/cc"
894
         *   Output: p: "bbbbb///cc"
895
         *           ret: "aaa///bbbbb/cc"
896
         *           return value: 3 (== strlen("aaa"))
897
         *
898
         *   Input:  p: "aaa//"
899
         *   Output: p: (pointer to NUL)
900
         *           ret: "aaa//"
901
         *           return value: 3 (== strlen("aaa"))
902
         *
903
         *   Input:  p: "/", ".", ""
904
         *   Output: p: (pointer to NUL)
905
         *           ret: NULL
906
         *           return value: 0
907
         *
908
         *   Input:  p: NULL
909
         *   Output: p: NULL
910
         *           ret: NULL
911
         *           return value: 0
912
         *
913
         *   Input:  p: "(too long component)"
914
         *   Output: return value: -EINVAL
915
         *
916
         *   (when accept_dot_dot is false)
917
         *   Input:  p: "//..//aaa///bbbbb/cc"
918
         *   Output: return value: -EINVAL
919
         */
920

921
        q = *p;
493,765,919✔
922

923
        first = skip_slash_or_dot(q);
493,765,919✔
924
        if (isempty(first)) {
493,765,919✔
925
                *p = first;
81,722,250✔
926
                if (ret)
81,722,250✔
927
                        *ret = NULL;
81,716,710✔
928
                return 0;
81,722,250✔
929
        }
930
        if (streq(first, ".")) {
412,043,669✔
931
                *p = first + 1;
7,312✔
932
                if (ret)
7,312✔
933
                        *ret = NULL;
7,254✔
934
                return 0;
7,312✔
935
        }
936

937
        end_first = strchrnul(first, '/');
412,036,357✔
938
        len = end_first - first;
412,036,357✔
939

940
        if (len > NAME_MAX)
412,036,357✔
941
                return -EINVAL;
942
        if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.')
412,036,205✔
943
                return -EINVAL;
944

945
        next = skip_slash_or_dot(end_first);
412,036,168✔
946

947
        *p = next + streq(next, ".");
412,036,168✔
948
        if (ret)
412,036,168✔
949
                *ret = first;
377,555,729✔
950
        return len;
412,036,168✔
951
}
952

953
static const char* skip_slash_or_dot_backward(const char *path, const char *q) {
14,635,524✔
954
        assert(path);
14,635,524✔
955
        assert(!q || q >= path);
14,635,524✔
956

957
        for (; q; q = PTR_SUB1(q, path)) {
29,835,225✔
958
                if (*q == '/')
22,024,078✔
959
                        continue;
7,630,091✔
960
                if (q > path && strneq(q - 1, "/.", 2))
14,393,987✔
961
                        continue;
264✔
962
                if (q == path && *q == '.')
14,393,723✔
963
                        continue;
81✔
964
                break;
965
        }
966
        return q;
14,635,524✔
967
}
968

969
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret) {
7,325,551✔
970
        const char *q, *last_end, *last_begin;
7,325,551✔
971
        size_t len;
7,325,551✔
972

973
        /* Similar to path_find_first_component(), but search components from the end.
974
        *
975
        * Examples
976
        *   Input:  path: "//.//aaa///bbbbb/cc//././"
977
        *           next: NULL
978
        *   Output: next: "/cc//././"
979
        *           ret: "cc//././"
980
        *           return value: 2 (== strlen("cc"))
981
        *
982
        *   Input:  path: "//.//aaa///bbbbb/cc//././"
983
        *           next: "/cc//././"
984
        *   Output: next: "///bbbbb/cc//././"
985
        *           ret: "bbbbb/cc//././"
986
        *           return value: 5 (== strlen("bbbbb"))
987
        *
988
        *   Input:  path: "//.//aaa///bbbbb/cc//././"
989
        *           next: "///bbbbb/cc//././"
990
        *   Output: next: "//.//aaa///bbbbb/cc//././" (next == path)
991
        *           ret: "aaa///bbbbb/cc//././"
992
        *           return value: 3 (== strlen("aaa"))
993
        *
994
        *   Input:  path: "/", ".", "", or NULL
995
        *   Output: next: equivalent to path
996
        *           ret: NULL
997
        *           return value: 0
998
        *
999
        *   Input:  path: "(too long component)"
1000
        *   Output: return value: -EINVAL
1001
        *
1002
        *   (when accept_dot_dot is false)
1003
        *   Input:  path: "//..//aaa///bbbbb/cc/..//"
1004
        *   Output: return value: -EINVAL
1005
        */
1006

1007
        if (isempty(path)) {
7,325,551✔
1008
                if (next)
9✔
1009
                        *next = path;
9✔
1010
                if (ret)
9✔
1011
                        *ret = NULL;
9✔
1012
                return 0;
9✔
1013
        }
1014

1015
        if (next && *next) {
7,325,542✔
1016
                if (*next < path || *next > path + strlen(path))
35,035✔
1017
                        return -EINVAL;
1018
                if (*next == path) {
35,035✔
1019
                        if (ret)
9✔
1020
                                *ret = NULL;
9✔
1021
                        return 0;
9✔
1022
                }
1023
                if (!IN_SET(**next, '\0', '/'))
35,026✔
1024
                        return -EINVAL;
1025
                q = *next - 1;
35,026✔
1026
        } else
1027
                q = path + strlen(path) - 1;
7,290,507✔
1028

1029
        q = skip_slash_or_dot_backward(path, q);
7,325,533✔
1030
        if (!q || /* the root directory */
7,325,533✔
1031
            (q == path && *q == '.')) { /* path is "." or "./" */
34✔
1032
                if (next)
15,470✔
1033
                        *next = path;
15,470✔
1034
                if (ret)
15,470✔
1035
                        *ret = NULL;
15,467✔
1036
                return 0;
15,470✔
1037
        }
1038

1039
        last_end = q + 1;
7,310,063✔
1040

1041
        while (q && *q != '/')
118,717,230✔
1042
                q = PTR_SUB1(q, path);
104,097,104✔
1043

1044
        last_begin = q ? q + 1 : path;
7,310,063✔
1045
        len = last_end - last_begin;
7,310,063✔
1046

1047
        if (len > NAME_MAX)
7,310,063✔
1048
                return -EINVAL;
1049
        if (!accept_dot_dot && len == 2 && strneq(last_begin, "..", 2))
7,310,059✔
1050
                return -EINVAL;
1051

1052
        if (next) {
7,310,034✔
1053
                q = skip_slash_or_dot_backward(path, q);
7,309,991✔
1054
                *next = q ? q + 1 : path;
7,309,991✔
1055
        }
1056

1057
        if (ret)
7,310,034✔
1058
                *ret = last_begin;
6,371,514✔
1059
        return len;
7,310,034✔
1060
}
1061

1062
const char* last_path_component(const char *path) {
159,266✔
1063

1064
        /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
1065
         *
1066
         *    a/b/c → c
1067
         *    a/b/c/ → c/
1068
         *    x → x
1069
         *    x/ → x/
1070
         *    /y → y
1071
         *    /y/ → y/
1072
         *    / → /
1073
         *    // → /
1074
         *    /foo/a → a
1075
         *    /foo/a/ → a/
1076
         *
1077
         *    Also, the empty string is mapped to itself.
1078
         *
1079
         * This is different than basename(), which returns "" when a trailing slash is present.
1080
         *
1081
         * This always succeeds (except if you pass NULL in which case it returns NULL, too).
1082
         */
1083

1084
        unsigned l, k;
159,266✔
1085

1086
        if (!path)
159,266✔
1087
                return NULL;
1088

1089
        l = k = strlen(path);
159,265✔
1090
        if (l == 0) /* special case — an empty string */
159,265✔
1091
                return path;
1092

1093
        while (k > 0 && path[k-1] == '/')
159,277✔
1094
                k--;
1095

1096
        if (k == 0) /* the root directory */
159,264✔
1097
                return path + l - 1;
3✔
1098

1099
        while (k > 0 && path[k-1] != '/')
1,369,591✔
1100
                k--;
1101

1102
        return path + k;
159,261✔
1103
}
1104

1105
int path_extract_filename(const char *path, char **ret) {
2,097,506✔
1106
        _cleanup_free_ char *a = NULL;
4,195,012✔
1107
        const char *c, *next = NULL;
2,097,506✔
1108
        int r;
2,097,506✔
1109

1110
        /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
1111
         * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
1112
         * slashes. Returns:
1113
         *
1114
         * -EINVAL        → if the path is not valid
1115
         * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir
1116
         *                  itself or "." is specified
1117
         * -ENOMEM        → no memory
1118
         *
1119
         * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to
1120
         * indicate the referenced file must be a directory.
1121
         *
1122
         * This function guarantees to return a fully valid filename, i.e. one that passes
1123
         * filename_is_valid() – this means "." and ".." are not accepted. */
1124

1125
        if (!path_is_valid(path))
2,097,506✔
1126
                return -EINVAL;
1127

1128
        r = path_find_last_component(path, false, &next, &c);
2,097,503✔
1129
        if (r < 0)
2,097,503✔
1130
                return r;
1131
        if (r == 0) /* root directory */
2,097,489✔
1132
                return -EADDRNOTAVAIL;
1133

1134
        a = strndup(c, r);
2,097,405✔
1135
        if (!a)
2,097,405✔
1136
                return -ENOMEM;
1137

1138
        *ret = TAKE_PTR(a);
2,097,405✔
1139
        return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
2,097,405✔
1140
}
1141

1142
int path_extract_directory(const char *path, char **ret) {
4,254,415✔
1143
        const char *c, *next = NULL;
4,254,415✔
1144
        int r;
4,254,415✔
1145

1146
        /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
1147
         *
1148
         * -EINVAL        → if the path is not valid
1149
         * -EDESTADDRREQ  → if no directory was specified in the passed in path, i.e. only a filename was passed
1150
         * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e.
1151
         *                   the root dir itself or "." was specified
1152
         * -ENOMEM        → no memory (surprise!)
1153
         *
1154
         * This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
1155
         */
1156

1157
        r = path_find_last_component(path, false, &next, &c);
4,254,415✔
1158
        if (r < 0)
4,254,415✔
1159
                return r;
4,254,415✔
1160
        if (r == 0) /* empty or root */
4,254,409✔
1161
                return isempty(path) ? -EINVAL : -EADDRNOTAVAIL;
30,755✔
1162
        if (next == path) {
4,239,031✔
1163
                if (*path != '/') /* filename only */
26,916✔
1164
                        return -EDESTADDRREQ;
1165

1166
                return strdup_to(ret, "/");
19,805✔
1167
        }
1168

1169
        _cleanup_free_ char *a = strndup(path, next - path);
4,212,115✔
1170
        if (!a)
4,212,115✔
1171
                return -ENOMEM;
1172

1173
        path_simplify(a);
4,212,115✔
1174

1175
        if (!path_is_valid(a))
4,212,115✔
1176
                return -EINVAL;
1177

1178
        if (ret)
4,212,115✔
1179
                *ret = TAKE_PTR(a);
4,212,115✔
1180

1181
        return 0;
1182
}
1183

1184
bool filename_part_is_valid(const char *p) {
1,010,246✔
1185
        const char *e;
1,010,246✔
1186

1187
        /* Checks f the specified string is OK to be *part* of a filename. This is different from
1188
         * filename_is_valid() as "." and ".." and "" are OK by this call, but not by filename_is_valid(). */
1189

1190
        if (!p)
1,010,246✔
1191
                return false;
1192

1193
        e = strchrnul(p, '/');
1,010,246✔
1194
        if (*e != 0)
1,010,246✔
1195
                return false;
1196

1197
        if (e - p > NAME_MAX) /* NAME_MAX is counted *without* the trailing NUL byte */
997,366✔
1198
                return false;
3✔
1199

1200
        return true;
1201
}
1202

1203
bool filename_is_valid(const char *p) {
1,006,130✔
1204

1205
        if (isempty(p))
1,006,130✔
1206
                return false;
1207

1208
        if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
1,006,119✔
1209
                return false;
1210

1211
        return filename_part_is_valid(p);
1,006,106✔
1212
}
1213

1214
bool path_is_valid_full(const char *p, bool accept_dot_dot) {
11,065,446✔
1215
        if (isempty(p))
11,065,446✔
1216
                return false;
1217

1218
        for (const char *e = p;;) {
11,065,438✔
1219
                int r;
34,486,073✔
1220

1221
                r = path_find_first_component(&e, accept_dot_dot, NULL);
34,486,073✔
1222
                if (r < 0)
34,486,073✔
1223
                        return false;
11,065,438✔
1224

1225
                if (e - p >= PATH_MAX) /* Already reached the maximum length for a path? (PATH_MAX is counted
34,486,037✔
1226
                                        * *with* the trailing NUL byte) */
1227
                        return false;
1228
                if (*e == 0)           /* End of string? Yay! */
34,486,034✔
1229
                        return true;
1230
        }
1231
}
1232

1233
bool path_is_normalized(const char *p) {
705,592✔
1234
        if (!path_is_safe(p))
705,592✔
1235
                return false;
1236

1237
        if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
705,567✔
1238
                return false;
1239

1240
        if (strstr(p, "//"))
705,564✔
1241
                return false;
1✔
1242

1243
        return true;
1244
}
1245

1246
int file_in_same_dir(const char *path, const char *filename, char **ret) {
265✔
1247
        _cleanup_free_ char *b = NULL;
265✔
1248
        int r;
265✔
1249

1250
        assert(path);
265✔
1251
        assert(filename);
265✔
1252
        assert(ret);
265✔
1253

1254
        /* This removes the last component of path and appends filename, unless the latter is absolute anyway
1255
         * or the former isn't */
1256

1257
        if (path_is_absolute(filename))
265✔
1258
                b = strdup(filename);
17✔
1259
        else {
1260
                _cleanup_free_ char *dn = NULL;
248✔
1261

1262
                r = path_extract_directory(path, &dn);
248✔
1263
                if (r == -EDESTADDRREQ) /* no path prefix */
248✔
1264
                        b = strdup(filename);
1✔
1265
                else if (r < 0)
247✔
1266
                        return r;
9✔
1267
                else
1268
                        b = path_join(dn, filename);
238✔
1269
        }
1270
        if (!b)
256✔
1271
                return -ENOMEM;
1272

1273
        *ret = TAKE_PTR(b);
256✔
1274
        return 0;
256✔
1275
}
1276

1277
bool hidden_or_backup_file(const char *filename) {
6,655,629✔
1278
        assert(filename);
6,655,629✔
1279

1280
        if (filename[0] == '.' ||
12,398,625✔
1281
            STR_IN_SET(filename,
5,742,996✔
1282
                       "lost+found",
1283
                       "aquota.user",
1284
                       "aquota.group") ||
5,742,993✔
1285
            endswith(filename, "~"))
5,742,993✔
1286
                return true;
912,638✔
1287

1288
        const char *dot = strrchr(filename, '.');
5,742,991✔
1289
        if (!dot)
5,742,991✔
1290
                return false;
1291

1292
        /* Please, let's not add more entries to the list below. If external projects think it's a good idea
1293
         * to come up with always new suffixes and that everybody else should just adjust to that, then it
1294
         * really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask
1295
         * those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups,
1296
         * possibly augmented with an additional string. Specifically: there's now:
1297
         *
1298
         *    The generic suffixes "~" and ".bak" for backup files
1299
         *    The generic prefix "." for hidden files
1300
         *
1301
         * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old",
1302
         * ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new",
1303
         * ".foopkg.old" or ".foopkg~" instead.
1304
         */
1305

1306
        return STR_IN_SET(dot + 1,
5,434,709✔
1307
                          "rpmnew",
1308
                          "rpmsave",
1309
                          "rpmorig",
1310
                          "dpkg-old",
1311
                          "dpkg-new",
1312
                          "dpkg-tmp",
1313
                          "dpkg-dist",
1314
                          "dpkg-bak",
1315
                          "dpkg-backup",
1316
                          "dpkg-remove",
1317
                          "ucf-new",
1318
                          "ucf-old",
1319
                          "ucf-dist",
1320
                          "swp",
1321
                          "bak",
1322
                          "old",
1323
                          "new");
1324
}
1325

1326
bool is_device_path(const char *path) {
9,432✔
1327

1328
        /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
1329
         * /dev. This accepts any path that starts with /dev/ or /sys/ and has something after that prefix.
1330
         * It does not actually resolve the path.
1331
         *
1332
         * Examples:
1333
         * /dev/sda, /dev/sda/foo, /sys/class, /dev/.., /sys/.., /./dev/foo → yes.
1334
         * /../dev/sda, /dev, /sys, /usr/path, /usr/../dev/sda → no.
1335
         */
1336

1337
        const char *p = PATH_STARTSWITH_SET(ASSERT_PTR(path), "/dev/", "/sys/");
9,432✔
1338
        return !isempty(p);
9,432✔
1339
}
1340

1341
bool valid_device_node_path(const char *path) {
460✔
1342

1343
        /* Some superficial checks whether the specified path is a valid device node path, all without
1344
         * looking at the actual device node. */
1345

1346
        if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
460✔
UNCOV
1347
                return false;
×
1348

1349
        if (endswith(path, "/")) /* can't be a device node if it ends in a slash */
460✔
1350
                return false;
1351

1352
        return path_is_normalized(path);
460✔
1353
}
1354

1355
bool valid_device_allow_pattern(const char *path) {
12✔
1356
        assert(path);
12✔
1357

1358
        /* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by
1359
         * DeviceAllow= and DeviceDeny=. */
1360

1361
        if (STARTSWITH_SET(path, "block-", "char-"))
12✔
1362
                return true;
4✔
1363

1364
        return valid_device_node_path(path);
8✔
1365
}
1366

1367
bool dot_or_dot_dot(const char *path) {
6,539,792✔
1368
        if (!path)
6,539,792✔
1369
                return false;
1370
        if (path[0] != '.')
6,539,791✔
1371
                return false;
1372
        if (path[1] == 0)
258,242✔
1373
                return true;
1374
        if (path[1] != '.')
129,627✔
1375
                return false;
1376

1377
        return path[2] == 0;
127,496✔
1378
}
1379

1380
bool path_implies_directory(const char *path) {
28,587✔
1381

1382
        /* Sometimes, if we look at a path we already know it must refer to a directory, because it is
1383
         * suffixed with a slash, or its last component is "." or ".." */
1384

1385
        if (!path)
28,587✔
1386
                return false;
28,587✔
1387

1388
        if (dot_or_dot_dot(path))
22,598✔
1389
                return true;
1390

1391
        return ENDSWITH_SET(path, "/", "/.", "/..");
22,596✔
1392
}
1393

1394
bool empty_or_root(const char *path) {
11,137,977✔
1395

1396
        /* For operations relative to some root directory, returns true if the specified root directory is
1397
         * redundant, i.e. either / or NULL or the empty string or any equivalent. */
1398

1399
        if (isempty(path))
11,137,977✔
1400
                return true;
1401

1402
        return path_equal(path, "/");
3,649,828✔
1403
}
1404

1405
const char* empty_to_root(const char *path) {
40,696✔
1406
        return isempty(path) ? "/" : path;
43,778✔
1407
}
1408

1409
bool path_strv_contains(char * const *l, const char *path) {
87,961✔
1410
        assert(path);
87,961✔
1411

1412
        STRV_FOREACH(i, l)
1,292,775✔
1413
                if (path_equal(*i, path))
1,220,061✔
1414
                        return true;
1415

1416
        return false;
1417
}
1418

1419
bool prefixed_path_strv_contains(char * const *l, const char *path) {
70✔
1420
        assert(path);
70✔
1421

1422
        STRV_FOREACH(i, l) {
71✔
1423
                const char *j = *i;
1✔
1424

1425
                if (*j == '-')
1✔
UNCOV
1426
                        j++;
×
1427
                if (*j == '+')
1✔
UNCOV
1428
                        j++;
×
1429

1430
                if (path_equal(j, path))
1✔
1431
                        return true;
1432
        }
1433

1434
        return false;
1435
}
1436

1437
int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
11,003✔
1438
        assert(pattern);
11,003✔
1439
        assert(prefix);
11,003✔
1440

1441
        for (const char *a = pattern, *b = prefix;;) {
11,003✔
1442
                _cleanup_free_ char *g = NULL, *h = NULL;
24,771✔
1443
                const char *p, *q;
33,011✔
1444
                int r, s;
33,011✔
1445

1446
                r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p);
33,011✔
1447
                if (r < 0)
33,011✔
1448
                        return r;
1449

1450
                s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q);
33,011✔
1451
                if (s < 0)
33,011✔
1452
                        return s;
1453

1454
                if (s == 0) {
33,011✔
1455
                        /* The pattern matches the prefix. */
1456
                        if (ret) {
2,763✔
1457
                                char *t;
2,763✔
1458

1459
                                t = path_join(prefix, p);
2,763✔
1460
                                if (!t)
2,763✔
1461
                                        return -ENOMEM;
1462

1463
                                *ret = t;
2,763✔
1464
                        }
1465
                        return true;
2,763✔
1466
                }
1467

1468
                if (r == 0)
30,248✔
1469
                        break;
1470

1471
                if (r == s && strneq(p, q, r))
30,244✔
1472
                        continue; /* common component. Check next. */
19,246✔
1473

1474
                g = strndup(p, r);
10,998✔
1475
                if (!g)
10,998✔
1476
                        return -ENOMEM;
1477

1478
                if (!string_is_glob(g))
10,998✔
1479
                        break;
1480

1481
                /* We found a glob component. Check if the glob pattern matches the prefix component. */
1482

1483
                h = strndup(q, s);
2,762✔
1484
                if (!h)
2,762✔
1485
                        return -ENOMEM;
1486

1487
                r = fnmatch(g, h, 0);
2,762✔
1488
                if (r == FNM_NOMATCH)
2,762✔
1489
                        break;
1490
                if (r != 0) /* Failure to process pattern? */
2,762✔
1491
                        return -EINVAL;
1492
        }
1493

1494
        /* The pattern does not match the prefix. */
1495
        if (ret)
8,240✔
1496
                *ret = NULL;
8,240✔
1497
        return false;
1498
}
1499

1500
const char* default_PATH(void) {
3,393✔
1501
#if HAVE_SPLIT_BIN
1502
        static int split = -1;
1503
        int r;
1504

1505
        /* Check whether /usr/sbin is not a symlink and return the appropriate $PATH.
1506
         * On error fall back to the safe value with both directories as configured… */
1507

1508
        if (split < 0)
1509
                STRV_FOREACH_PAIR(bin, sbin, STRV_MAKE("/usr/bin", "/usr/sbin",
1510
                                                       "/usr/local/bin", "/usr/local/sbin")) {
1511
                        r = inode_same(*bin, *sbin, AT_NO_AUTOMOUNT);
1512
                        if (r > 0 || r == -ENOENT)
1513
                                continue;
1514
                        if (r < 0)
1515
                                log_debug_errno(r, "Failed to compare \"%s\" and \"%s\", using compat $PATH: %m",
1516
                                                *bin, *sbin);
1517
                        split = true;
1518
                        break;
1519
                }
1520
        if (split < 0)
1521
                split = false;
1522
        if (split)
1523
                return DEFAULT_PATH_WITH_SBIN;
1524
#endif
1525
        return DEFAULT_PATH_WITHOUT_SBIN;
3,393✔
1526
}
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