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

systemd / systemd / 14345988482

08 Apr 2025 09:45PM UTC coverage: 71.913% (+0.006%) from 71.907%
14345988482

push

github

yuwata
test: Improve coverage in test-memfd-util and use ASSERT_OK() macro and friends

14 of 16 new or added lines in 1 file covered. (87.5%)

4696 existing lines in 76 files now uncovered.

296867 of 412812 relevant lines covered (71.91%)

666289.39 hits per line

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

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

3
#include <errno.h>
4
#include <fnmatch.h>
5
#include <limits.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <unistd.h>
9

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

24
int path_split_and_make_absolute(const char *p, char ***ret) {
1,057✔
25
        _cleanup_strv_free_ char **l = NULL;
1,057✔
26
        int r;
1,057✔
27

28
        assert(p);
1,057✔
29
        assert(ret);
1,057✔
30

31
        l = strv_split(p, ":");
1,057✔
32
        if (!l)
1,057✔
33
                return -ENOMEM;
34

35
        r = path_strv_make_absolute_cwd(l);
1,057✔
36
        if (r < 0)
1,057✔
37
                return r;
38

39
        *ret = TAKE_PTR(l);
1,057✔
40
        return r;
1,057✔
41
}
42

43
char* path_make_absolute(const char *p, const char *prefix) {
2,242✔
44
        assert(p);
2,242✔
45

46
        /* Makes every item in the list an absolute path by prepending
47
         * the prefix, if specified and necessary */
48

49
        if (path_is_absolute(p) || isempty(prefix))
2,242✔
50
                return strdup(p);
306✔
51

52
        return path_join(prefix, p);
1,936✔
53
}
54

55
int safe_getcwd(char **ret) {
5,465✔
56
        _cleanup_free_ char *cwd = NULL;
5,465✔
57

58
        cwd = get_current_dir_name();
5,465✔
59
        if (!cwd)
5,465✔
60
                return negative_errno();
×
61

62
        /* Let's make sure the directory is really absolute, to protect us from the logic behind
63
         * CVE-2018-1000001 */
64
        if (cwd[0] != '/')
5,465✔
65
                return -ENOMEDIUM;
66

67
        if (ret)
5,465✔
68
                *ret = TAKE_PTR(cwd);
5,465✔
69

70
        return 0;
71
}
72

73
int path_make_absolute_cwd(const char *p, char **ret) {
3,050,830✔
74
        char *c;
3,050,830✔
75
        int r;
3,050,830✔
76

77
        assert(p);
3,050,830✔
78
        assert(ret);
3,050,830✔
79

80
        /* Similar to path_make_absolute(), but prefixes with the
81
         * current working directory. */
82

83
        if (path_is_absolute(p))
3,050,830✔
84
                c = strdup(p);
3,050,553✔
85
        else {
86
                _cleanup_free_ char *cwd = NULL;
277✔
87

88
                r = safe_getcwd(&cwd);
277✔
89
                if (r < 0)
277✔
90
                        return r;
×
91

92
                c = path_join(cwd, p);
277✔
93
        }
94
        if (!c)
3,050,830✔
95
                return -ENOMEM;
96

97
        *ret = c;
3,050,830✔
98
        return 0;
3,050,830✔
99
}
100

101
int path_make_relative(const char *from, const char *to, char **ret) {
801,508✔
102
        _cleanup_free_ char *result = NULL;
801,508✔
103
        unsigned n_parents;
801,508✔
104
        const char *f, *t;
801,508✔
105
        int r, k;
801,508✔
106
        char *p;
801,508✔
107

108
        assert(from);
801,508✔
109
        assert(to);
801,508✔
110
        assert(ret);
801,508✔
111

112
        /* Strips the common part, and adds ".." elements as necessary. */
113

114
        if (!path_is_absolute(from) || !path_is_absolute(to))
1,603,012✔
115
                return -EINVAL;
116

117
        for (;;) {
5,796,054✔
118
                r = path_find_first_component(&from, true, &f);
5,796,054✔
119
                if (r < 0)
5,796,054✔
120
                        return r;
121

122
                k = path_find_first_component(&to, true, &t);
5,796,054✔
123
                if (k < 0)
5,796,054✔
124
                        return k;
125

126
                if (r == 0) {
5,796,054✔
127
                        /* end of 'from' */
128
                        if (k == 0) {
666,761✔
129
                                /* from and to are equivalent. */
130
                                result = strdup(".");
4✔
131
                                if (!result)
4✔
132
                                        return -ENOMEM;
133
                        } else {
134
                                /* 'to' is inside of 'from'. */
135
                                r = path_simplify_alloc(t, &result);
666,757✔
136
                                if (r < 0)
666,757✔
137
                                        return r;
138

139
                                if (!path_is_valid(result))
666,757✔
140
                                        return -EINVAL;
141
                        }
142

143
                        *ret = TAKE_PTR(result);
666,761✔
144
                        return 0;
666,761✔
145
                }
146

147
                if (r != k || !strneq(f, t, r))
5,129,293✔
148
                        break;
149
        }
150

151
        /* If we're here, then "from_dir" has one or more elements that need to
152
         * be replaced with "..". */
153

154
        for (n_parents = 1;; n_parents++) {
158,570✔
155
                /* If this includes ".." we can't do a simple series of "..". */
156
                r = path_find_first_component(&from, false, &f);
293,313✔
157
                if (r < 0)
293,313✔
158
                        return r;
159
                if (r == 0)
293,311✔
160
                        break;
161
        }
162

163
        if (isempty(t) && n_parents * 3 > PATH_MAX)
134,741✔
164
                /* PATH_MAX is counted *with* the trailing NUL byte */
165
                return -EINVAL;
166

167
        result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t));
404,219✔
168
        if (!result)
134,741✔
169
                return -ENOMEM;
170

171
        for (p = result; n_parents > 0; n_parents--)
428,052✔
172
                p = mempcpy(p, "../", 3);
293,311✔
173

174
        if (isempty(t)) {
134,741✔
175
                /* Remove trailing slash and terminate string. */
176
                *(--p) = '\0';
2✔
177
                *ret = TAKE_PTR(result);
2✔
178
                return 0;
2✔
179
        }
180

181
        strcpy(p, t);
134,739✔
182

183
        path_simplify(result);
134,739✔
184

185
        if (!path_is_valid(result))
134,739✔
186
                return -EINVAL;
187

188
        *ret = TAKE_PTR(result);
134,739✔
189
        return 0;
134,739✔
190
}
191

192
int path_make_relative_parent(const char *from_child, const char *to, char **ret) {
134,830✔
193
        _cleanup_free_ char *from = NULL;
134,830✔
194
        int r;
134,830✔
195

196
        assert(from_child);
134,830✔
197
        assert(to);
134,830✔
198
        assert(ret);
134,830✔
199

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

213
        r = path_extract_directory(from_child, &from);
134,830✔
214
        if (r < 0)
134,830✔
215
                return r;
216

217
        return path_make_relative(from, to, ret);
134,829✔
218
}
219

220
char* path_startswith_strv(const char *p, char * const *strv) {
134,144✔
221
        assert(p);
134,144✔
222

223
        STRV_FOREACH(s, strv) {
508,218✔
224
                char *t;
399,012✔
225

226
                t = path_startswith(p, *s);
399,012✔
227
                if (t)
399,012✔
228
                        return t;
229
        }
230

231
        return NULL;
232
}
233

234
int path_strv_make_absolute_cwd(char **l) {
1,359✔
235
        int r;
1,359✔
236

237
        /* Goes through every item in the string list and makes it
238
         * absolute. This works in place and won't rollback any
239
         * changes on failure. */
240

241
        STRV_FOREACH(s, l) {
7,300✔
242
                char *t;
5,941✔
243

244
                r = path_make_absolute_cwd(*s, &t);
5,941✔
245
                if (r < 0)
5,941✔
246
                        return r;
×
247

248
                path_simplify(t);
5,941✔
249
                free_and_replace(*s, t);
5,941✔
250
        }
251

252
        return 0;
253
}
254

255
char** path_strv_resolve(char **l, const char *root) {
15,715✔
256
        unsigned k = 0;
15,715✔
257
        bool enomem = false;
15,715✔
258
        int r;
15,715✔
259

260
        if (strv_isempty(l))
31,430✔
261
                return l;
262

263
        /* Goes through every item in the string list and canonicalize
264
         * the path. This works in place and won't rollback any
265
         * changes on failure. */
266

267
        STRV_FOREACH(s, l) {
93,671✔
268
                _cleanup_free_ char *orig = NULL;
77,956✔
269
                char *t, *u;
77,956✔
270

271
                if (!path_is_absolute(*s)) {
77,956✔
272
                        free(*s);
×
273
                        continue;
×
274
                }
275

276
                if (root) {
77,956✔
277
                        orig = *s;
7✔
278
                        t = path_join(root, orig);
7✔
279
                        if (!t) {
7✔
280
                                enomem = true;
×
281
                                continue;
×
282
                        }
283
                } else
284
                        t = *s;
285

286
                r = chase(t, root, 0, &u, NULL);
77,956✔
287
                if (r == -ENOENT) {
77,956✔
288
                        if (root) {
67,083✔
289
                                u = TAKE_PTR(orig);
2✔
290
                                free(t);
2✔
291
                        } else
292
                                u = t;
67,081✔
293
                } else if (r < 0) {
10,873✔
294
                        free(t);
×
295

296
                        if (r == -ENOMEM)
×
297
                                enomem = true;
×
298

299
                        continue;
×
300
                } else if (root) {
10,873✔
301
                        char *x;
5✔
302

303
                        free(t);
5✔
304
                        x = path_startswith(u, root);
5✔
305
                        if (x) {
5✔
306
                                /* restore the slash if it was lost */
307
                                if (!startswith(x, "/"))
5✔
308
                                        *(--x) = '/';
5✔
309

310
                                t = strdup(x);
5✔
311
                                free(u);
5✔
312
                                if (!t) {
5✔
313
                                        enomem = true;
×
314
                                        continue;
×
315
                                }
316
                                u = t;
5✔
317
                        } else {
318
                                /* canonicalized path goes outside of
319
                                 * prefix, keep the original path instead */
320
                                free_and_replace(u, orig);
×
321
                        }
322
                } else
323
                        free(t);
10,868✔
324

325
                l[k++] = u;
77,956✔
326
        }
327

328
        l[k] = NULL;
15,715✔
329

330
        if (enomem)
15,715✔
331
                return NULL;
×
332

333
        return l;
334
}
335

336
char** path_strv_resolve_uniq(char **l, const char *root) {
15,714✔
337

338
        if (strv_isempty(l))
15,714✔
339
                return l;
340

341
        if (!path_strv_resolve(l, root))
15,714✔
342
                return NULL;
343

344
        return strv_uniq(l);
15,714✔
345
}
346

347
char* path_simplify_full(char *path, PathSimplifyFlags flags) {
7,813,185✔
348
        bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
7,813,185✔
349
        char *f = path;
7,813,185✔
350
        int r;
7,813,185✔
351

352
        /* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
353
         * Modifies the passed string in-place.
354
         *
355
         * ///foo//./bar/.   becomes /foo/bar
356
         * .//./foo//./bar/. becomes foo/bar
357
         * /../foo/bar       becomes /foo/bar
358
         * /../foo/bar/..    becomes /foo/bar/..
359
         */
360

361
        if (isempty(path))
7,813,185✔
362
                return path;
363

364
        keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
7,791,375✔
365

366
        absolute = path_is_absolute(path);
7,791,375✔
367
        f += absolute;  /* Keep leading /, if present. */
7,791,375✔
368

369
        for (const char *p = f;;) {
7,791,375✔
370
                const char *e;
31,380,042✔
371

372
                r = path_find_first_component(&p, true, &e);
31,380,042✔
373
                if (r == 0)
31,380,042✔
374
                        break;
375

376
                if (r > 0 && absolute && beginning && path_startswith(e, ".."))
23,588,671✔
377
                        /* If we're at the beginning of an absolute path, we can safely skip ".." */
378
                        continue;
79✔
379

380
                beginning = false;
23,588,592✔
381

382
                if (add_slash)
23,588,592✔
383
                        *f++ = '/';
15,803,413✔
384

385
                if (r < 0) {
23,588,592✔
386
                        /* if path is invalid, then refuse to simplify the remaining part. */
387
                        memmove(f, p, strlen(p) + 1);
4✔
388
                        return path;
4✔
389
                }
390

391
                memmove(f, e, r);
23,588,588✔
392
                f += r;
23,588,588✔
393

394
                add_slash = true;
23,588,588✔
395
        }
396

397
        /* Special rule, if we stripped everything, we need a "." for the current directory. */
398
        if (f == path)
7,791,371✔
399
                *f++ = '.';
7✔
400

401
        if (*(f-1) != '/' && keep_trailing_slash)
7,791,371✔
402
                *f++ = '/';
12✔
403

404
        *f = '\0';
7,791,371✔
405
        return path;
7,791,371✔
406
}
407

408
char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
48,864,920✔
409
        assert(path);
48,864,920✔
410
        assert(prefix);
48,864,920✔
411

412
        /* Returns a pointer to the start of the first component after the parts matched by
413
         * the prefix, iff
414
         * - both paths are absolute or both paths are relative,
415
         * and
416
         * - each component in prefix in turn matches a component in path at the same position.
417
         * An empty string will be returned when the prefix and path are equivalent.
418
         *
419
         * Returns NULL otherwise.
420
         */
421

422
        if ((path[0] == '/') != (prefix[0] == '/'))
48,864,920✔
423
                return NULL;
424

425
        for (;;) {
43,048,677✔
426
                const char *p, *q;
91,853,841✔
427
                int r, k;
91,853,841✔
428

429
                r = path_find_first_component(&path, accept_dot_dot, &p);
91,853,841✔
430
                if (r < 0)
91,853,841✔
431
                        return NULL;
48,805,164✔
432

433
                k = path_find_first_component(&prefix, accept_dot_dot, &q);
91,853,841✔
434
                if (k < 0)
91,853,841✔
435
                        return NULL;
436

437
                if (k == 0)
91,853,841✔
438
                        return (char*) (p ?: path);
35,647,634✔
439

440
                if (r != k)
56,206,207✔
441
                        return NULL;
442

443
                if (!strneq(p, q, r))
45,739,943✔
444
                        return NULL;
445
        }
446
}
447

448
int path_compare(const char *a, const char *b) {
16,660,186✔
449
        int r;
16,660,186✔
450

451
        /* Order NULL before non-NULL */
452
        r = CMP(!!a, !!b);
16,660,186✔
453
        if (r != 0)
16,647,566✔
454
                return r;
12,623✔
455

456
        /* A relative path and an absolute path must not compare as equal.
457
         * Which one is sorted before the other does not really matter.
458
         * Here a relative path is ordered before an absolute path. */
459
        r = CMP(path_is_absolute(a), path_is_absolute(b));
33,295,124✔
460
        if (r != 0)
16,419,223✔
461
                return r;
258,959✔
462

463
        for (;;) {
13,987,246✔
464
                const char *aa, *bb;
30,375,850✔
465
                int j, k;
30,375,850✔
466

467
                j = path_find_first_component(&a, true, &aa);
30,375,850✔
468
                k = path_find_first_component(&b, true, &bb);
30,375,850✔
469

470
                if (j < 0 || k < 0) {
30,375,850✔
471
                        /* When one of paths is invalid, order invalid path after valid one. */
472
                        r = CMP(j < 0, k < 0);
3✔
473
                        if (r != 0)
×
474
                                return r;
16,388,604✔
475

476
                        /* fallback to use strcmp() if both paths are invalid. */
477
                        return strcmp(a, b);
×
478
                }
479

480
                /* Order prefixes first: "/foo" before "/foo/bar" */
481
                if (j == 0) {
30,375,847✔
482
                        if (k == 0)
2,489,449✔
483
                                return 0;
484
                        return -1;
94,377✔
485
                }
486
                if (k == 0)
27,886,398✔
487
                        return 1;
488

489
                /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
490
                r = memcmp(aa, bb, MIN(j, k));
25,619,610✔
491
                if (r != 0)
25,619,610✔
492
                        return r;
493

494
                /* Sort "/foo/a" before "/foo/aaa" */
495
                r = CMP(j, k);
14,065,374✔
496
                if (r != 0)
14,014,984✔
497
                        return r;
78,128✔
498
        }
499
}
500

501
int path_compare_filename(const char *a, const char *b) {
433,410✔
502
        _cleanup_free_ char *fa = NULL, *fb = NULL;
433,410✔
503
        int r, j, k;
433,410✔
504

505
        /* Order NULL before non-NULL */
506
        r = CMP(!!a, !!b);
433,410✔
507
        if (r != 0)
433,410✔
508
                return r;
×
509

510
        j = path_extract_filename(a, &fa);
433,410✔
511
        k = path_extract_filename(b, &fb);
433,410✔
512

513
        /* When one of paths is "." or root, then order it earlier. */
514
        r = CMP(j != -EADDRNOTAVAIL, k != -EADDRNOTAVAIL);
433,410✔
515
        if (r != 0)
433,406✔
516
                return r;
8✔
517

518
        /* When one of paths is invalid (or we get OOM), order invalid path after valid one. */
519
        r = CMP(j < 0, k < 0);
433,402✔
520
        if (r != 0)
433,402✔
521
                return r;
×
522

523
        /* fallback to use strcmp() if both paths are invalid. */
524
        if (j < 0)
433,402✔
525
                return strcmp(a, b);
30✔
526

527
        return strcmp(fa, fb);
433,372✔
528
}
529

530
int path_equal_or_inode_same_full(const char *a, const char *b, int flags) {
51,633✔
531
        /* Returns true if paths are of the same entry, false if not, <0 on error. */
532

533
        if (path_equal(a, b))
51,633✔
534
                return 1;
535

536
        if (!a || !b)
483✔
537
                return 0;
538

539
        return inode_same(a, b, flags);
483✔
540
}
541

542
char* path_extend_internal(char **x, ...) {
22,171,079✔
543
        size_t sz, old_sz;
22,171,079✔
544
        char *q, *nx;
22,171,079✔
545
        const char *p;
22,171,079✔
546
        va_list ap;
22,171,079✔
547
        bool slash;
22,171,079✔
548

549
        /* Joins all listed strings until the sentinel and places a "/" between them unless the strings
550
         * end/begin already with one so that it is unnecessary. Note that slashes which are already
551
         * duplicate won't be removed. The string returned is hence always equal to or longer than the sum of
552
         * the lengths of the individual strings.
553
         *
554
         * The first argument may be an already allocated string that is extended via realloc() if
555
         * non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the
556
         * first parameter to distinguish the two operations.
557
         *
558
         * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of
559
         * which some are optional.
560
         *
561
         * Examples:
562
         *
563
         * path_join("foo", "bar") → "foo/bar"
564
         * path_join("foo/", "bar") → "foo/bar"
565
         * path_join("", "foo", "", "bar", "") → "foo/bar" */
566

567
        sz = old_sz = x ? strlen_ptr(*x) : 0;
22,171,079✔
568
        va_start(ap, x);
22,171,079✔
569
        while ((p = va_arg(ap, char*)) != POINTER_MAX) {
50,016,420✔
570
                size_t add;
27,845,341✔
571

572
                if (isempty(p))
27,845,341✔
573
                        continue;
530,694✔
574

575
                add = 1 + strlen(p);
27,314,647✔
576
                if (sz > SIZE_MAX - add) { /* overflow check */
27,314,647✔
577
                        va_end(ap);
×
578
                        return NULL;
×
579
                }
580

581
                sz += add;
27,314,647✔
582
        }
583
        va_end(ap);
22,171,079✔
584

585
        nx = realloc(x ? *x : NULL, GREEDY_ALLOC_ROUND_UP(sz+1));
22,171,079✔
586
        if (!nx)
22,171,079✔
587
                return NULL;
588
        if (x)
22,171,079✔
589
                *x = nx;
16,794,666✔
590

591
        if (old_sz > 0)
22,171,079✔
592
                slash = nx[old_sz-1] == '/';
16,723,007✔
593
        else {
594
                nx[old_sz] = 0;
5,448,072✔
595
                slash = true; /* no need to generate a slash anymore */
5,448,072✔
596
        }
597

598
        q = nx + old_sz;
22,171,079✔
599

600
        va_start(ap, x);
22,171,079✔
601
        while ((p = va_arg(ap, char*)) != POINTER_MAX) {
50,016,420✔
602
                if (isempty(p))
27,845,341✔
603
                        continue;
530,694✔
604

605
                if (!slash && p[0] != '/')
27,314,647✔
606
                        *(q++) = '/';
16,978,886✔
607

608
                q = stpcpy(q, p);
27,314,647✔
609
                slash = endswith(p, "/");
27,314,647✔
610
        }
611
        va_end(ap);
22,171,079✔
612

613
        return nx;
22,171,079✔
614
}
615

616
int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd) {
27,054✔
617
        _cleanup_close_ int fd = -EBADF;
27,054✔
618
        _cleanup_free_ char *resolved = NULL;
27,054✔
619
        int r;
27,054✔
620

621
        assert(name);
27,054✔
622

623
        /* Function chase() is invoked only when root is not NULL, as using it regardless of
624
         * root value would alter the behavior of existing callers for example: /bin/sleep would become
625
         * /usr/bin/sleep when find_executables is called. Hence, this function should be invoked when
626
         * needed to avoid unforeseen regression or other complicated changes. */
627
        if (root) {
27,054✔
628
                /* prefix root to name in case full paths are not specified */
629
                r = chase(name, root, CHASE_PREFIX_ROOT, &resolved, &fd);
4✔
630
                if (r < 0)
4✔
631
                        return r;
632

633
                name = resolved;
4✔
634
        } else {
635
                /* We need to use O_PATH because there may be executables for which we have only exec permissions,
636
                 * but not read (usually suid executables). */
637
                fd = open(name, O_PATH|O_CLOEXEC);
27,050✔
638
                if (fd < 0)
27,050✔
639
                        return -errno;
4,221✔
640
        }
641

642
        r = fd_verify_regular(fd);
22,833✔
643
        if (r < 0)
22,833✔
644
                return r;
645

646
        r = access_fd(fd, X_OK);
22,830✔
647
        if (r == -ENOSYS)
22,830✔
648
                /* /proc/ is not mounted. Fall back to access(). */
UNCOV
649
                r = RET_NERRNO(access(name, X_OK));
×
650
        if (r < 0)
22,830✔
651
                return r;
652

653
        if (ret_path) {
22,829✔
654
                if (resolved)
22,475✔
UNCOV
655
                        *ret_path = TAKE_PTR(resolved);
×
656
                else {
657
                        r = path_make_absolute_cwd(name, ret_path);
22,475✔
658
                        if (r < 0)
22,475✔
659
                                return r;
660

661
                        path_simplify(*ret_path);
22,475✔
662
                }
663
        }
664

665
        if (ret_fd)
22,829✔
666
                *ret_fd = TAKE_FD(fd);
21,206✔
667

668
        return 0;
669
}
670

671
int find_executable_full(
22,839✔
672
                const char *name,
673
                const char *root,
674
                char * const *exec_search_path,
675
                bool use_path_envvar,
676
                char **ret_filename,
677
                int *ret_fd) {
678

679
        int last_error = -ENOENT, r = 0;
22,839✔
680

681
        assert(name);
22,839✔
682

683
        if (is_path(name))
22,839✔
684
                return open_and_check_executable(name, root, ret_filename, ret_fd);
22,839✔
685

686
        if (exec_search_path) {
3,289✔
687
                STRV_FOREACH(element, exec_search_path) {
6✔
688
                        _cleanup_free_ char *full_path = NULL;
5✔
689

690
                        if (!path_is_absolute(*element)) {
5✔
UNCOV
691
                                log_debug("Exec search path '%s' isn't absolute, ignoring.", *element);
×
UNCOV
692
                                continue;
×
693
                        }
694

695
                        full_path = path_join(*element, name);
5✔
696
                        if (!full_path)
5✔
697
                                return -ENOMEM;
698

699
                        r = open_and_check_executable(full_path, root, ret_filename, ret_fd);
5✔
700
                        if (r >= 0)
5✔
701
                                return 0;
702
                        if (r != -EACCES)
4✔
703
                                last_error = r;
4✔
704
                }
705
                return last_error;
706
        }
707

708
        const char *p = NULL;
3,287✔
709

710
        if (use_path_envvar)
3,287✔
711
                /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
712
                 * binary. */
713
                p = getenv("PATH");
935✔
714
        if (!p)
3,287✔
715
                p = default_PATH();
2,362✔
716

717
        /* Resolve a single-component name to a full path */
718
        for (;;) {
7,503✔
719
                _cleanup_free_ char *element = NULL;
7,499✔
720

721
                r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
7,503✔
722
                if (r < 0)
7,503✔
723
                        return r;
724
                if (r == 0)
7,503✔
725
                        break;
726

727
                if (!path_is_absolute(element)) {
7,499✔
UNCOV
728
                        log_debug("Exec search path '%s' isn't absolute, ignoring.", element);
×
UNCOV
729
                        continue;
×
730
                }
731

732
                if (!path_extend(&element, name))
7,499✔
733
                        return -ENOMEM;
734

735
                r = open_and_check_executable(element, root, ret_filename, ret_fd);
7,499✔
736
                if (r >= 0) /* Found it! */
7,499✔
737
                        return 0;
738
                /* PATH entries which we don't have access to are ignored, as per tradition. */
739
                if (r != -EACCES)
4,216✔
740
                        last_error = r;
4,216✔
741
        }
742

743
        return last_error;
4✔
744
}
745

UNCOV
746
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
×
UNCOV
747
        bool changed = false, originally_unset;
×
748

UNCOV
749
        assert(timestamp);
×
750

UNCOV
751
        if (!paths)
×
752
                return false;
753

UNCOV
754
        originally_unset = *timestamp == 0;
×
755

UNCOV
756
        STRV_FOREACH(i, paths) {
×
757
                struct stat stats;
×
758
                usec_t u;
×
759

760
                if (stat(*i, &stats) < 0)
×
UNCOV
761
                        continue;
×
762

UNCOV
763
                u = timespec_load(&stats.st_mtim);
×
764

765
                /* check first */
UNCOV
766
                if (*timestamp >= u)
×
767
                        continue;
×
768

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

771
                /* update timestamp */
772
                if (update) {
×
UNCOV
773
                        *timestamp = u;
×
774
                        changed = true;
×
775
                } else
UNCOV
776
                        return true;
×
777
        }
778

779
        return changed;
780
}
781

782
static int executable_is_good(const char *executable) {
186✔
783
        _cleanup_free_ char *p = NULL, *d = NULL;
186✔
784
        int r;
186✔
785

786
        r = find_executable(executable, &p);
186✔
787
        if (r == -ENOENT)
186✔
788
                return 0;
789
        if (r < 0)
184✔
790
                return r;
791

792
        /* An fsck that is linked to /bin/true is a non-existent fsck */
793

794
        r = readlink_malloc(p, &d);
184✔
795
        if (r == -EINVAL) /* not a symlink */
184✔
796
                return 1;
797
        if (r < 0)
64✔
798
                return r;
799

800
        return !PATH_IN_SET(d, "true"
64✔
801
                               "/bin/true",
802
                               "/usr/bin/true",
803
                               "/dev/null");
804
}
805

806
int fsck_exists(void) {
100✔
807
        return executable_is_good("fsck");
100✔
808
}
809

810
int fsck_exists_for_fstype(const char *fstype) {
86✔
811
        const char *checker;
86✔
812
        int r;
86✔
813

814
        assert(fstype);
86✔
815

816
        if (streq(fstype, "auto"))
86✔
817
                return -EINVAL;
818

819
        r = fsck_exists();
86✔
820
        if (r <= 0)
86✔
821
                return r;
822

823
        checker = strjoina("fsck.", fstype);
430✔
824
        return executable_is_good(checker);
86✔
825
}
826

827
static const char* skip_slash_or_dot(const char *p) {
795,548,548✔
828
        for (; !isempty(p); p++) {
1,234,073,723✔
829
                if (*p == '/')
1,069,575,337✔
830
                        continue;
438,524,381✔
831
                if (startswith(p, "./")) {
631,050,956✔
832
                        p++;
794✔
833
                        continue;
794✔
834
                }
835
                break;
836
        }
837
        return p;
795,548,548✔
838
}
839

840
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret) {
433,169,853✔
841
        const char *q, *first, *end_first, *next;
433,169,853✔
842
        size_t len;
433,169,853✔
843

844
        assert(p);
433,169,853✔
845

846
        /* When a path is input, then returns the pointer to the first component and its length, and
847
         * move the input pointer to the next component or nul. This skips both over any '/'
848
         * immediately *before* and *after* the first component before returning.
849
         *
850
         * Examples
851
         *   Input:  p: "//.//aaa///bbbbb/cc"
852
         *   Output: p: "bbbbb///cc"
853
         *           ret: "aaa///bbbbb/cc"
854
         *           return value: 3 (== strlen("aaa"))
855
         *
856
         *   Input:  p: "aaa//"
857
         *   Output: p: (pointer to NUL)
858
         *           ret: "aaa//"
859
         *           return value: 3 (== strlen("aaa"))
860
         *
861
         *   Input:  p: "/", ".", ""
862
         *   Output: p: (pointer to NUL)
863
         *           ret: NULL
864
         *           return value: 0
865
         *
866
         *   Input:  p: NULL
867
         *   Output: p: NULL
868
         *           ret: NULL
869
         *           return value: 0
870
         *
871
         *   Input:  p: "(too long component)"
872
         *   Output: return value: -EINVAL
873
         *
874
         *   (when accept_dot_dot is false)
875
         *   Input:  p: "//..//aaa///bbbbb/cc"
876
         *   Output: return value: -EINVAL
877
         */
878

879
        q = *p;
433,169,853✔
880

881
        first = skip_slash_or_dot(q);
433,169,853✔
882
        if (isempty(first)) {
433,169,853✔
883
                *p = first;
70,782,465✔
884
                if (ret)
70,782,465✔
885
                        *ret = NULL;
70,777,594✔
886
                return 0;
70,782,465✔
887
        }
888
        if (streq(first, ".")) {
362,387,388✔
889
                *p = first + 1;
8,503✔
890
                if (ret)
8,503✔
891
                        *ret = NULL;
8,446✔
892
                return 0;
8,503✔
893
        }
894

895
        end_first = strchrnul(first, '/');
362,378,885✔
896
        len = end_first - first;
362,378,885✔
897

898
        if (len > NAME_MAX)
362,378,885✔
899
                return -EINVAL;
900
        if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.')
362,378,732✔
901
                return -EINVAL;
902

903
        next = skip_slash_or_dot(end_first);
362,378,695✔
904

905
        *p = next + streq(next, ".");
362,378,695✔
906
        if (ret)
362,378,695✔
907
                *ret = first;
327,681,622✔
908
        return len;
362,378,695✔
909
}
910

911
static const char* skip_slash_or_dot_backward(const char *path, const char *q) {
15,058,041✔
912
        assert(path);
15,058,041✔
913
        assert(!q || q >= path);
15,058,041✔
914

915
        for (; q; q = PTR_SUB1(q, path)) {
30,572,229✔
916
                if (*q == '/')
22,605,658✔
917
                        continue;
7,786,128✔
918
                if (q > path && strneq(q - 1, "/.", 2))
14,819,530✔
919
                        continue;
264✔
920
                if (q == path && *q == '.')
14,819,266✔
921
                        continue;
80✔
922
                break;
923
        }
924
        return q;
15,058,041✔
925
}
926

927
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret) {
7,536,418✔
928
        const char *q, *last_end, *last_begin;
7,536,418✔
929
        size_t len;
7,536,418✔
930

931
        /* Similar to path_find_first_component(), but search components from the end.
932
        *
933
        * Examples
934
        *   Input:  path: "//.//aaa///bbbbb/cc//././"
935
        *           next: NULL
936
        *   Output: next: "/cc//././"
937
        *           ret: "cc//././"
938
        *           return value: 2 (== strlen("cc"))
939
        *
940
        *   Input:  path: "//.//aaa///bbbbb/cc//././"
941
        *           next: "/cc//././"
942
        *   Output: next: "///bbbbb/cc//././"
943
        *           ret: "bbbbb/cc//././"
944
        *           return value: 5 (== strlen("bbbbb"))
945
        *
946
        *   Input:  path: "//.//aaa///bbbbb/cc//././"
947
        *           next: "///bbbbb/cc//././"
948
        *   Output: next: "//.//aaa///bbbbb/cc//././" (next == path)
949
        *           ret: "aaa///bbbbb/cc//././"
950
        *           return value: 3 (== strlen("aaa"))
951
        *
952
        *   Input:  path: "/", ".", "", or NULL
953
        *   Output: next: equivalent to path
954
        *           ret: NULL
955
        *           return value: 0
956
        *
957
        *   Input:  path: "(too long component)"
958
        *   Output: return value: -EINVAL
959
        *
960
        *   (when accept_dot_dot is false)
961
        *   Input:  path: "//..//aaa///bbbbb/cc/..//"
962
        *   Output: return value: -EINVAL
963
        */
964

965
        if (isempty(path)) {
7,536,418✔
966
                if (next)
9✔
967
                        *next = path;
9✔
968
                if (ret)
9✔
969
                        *ret = NULL;
9✔
970
                return 0;
9✔
971
        }
972

973
        if (next && *next) {
7,536,409✔
974
                if (*next < path || *next > path + strlen(path))
35,500✔
975
                        return -EINVAL;
976
                if (*next == path) {
35,500✔
977
                        if (ret)
9✔
978
                                *ret = NULL;
9✔
979
                        return 0;
9✔
980
                }
981
                if (!IN_SET(**next, '\0', '/'))
35,491✔
982
                        return -EINVAL;
983
                q = *next - 1;
35,491✔
984
        } else
985
                q = path + strlen(path) - 1;
7,500,909✔
986

987
        q = skip_slash_or_dot_backward(path, q);
7,536,400✔
988
        if (!q || /* the root directory */
7,536,400✔
989
            (q == path && *q == '.')) { /* path is "." or "./" */
34✔
990
                if (next)
14,689✔
991
                        *next = path;
14,689✔
992
                if (ret)
14,689✔
993
                        *ret = NULL;
14,686✔
994
                return 0;
14,689✔
995
        }
996

997
        last_end = q + 1;
7,521,711✔
998

999
        while (q && *q != '/')
123,179,340✔
1000
                q = PTR_SUB1(q, path);
108,135,918✔
1001

1002
        last_begin = q ? q + 1 : path;
7,521,711✔
1003
        len = last_end - last_begin;
7,521,711✔
1004

1005
        if (len > NAME_MAX)
7,521,711✔
1006
                return -EINVAL;
1007
        if (!accept_dot_dot && len == 2 && strneq(last_begin, "..", 2))
7,521,707✔
1008
                return -EINVAL;
1009

1010
        if (next) {
7,521,682✔
1011
                q = skip_slash_or_dot_backward(path, q);
7,521,641✔
1012
                *next = q ? q + 1 : path;
7,521,641✔
1013
        }
1014

1015
        if (ret)
7,521,682✔
1016
                *ret = last_begin;
6,682,490✔
1017
        return len;
7,521,682✔
1018
}
1019

1020
const char* last_path_component(const char *path) {
160,145✔
1021

1022
        /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
1023
         *
1024
         *    a/b/c → c
1025
         *    a/b/c/ → c/
1026
         *    x → x
1027
         *    x/ → x/
1028
         *    /y → y
1029
         *    /y/ → y/
1030
         *    / → /
1031
         *    // → /
1032
         *    /foo/a → a
1033
         *    /foo/a/ → a/
1034
         *
1035
         *    Also, the empty string is mapped to itself.
1036
         *
1037
         * This is different than basename(), which returns "" when a trailing slash is present.
1038
         *
1039
         * This always succeeds (except if you pass NULL in which case it returns NULL, too).
1040
         */
1041

1042
        unsigned l, k;
160,145✔
1043

1044
        if (!path)
160,145✔
1045
                return NULL;
1046

1047
        l = k = strlen(path);
160,144✔
1048
        if (l == 0) /* special case — an empty string */
160,144✔
1049
                return path;
1050

1051
        while (k > 0 && path[k-1] == '/')
160,156✔
1052
                k--;
1053

1054
        if (k == 0) /* the root directory */
160,143✔
1055
                return path + l - 1;
3✔
1056

1057
        while (k > 0 && path[k-1] != '/')
1,373,929✔
1058
                k--;
1059

1060
        return path + k;
160,140✔
1061
}
1062

1063
int path_extract_filename(const char *path, char **ret) {
2,592,969✔
1064
        _cleanup_free_ char *a = NULL;
5,185,938✔
1065
        const char *c, *next = NULL;
2,592,969✔
1066
        int r;
2,592,969✔
1067

1068
        /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
1069
         * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
1070
         * slashes. Returns:
1071
         *
1072
         * -EINVAL        → if the path is not valid
1073
         * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir
1074
         *                  itself or "." is specified
1075
         * -ENOMEM        → no memory
1076
         *
1077
         * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to
1078
         * indicate the referenced file must be a directory.
1079
         *
1080
         * This function guarantees to return a fully valid filename, i.e. one that passes
1081
         * filename_is_valid() – this means "." and ".." are not accepted. */
1082

1083
        if (!path_is_valid(path))
2,592,969✔
1084
                return -EINVAL;
1085

1086
        r = path_find_last_component(path, false, &next, &c);
2,592,967✔
1087
        if (r < 0)
2,592,967✔
1088
                return r;
1089
        if (r == 0) /* root directory */
2,592,953✔
1090
                return -EADDRNOTAVAIL;
1091

1092
        a = strndup(c, r);
2,592,871✔
1093
        if (!a)
2,592,871✔
1094
                return -ENOMEM;
1095

1096
        *ret = TAKE_PTR(a);
2,592,871✔
1097
        return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
2,592,871✔
1098
}
1099

1100
int path_extract_directory(const char *path, char **ret) {
4,068,683✔
1101
        const char *c, *next = NULL;
4,068,683✔
1102
        int r;
4,068,683✔
1103

1104
        /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
1105
         *
1106
         * -EINVAL        → if the path is not valid
1107
         * -EDESTADDRREQ  → if no directory was specified in the passed in path, i.e. only a filename was passed
1108
         * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e.
1109
         *                   the root dir itself or "." was specified
1110
         * -ENOMEM        → no memory (surprise!)
1111
         *
1112
         * This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
1113
         */
1114

1115
        r = path_find_last_component(path, false, &next, &c);
4,068,683✔
1116
        if (r < 0)
4,068,683✔
1117
                return r;
4,068,683✔
1118
        if (r == 0) /* empty or root */
4,068,677✔
1119
                return isempty(path) ? -EINVAL : -EADDRNOTAVAIL;
29,197✔
1120
        if (next == path) {
4,054,078✔
1121
                if (*path != '/') /* filename only */
26,333✔
1122
                        return -EDESTADDRREQ;
1123

1124
                return strdup_to(ret, "/");
19,330✔
1125
        }
1126

1127
        _cleanup_free_ char *a = strndup(path, next - path);
4,027,745✔
1128
        if (!a)
4,027,745✔
1129
                return -ENOMEM;
1130

1131
        path_simplify(a);
4,027,745✔
1132

1133
        if (!path_is_valid(a))
4,027,745✔
1134
                return -EINVAL;
1135

1136
        if (ret)
4,027,745✔
1137
                *ret = TAKE_PTR(a);
4,027,745✔
1138

1139
        return 0;
1140
}
1141

1142
bool filename_part_is_valid(const char *p) {
922,275✔
1143
        const char *e;
922,275✔
1144

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

1148
        if (!p)
922,275✔
1149
                return false;
1150

1151
        e = strchrnul(p, '/');
922,275✔
1152
        if (*e != 0)
922,275✔
1153
                return false;
1154

1155
        if (e - p > NAME_MAX) /* NAME_MAX is counted *without* the trailing NUL byte */
910,588✔
1156
                return false;
3✔
1157

1158
        return true;
1159
}
1160

1161
bool filename_is_valid(const char *p) {
918,207✔
1162

1163
        if (isempty(p))
918,207✔
1164
                return false;
1165

1166
        if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
918,196✔
1167
                return false;
1168

1169
        return filename_part_is_valid(p);
918,183✔
1170
}
1171

1172
bool path_is_valid_full(const char *p, bool accept_dot_dot) {
10,830,394✔
1173
        if (isempty(p))
10,830,394✔
1174
                return false;
1175

1176
        for (const char *e = p;;) {
10,830,387✔
1177
                int r;
34,702,037✔
1178

1179
                r = path_find_first_component(&e, accept_dot_dot, NULL);
34,702,037✔
1180
                if (r < 0)
34,702,037✔
1181
                        return false;
10,830,387✔
1182

1183
                if (e - p >= PATH_MAX) /* Already reached the maximum length for a path? (PATH_MAX is counted
34,702,001✔
1184
                                        * *with* the trailing NUL byte) */
1185
                        return false;
1186
                if (*e == 0)           /* End of string? Yay! */
34,701,998✔
1187
                        return true;
1188
        }
1189
}
1190

1191
bool path_is_normalized(const char *p) {
588,859✔
1192
        if (!path_is_safe(p))
588,859✔
1193
                return false;
1194

1195
        if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
588,834✔
1196
                return false;
1197

1198
        if (strstr(p, "//"))
588,831✔
1199
                return false;
1✔
1200

1201
        return true;
1202
}
1203

1204
int file_in_same_dir(const char *path, const char *filename, char **ret) {
265✔
1205
        _cleanup_free_ char *b = NULL;
265✔
1206
        int r;
265✔
1207

1208
        assert(path);
265✔
1209
        assert(filename);
265✔
1210
        assert(ret);
265✔
1211

1212
        /* This removes the last component of path and appends filename, unless the latter is absolute anyway
1213
         * or the former isn't */
1214

1215
        if (path_is_absolute(filename))
265✔
1216
                b = strdup(filename);
17✔
1217
        else {
1218
                _cleanup_free_ char *dn = NULL;
248✔
1219

1220
                r = path_extract_directory(path, &dn);
248✔
1221
                if (r == -EDESTADDRREQ) /* no path prefix */
248✔
1222
                        b = strdup(filename);
1✔
1223
                else if (r < 0)
247✔
1224
                        return r;
9✔
1225
                else
1226
                        b = path_join(dn, filename);
238✔
1227
        }
1228
        if (!b)
256✔
1229
                return -ENOMEM;
1230

1231
        *ret = TAKE_PTR(b);
256✔
1232
        return 0;
256✔
1233
}
1234

1235
bool hidden_or_backup_file(const char *filename) {
6,660,496✔
1236
        assert(filename);
6,660,496✔
1237

1238
        if (filename[0] == '.' ||
12,501,562✔
1239
            STR_IN_SET(filename,
5,841,066✔
1240
                       "lost+found",
1241
                       "aquota.user",
1242
                       "aquota.group") ||
5,841,063✔
1243
            endswith(filename, "~"))
5,841,063✔
1244
                return true;
819,435✔
1245

1246
        const char *dot = strrchr(filename, '.');
5,841,061✔
1247
        if (!dot)
5,841,061✔
1248
                return false;
1249

1250
        /* Please, let's not add more entries to the list below. If external projects think it's a good idea
1251
         * to come up with always new suffixes and that everybody else should just adjust to that, then it
1252
         * really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask
1253
         * those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups,
1254
         * possibly augmented with an additional string. Specifically: there's now:
1255
         *
1256
         *    The generic suffixes "~" and ".bak" for backup files
1257
         *    The generic prefix "." for hidden files
1258
         *
1259
         * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old",
1260
         * ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new",
1261
         * ".foopkg.old" or ".foopkg~" instead.
1262
         */
1263

1264
        return STR_IN_SET(dot + 1,
5,341,688✔
1265
                          "rpmnew",
1266
                          "rpmsave",
1267
                          "rpmorig",
1268
                          "dpkg-old",
1269
                          "dpkg-new",
1270
                          "dpkg-tmp",
1271
                          "dpkg-dist",
1272
                          "dpkg-bak",
1273
                          "dpkg-backup",
1274
                          "dpkg-remove",
1275
                          "ucf-new",
1276
                          "ucf-old",
1277
                          "ucf-dist",
1278
                          "swp",
1279
                          "bak",
1280
                          "old",
1281
                          "new");
1282
}
1283

1284
bool is_device_path(const char *path) {
13,311✔
1285

1286
        /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
1287
         * /dev. This accepts any path that starts with /dev/ or /sys/ and has something after that prefix.
1288
         * It does not actually resolve the path.
1289
         *
1290
         * Examples:
1291
         * /dev/sda, /dev/sda/foo, /sys/class, /dev/.., /sys/.., /./dev/foo → yes.
1292
         * /../dev/sda, /dev, /sys, /usr/path, /usr/../dev/sda → no.
1293
         */
1294

1295
        const char *p = PATH_STARTSWITH_SET(ASSERT_PTR(path), "/dev/", "/sys/");
13,311✔
1296
        return !isempty(p);
13,311✔
1297
}
1298

1299
bool valid_device_node_path(const char *path) {
452✔
1300

1301
        /* Some superficial checks whether the specified path is a valid device node path, all without
1302
         * looking at the actual device node. */
1303

1304
        if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
452✔
UNCOV
1305
                return false;
×
1306

1307
        if (endswith(path, "/")) /* can't be a device node if it ends in a slash */
452✔
1308
                return false;
1309

1310
        return path_is_normalized(path);
452✔
1311
}
1312

1313
bool valid_device_allow_pattern(const char *path) {
12✔
1314
        assert(path);
12✔
1315

1316
        /* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by
1317
         * DeviceAllow= and DeviceDeny=. */
1318

1319
        if (STARTSWITH_SET(path, "block-", "char-"))
12✔
1320
                return true;
4✔
1321

1322
        return valid_device_node_path(path);
8✔
1323
}
1324

1325
bool dot_or_dot_dot(const char *path) {
6,229,935✔
1326
        if (!path)
6,229,935✔
1327
                return false;
1328
        if (path[0] != '.')
6,229,934✔
1329
                return false;
1330
        if (path[1] == 0)
260,916✔
1331
                return true;
1332
        if (path[1] != '.')
130,916✔
1333
                return false;
1334

1335
        return path[2] == 0;
128,895✔
1336
}
1337

1338
bool path_implies_directory(const char *path) {
19,091✔
1339

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

1343
        if (!path)
19,091✔
1344
                return false;
19,091✔
1345

1346
        if (dot_or_dot_dot(path))
17,339✔
1347
                return true;
1348

1349
        return ENDSWITH_SET(path, "/", "/.", "/..");
17,337✔
1350
}
1351

1352
bool empty_or_root(const char *path) {
10,381,986✔
1353

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

1357
        if (isempty(path))
10,381,986✔
1358
                return true;
1359

1360
        return path_equal(path, "/");
3,322,330✔
1361
}
1362

1363
bool path_strv_contains(char * const *l, const char *path) {
80,832✔
1364
        assert(path);
80,832✔
1365

1366
        STRV_FOREACH(i, l)
1,146,992✔
1367
                if (path_equal(*i, path))
1,080,375✔
1368
                        return true;
1369

1370
        return false;
1371
}
1372

1373
bool prefixed_path_strv_contains(char * const *l, const char *path) {
70✔
1374
        assert(path);
70✔
1375

1376
        STRV_FOREACH(i, l) {
71✔
1377
                const char *j = *i;
1✔
1378

1379
                if (*j == '-')
1✔
UNCOV
1380
                        j++;
×
1381
                if (*j == '+')
1✔
UNCOV
1382
                        j++;
×
1383

1384
                if (path_equal(j, path))
1✔
1385
                        return true;
1386
        }
1387

1388
        return false;
1389
}
1390

1391
int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
10,991✔
1392
        assert(pattern);
10,991✔
1393
        assert(prefix);
10,991✔
1394

1395
        for (const char *a = pattern, *b = prefix;;) {
10,991✔
1396
                _cleanup_free_ char *g = NULL, *h = NULL;
24,744✔
1397
                const char *p, *q;
32,975✔
1398
                int r, s;
32,975✔
1399

1400
                r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p);
32,975✔
1401
                if (r < 0)
32,975✔
1402
                        return r;
1403

1404
                s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q);
32,975✔
1405
                if (s < 0)
32,975✔
1406
                        return s;
1407

1408
                if (s == 0) {
32,975✔
1409
                        /* The pattern matches the prefix. */
1410
                        if (ret) {
2,760✔
1411
                                char *t;
2,760✔
1412

1413
                                t = path_join(prefix, p);
2,760✔
1414
                                if (!t)
2,760✔
1415
                                        return -ENOMEM;
1416

1417
                                *ret = t;
2,760✔
1418
                        }
1419
                        return true;
2,760✔
1420
                }
1421

1422
                if (r == 0)
30,215✔
1423
                        break;
1424

1425
                if (r == s && strneq(p, q, r))
30,211✔
1426
                        continue; /* common component. Check next. */
19,225✔
1427

1428
                g = strndup(p, r);
10,986✔
1429
                if (!g)
10,986✔
1430
                        return -ENOMEM;
1431

1432
                if (!string_is_glob(g))
10,986✔
1433
                        break;
1434

1435
                /* We found a glob component. Check if the glob pattern matches the prefix component. */
1436

1437
                h = strndup(q, s);
2,759✔
1438
                if (!h)
2,759✔
1439
                        return -ENOMEM;
1440

1441
                r = fnmatch(g, h, 0);
2,759✔
1442
                if (r == FNM_NOMATCH)
2,759✔
1443
                        break;
1444
                if (r != 0) /* Failure to process pattern? */
2,759✔
1445
                        return -EINVAL;
1446
        }
1447

1448
        /* The pattern does not match the prefix. */
1449
        if (ret)
8,231✔
1450
                *ret = NULL;
8,231✔
1451
        return false;
1452
}
1453

1454
const char* default_PATH(void) {
3,258✔
1455
#if HAVE_SPLIT_BIN
1456
        static int split = -1;
1457
        int r;
1458

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

1462
        if (split < 0)
1463
                STRV_FOREACH_PAIR(bin, sbin, STRV_MAKE("/usr/bin", "/usr/sbin",
1464
                                                       "/usr/local/bin", "/usr/local/sbin")) {
1465
                        r = inode_same(*bin, *sbin, AT_NO_AUTOMOUNT);
1466
                        if (r > 0 || r == -ENOENT)
1467
                                continue;
1468
                        if (r < 0)
1469
                                log_debug_errno(r, "Failed to compare \"%s\" and \"%s\", using compat $PATH: %m",
1470
                                                *bin, *sbin);
1471
                        split = true;
1472
                        break;
1473
                }
1474
        if (split < 0)
1475
                split = false;
1476
        if (split)
1477
                return DEFAULT_PATH_WITH_SBIN;
1478
#endif
1479
        return DEFAULT_PATH_WITHOUT_SBIN;
3,258✔
1480
}
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