]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/path-util.c
Merge pull request #19485 from yuwata/path-util
[thirdparty/systemd.git] / src / basic / path-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
9eb977db 2
9eb977db 3#include <errno.h>
11c3a366 4#include <limits.h>
07630cea
LP
5#include <stdio.h>
6#include <stdlib.h>
07630cea 7#include <unistd.h>
9eb977db 8
5f311f8c
LP
9/* When we include libgen.h because we need dirname() we immediately
10 * undefine basename() since libgen.h defines it as a macro to the
11 * POSIX version which is really broken. We prefer GNU basename(). */
12#include <libgen.h>
13#undef basename
14
b5efdb8a 15#include "alloc-util.h"
93cc7779 16#include "extract-word.h"
bb0c0d6f 17#include "fd-util.h"
f4f15635 18#include "fs-util.h"
5a46d55f 19#include "glob-util.h"
9eb977db 20#include "log.h"
07630cea 21#include "macro.h"
d8b4d14d 22#include "nulstr-util.h"
5a46d55f 23#include "parse-util.h"
3ffd4af2 24#include "path-util.h"
8fcde012 25#include "stat-util.h"
07630cea 26#include "string-util.h"
9eb977db 27#include "strv.h"
93cc7779 28#include "time-util.h"
58a53add 29#include "utf8.h"
9eb977db 30
0f474365 31int path_split_and_make_absolute(const char *p, char ***ret) {
9eb977db 32 char **l;
0f474365
LP
33 int r;
34
9eb977db 35 assert(p);
0f474365 36 assert(ret);
9eb977db 37
116cc028
ZJS
38 l = strv_split(p, ":");
39 if (!l)
ce9d6bcf 40 return -ENOMEM;
9eb977db 41
0f474365
LP
42 r = path_strv_make_absolute_cwd(l);
43 if (r < 0) {
9eb977db 44 strv_free(l);
0f474365 45 return r;
9eb977db
KS
46 }
47
0f474365
LP
48 *ret = l;
49 return r;
9eb977db
KS
50}
51
52char *path_make_absolute(const char *p, const char *prefix) {
53 assert(p);
54
55 /* Makes every item in the list an absolute path by prepending
56 * the prefix, if specified and necessary */
57
81cce8de 58 if (path_is_absolute(p) || isempty(prefix))
9eb977db
KS
59 return strdup(p);
60
657ee2d8 61 return path_join(prefix, p);
9eb977db
KS
62}
63
a2556d25
LP
64int safe_getcwd(char **ret) {
65 char *cwd;
66
67 cwd = get_current_dir_name();
68 if (!cwd)
69 return negative_errno();
70
71 /* Let's make sure the directory is really absolute, to protect us from the logic behind
72 * CVE-2018-1000001 */
73 if (cwd[0] != '/') {
74 free(cwd);
75 return -ENOMEDIUM;
76 }
77
78 *ret = cwd;
79 return 0;
80}
81
0f474365
LP
82int path_make_absolute_cwd(const char *p, char **ret) {
83 char *c;
d7249575 84 int r;
9eb977db
KS
85
86 assert(p);
0f474365 87 assert(ret);
9eb977db
KS
88
89 /* Similar to path_make_absolute(), but prefixes with the
90 * current working directory. */
91
92 if (path_is_absolute(p))
0f474365
LP
93 c = strdup(p);
94 else {
95 _cleanup_free_ char *cwd = NULL;
9eb977db 96
d7249575
LP
97 r = safe_getcwd(&cwd);
98 if (r < 0)
99 return r;
0f474365 100
62a85ee0 101 c = path_join(cwd, p);
0f474365
LP
102 }
103 if (!c)
104 return -ENOMEM;
9eb977db 105
0f474365
LP
106 *ret = c;
107 return 0;
9eb977db
KS
108}
109
fe69c41e
YW
110int path_make_relative(const char *from, const char *to, char **ret) {
111 _cleanup_free_ char *result = NULL;
112 unsigned n_parents;
113 const char *f, *t;
114 int r, k;
115 char *p;
116
117 assert(from);
118 assert(to);
119 assert(ret);
7cb9c51c
TK
120
121 /* Strips the common part, and adds ".." elements as necessary. */
122
fe69c41e 123 if (!path_is_absolute(from) || !path_is_absolute(to))
7cb9c51c
TK
124 return -EINVAL;
125
7cb9c51c 126 for (;;) {
fe69c41e
YW
127 r = path_find_first_component(&from, true, &f);
128 if (r < 0)
129 return r;
130
131 k = path_find_first_component(&to, true, &t);
132 if (k < 0)
133 return k;
134
135 if (r == 0) {
136 /* end of 'from' */
137 if (k == 0) {
138 /* from and to are equivalent. */
139 result = strdup(".");
140 if (!result)
141 return -ENOMEM;
142 } else {
143 /* 'to' is inside of 'from'. */
144 result = strdup(t);
145 if (!result)
146 return -ENOMEM;
147
4ff361cc 148 path_simplify(result);
fe69c41e
YW
149
150 if (!path_is_valid(result))
151 return -EINVAL;
152 }
153
154 *ret = TAKE_PTR(result);
7cb9c51c
TK
155 return 0;
156 }
157
fe69c41e 158 if (r != k || !strneq(f, t, r))
7cb9c51c 159 break;
7cb9c51c
TK
160 }
161
162 /* If we're here, then "from_dir" has one or more elements that need to
163 * be replaced with "..". */
164
fe69c41e
YW
165 for (n_parents = 1;; n_parents++) {
166 /* If this includes ".." we can't do a simple series of "..". */
167 r = path_find_first_component(&from, false, &f);
168 if (r < 0)
169 return r;
170 if (r == 0)
171 break;
172 }
2a5beb66 173
fe69c41e
YW
174 if (isempty(t) && n_parents * 3 > PATH_MAX)
175 /* PATH_MAX is counted *with* the trailing NUL byte */
176 return -EINVAL;
2a5beb66 177
fe69c41e
YW
178 result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t));
179 if (!result)
180 return -ENOMEM;
2a5beb66 181
fe69c41e
YW
182 for (p = result; n_parents > 0; n_parents--)
183 p = mempcpy(p, "../", 3);
2a5beb66 184
fe69c41e
YW
185 if (isempty(t)) {
186 /* Remove trailing slash and terminate string. */
187 *(--p) = '\0';
188 *ret = TAKE_PTR(result);
189 return 0;
7cb9c51c
TK
190 }
191
fe69c41e 192 strcpy(p, t);
7cb9c51c 193
4ff361cc 194 path_simplify(result);
7cb9c51c 195
fe69c41e
YW
196 if (!path_is_valid(result))
197 return -EINVAL;
7cb9c51c 198
fe69c41e 199 *ret = TAKE_PTR(result);
7cb9c51c
TK
200 return 0;
201}
202
cc4d7d81
ZJS
203char* path_startswith_strv(const char *p, char **set) {
204 char **s, *t;
205
206 STRV_FOREACH(s, set) {
207 t = path_startswith(p, *s);
208 if (t)
209 return t;
210 }
211
212 return NULL;
213}
214
0f474365 215int path_strv_make_absolute_cwd(char **l) {
9eb977db 216 char **s;
0f474365 217 int r;
9eb977db
KS
218
219 /* Goes through every item in the string list and makes it
220 * absolute. This works in place and won't rollback any
221 * changes on failure. */
222
223 STRV_FOREACH(s, l) {
224 char *t;
225
0f474365
LP
226 r = path_make_absolute_cwd(*s, &t);
227 if (r < 0)
228 return r;
9eb977db 229
4ff361cc 230 path_simplify(t);
32a8f700 231 free_and_replace(*s, t);
9eb977db
KS
232 }
233
0f474365 234 return 0;
9eb977db
KS
235}
236
e1873695 237char **path_strv_resolve(char **l, const char *root) {
9eb977db
KS
238 char **s;
239 unsigned k = 0;
240 bool enomem = false;
e1873695 241 int r;
9eb977db
KS
242
243 if (strv_isempty(l))
244 return l;
245
246 /* Goes through every item in the string list and canonicalize
247 * the path. This works in place and won't rollback any
248 * changes on failure. */
249
250 STRV_FOREACH(s, l) {
12ed81d9 251 _cleanup_free_ char *orig = NULL;
e1873695 252 char *t, *u;
9eb977db 253
12ed81d9
ZJS
254 if (!path_is_absolute(*s)) {
255 free(*s);
9eb977db 256 continue;
12ed81d9 257 }
112cfb18 258
e1873695 259 if (root) {
12ed81d9 260 orig = *s;
c6134d3e 261 t = path_join(root, orig);
112cfb18
MM
262 if (!t) {
263 enomem = true;
264 continue;
265 }
12ed81d9 266 } else
112cfb18 267 t = *s;
9eb977db 268
a5648b80 269 r = chase_symlinks(t, root, 0, &u, NULL);
e1873695
LP
270 if (r == -ENOENT) {
271 if (root) {
ae2a15bc 272 u = TAKE_PTR(orig);
874310b7 273 free(t);
e1873695
LP
274 } else
275 u = t;
276 } else if (r < 0) {
277 free(t);
874310b7 278
e1873695
LP
279 if (r == -ENOMEM)
280 enomem = true;
281
282 continue;
283 } else if (root) {
12ed81d9
ZJS
284 char *x;
285
286 free(t);
e1873695 287 x = path_startswith(u, root);
12ed81d9
ZJS
288 if (x) {
289 /* restore the slash if it was lost */
290 if (!startswith(x, "/"))
291 *(--x) = '/';
292
293 t = strdup(x);
294 free(u);
295 if (!t) {
296 enomem = true;
297 continue;
298 }
299 u = t;
300 } else {
301 /* canonicalized path goes outside of
302 * prefix, keep the original path instead */
3b319885 303 free_and_replace(u, orig);
12ed81d9 304 }
91a6489d
LP
305 } else
306 free(t);
9eb977db
KS
307
308 l[k++] = u;
309 }
310
311 l[k] = NULL;
312
313 if (enomem)
314 return NULL;
315
316 return l;
317}
318
e1873695 319char **path_strv_resolve_uniq(char **l, const char *root) {
112cfb18 320
fabe5c0e
LP
321 if (strv_isempty(l))
322 return l;
323
e1873695 324 if (!path_strv_resolve(l, root))
fabe5c0e
LP
325 return NULL;
326
327 return strv_uniq(l);
328}
329
4ff361cc 330char *path_simplify(char *path) {
cb71ed91
YW
331 bool add_slash = false;
332 char *f = path;
333 int r;
9eb977db 334
858d36c1
YW
335 assert(path);
336
cb71ed91
YW
337 /* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
338 * Modifies the passed string in-place.
9eb977db 339 *
cb71ed91
YW
340 * ///foo//./bar/. becomes /foo/bar
341 * .//./foo//./bar/. becomes foo/bar
9eb977db
KS
342 */
343
afbae3e9
TH
344 if (isempty(path))
345 return path;
346
cb71ed91 347 if (path_is_absolute(path))
858d36c1 348 f++;
858d36c1 349
cb71ed91
YW
350 for (const char *p = f;;) {
351 const char *e;
9eb977db 352
cb71ed91
YW
353 r = path_find_first_component(&p, true, &e);
354 if (r == 0)
355 break;
9eb977db 356
cb71ed91
YW
357 if (add_slash)
358 *f++ = '/';
858d36c1 359
cb71ed91
YW
360 if (r < 0) {
361 /* if path is invalid, then refuse to simplify remaining part. */
362 memmove(f, p, strlen(p) + 1);
363 return path;
9eb977db
KS
364 }
365
cb71ed91
YW
366 memmove(f, e, r);
367 f += r;
9eb977db 368
cb71ed91 369 add_slash = true;
afbae3e9 370 }
9eb977db 371
cb71ed91
YW
372 /* Special rule, if we stripped everything, we need a "." for the current directory. */
373 if (f == path)
374 *f++ = '.';
375
376 *f = '\0';
9eb977db
KS
377 return path;
378}
379
b47aa73a
ZJS
380int path_simplify_and_warn(
381 char *path,
382 unsigned flag,
383 const char *unit,
384 const char *filename,
385 unsigned line,
386 const char *lvalue) {
387
388 bool fatal = flag & PATH_CHECK_FATAL;
389
390 assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
391
392 if (!utf8_is_valid(path))
393 return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
394
395 if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
396 bool absolute;
397
398 absolute = path_is_absolute(path);
399
400 if (!absolute && (flag & PATH_CHECK_ABSOLUTE))
401 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
402 "%s= path is not absolute%s: %s",
403 lvalue, fatal ? "" : ", ignoring", path);
404
405 if (absolute && (flag & PATH_CHECK_RELATIVE))
406 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
407 "%s= path is absolute%s: %s",
408 lvalue, fatal ? "" : ", ignoring", path);
409 }
410
4ff361cc 411 path_simplify(path);
b47aa73a
ZJS
412
413 if (!path_is_valid(path))
414 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
415 "%s= path has invalid length (%zu bytes)%s.",
416 lvalue, strlen(path), fatal ? "" : ", ignoring");
417
418 if (!path_is_normalized(path))
419 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
420 "%s= path is not normalized%s: %s",
421 lvalue, fatal ? "" : ", ignoring", path);
422
423 return 0;
424}
425
63f11e35 426char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
9eb977db
KS
427 assert(path);
428 assert(prefix);
429
0470289b
ZJS
430 /* Returns a pointer to the start of the first component after the parts matched by
431 * the prefix, iff
432 * - both paths are absolute or both paths are relative,
433 * and
434 * - each component in prefix in turn matches a component in path at the same position.
435 * An empty string will be returned when the prefix and path are equivalent.
436 *
437 * Returns NULL otherwise.
438 */
439
9eb977db 440 if ((path[0] == '/') != (prefix[0] == '/'))
424a19f8 441 return NULL;
9eb977db
KS
442
443 for (;;) {
63f11e35
YW
444 const char *p, *q;
445 int r, k;
9eb977db 446
63f11e35
YW
447 r = path_find_first_component(&path, accept_dot_dot, &p);
448 if (r < 0)
449 return NULL;
9eb977db 450
63f11e35
YW
451 k = path_find_first_component(&prefix, accept_dot_dot, &q);
452 if (k < 0)
424a19f8 453 return NULL;
9eb977db 454
63f11e35
YW
455 if (k == 0)
456 return (char*) (p ?: path);
9eb977db 457
63f11e35 458 if (r != k)
424a19f8 459 return NULL;
9eb977db 460
63f11e35 461 if (!strneq(p, q, r))
424a19f8 462 return NULL;
9eb977db
KS
463 }
464}
465
2230852b 466int path_compare(const char *a, const char *b) {
353df443 467 int r;
2230852b 468
9eb977db
KS
469 assert(a);
470 assert(b);
471
f21f31b2 472 /* A relative path and an absolute path must not compare as equal.
2230852b
MS
473 * Which one is sorted before the other does not really matter.
474 * Here a relative path is ordered before an absolute path. */
353df443
YW
475 r = CMP(path_is_absolute(a), path_is_absolute(b));
476 if (r != 0)
477 return r;
9eb977db
KS
478
479 for (;;) {
353df443
YW
480 const char *aa, *bb;
481 int j, k;
9eb977db 482
353df443
YW
483 j = path_find_first_component(&a, true, &aa);
484 k = path_find_first_component(&b, true, &bb);
9eb977db 485
353df443
YW
486 if (j < 0 || k < 0) {
487 /* When one of paths is invalid, order invalid path after valid one. */
488 r = CMP(j < 0, k < 0);
489 if (r != 0)
490 return r;
491
492 /* fallback to use strcmp() if both paths are invalid. */
493 return strcmp(a, b);
494 }
9eb977db 495
2230852b 496 /* Order prefixes first: "/foo" before "/foo/bar" */
353df443
YW
497 if (j == 0) {
498 if (k == 0)
499 return 0;
2230852b 500 return -1;
353df443
YW
501 }
502 if (k == 0)
2230852b 503 return 1;
9eb977db 504
2230852b 505 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
353df443
YW
506 r = memcmp(aa, bb, MIN(j, k));
507 if (r != 0)
508 return r;
9eb977db 509
2230852b 510 /* Sort "/foo/a" before "/foo/aaa" */
353df443
YW
511 r = CMP(j, k);
512 if (r != 0)
513 return r;
9eb977db
KS
514 }
515}
516
2230852b
MS
517bool path_equal(const char *a, const char *b) {
518 return path_compare(a, b) == 0;
519}
520
e3f791a2
ZJS
521bool path_equal_or_files_same(const char *a, const char *b, int flags) {
522 return path_equal(a, b) || files_same(a, b, flags) > 0;
c78e47a6
MS
523}
524
727e63e3
LB
525bool path_equal_filename(const char *a, const char *b) {
526 _cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
527 int r;
528
529 assert(a);
530 assert(b);
531
532 r = path_extract_filename(a, &a_basename);
533 if (r < 0) {
534 log_debug_errno(r, "Failed to parse basename of %s: %m", a);
535 return false;
536 }
537 r = path_extract_filename(b, &b_basename);
538 if (r < 0) {
539 log_debug_errno(r, "Failed to parse basename of %s: %m", b);
540 return false;
541 }
542
543 return path_equal(a_basename, b_basename);
544}
545
7ae27680
LP
546char* path_extend_internal(char **x, ...) {
547 size_t sz, old_sz;
548 char *q, *nx;
cd8194a3
LP
549 const char *p;
550 va_list ap;
551 bool slash;
cd8194a3 552
652ef298
ZJS
553 /* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
554 * already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
555 * removed. The string returned is hence always equal to or longer than the sum of the lengths of each
556 * individual string.
cd8194a3 557 *
7ae27680
LP
558 * The first argument may be an already allocated string that is extended via realloc() if
559 * non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the
560 * first parameter to distinguish the two operations.
561 *
cd8194a3
LP
562 * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
563 * are optional.
564 *
565 * Examples:
566 *
62a85ee0
ZJS
567 * path_join("foo", "bar") → "foo/bar"
568 * path_join("foo/", "bar") → "foo/bar"
569 * path_join("", "foo", "", "bar", "") → "foo/bar" */
cd8194a3 570
7ae27680
LP
571 sz = old_sz = x ? strlen_ptr(*x) : 0;
572 va_start(ap, x);
573 while ((p = va_arg(ap, char*)) != POINTER_MAX) {
574 size_t add;
575
576 if (isempty(p))
577 continue;
578
579 add = 1 + strlen(p);
580 if (sz > SIZE_MAX - add) /* overflow check */
581 return NULL;
582
583 sz += add;
584 }
585
cd8194a3
LP
586 va_end(ap);
587
7ae27680
LP
588 nx = realloc(x ? *x : NULL, GREEDY_ALLOC_ROUND_UP(sz+1));
589 if (!nx)
cd8194a3 590 return NULL;
7ae27680
LP
591 if (x)
592 *x = nx;
cd8194a3 593
7ae27680 594 if (old_sz > 0)
340cd6b6 595 slash = nx[old_sz-1] == '/';
7ae27680
LP
596 else {
597 nx[old_sz] = 0;
cd8194a3
LP
598 slash = true; /* no need to generate a slash anymore */
599 }
600
7ae27680
LP
601 q = nx + old_sz;
602
603 va_start(ap, x);
66032ef4 604 while ((p = va_arg(ap, char*)) != POINTER_MAX) {
652ef298 605 if (isempty(p))
cd8194a3
LP
606 continue;
607
608 if (!slash && p[0] != '/')
609 *(q++) = '/';
610
611 q = stpcpy(q, p);
612 slash = endswith(p, "/");
613 }
614 va_end(ap);
615
7ae27680 616 return nx;
cd8194a3
LP
617}
618
5ca9139a 619static int check_x_access(const char *path, int *ret_fd) {
ece852c8 620 _cleanup_close_ int fd = -1;
ece852c8 621 int r;
5ca9139a 622
888f65ac
YW
623 /* We need to use O_PATH because there may be executables for which we have only exec
624 * permissions, but not read (usually suid executables). */
625 fd = open(path, O_PATH|O_CLOEXEC);
626 if (fd < 0)
627 return -errno;
ece852c8 628
888f65ac
YW
629 r = fd_verify_regular(fd);
630 if (r < 0)
631 return r;
ece852c8 632
888f65ac
YW
633 r = access_fd(fd, X_OK);
634 if (r < 0)
635 return r;
ece852c8
YW
636
637 if (ret_fd)
638 *ret_fd = TAKE_FD(fd);
639
5ca9139a
ZJS
640 return 0;
641}
642
643int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
85eca92e 644 int last_error, r;
92673045 645 const char *p = NULL;
85eca92e 646
c9d954b2 647 assert(name);
4087cb9e 648
571d0134 649 if (is_path(name)) {
5ca9139a 650 _cleanup_close_ int fd = -1;
b972115c 651
5ca9139a
ZJS
652 r = check_x_access(name, ret_fd ? &fd : NULL);
653 if (r < 0)
654 return r;
655
656 if (ret_filename) {
657 r = path_make_absolute_cwd(name, ret_filename);
0f474365
LP
658 if (r < 0)
659 return r;
eb66db55 660 }
c9d954b2 661
5ca9139a
ZJS
662 if (ret_fd)
663 *ret_fd = TAKE_FD(fd);
664
c9d954b2 665 return 0;
85eca92e 666 }
c9d954b2 667
92673045
ZJS
668 if (use_path_envvar)
669 /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
670 * binary. */
671 p = getenv("PATH");
85eca92e
LP
672 if (!p)
673 p = DEFAULT_PATH;
674
675 last_error = -ENOENT;
676
5ca9139a 677 /* Resolve a single-component name to a full path */
85eca92e 678 for (;;) {
4ede9802 679 _cleanup_free_ char *element = NULL;
5ca9139a 680 _cleanup_close_ int fd = -1;
85eca92e
LP
681
682 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
683 if (r < 0)
684 return r;
685 if (r == 0)
686 break;
687
0f474365
LP
688 if (!path_is_absolute(element))
689 continue;
690
4ede9802 691 if (!path_extend(&element, name))
85eca92e
LP
692 return -ENOMEM;
693
4ede9802 694 r = check_x_access(element, ret_fd ? &fd : NULL);
ece852c8
YW
695 if (r < 0) {
696 /* PATH entries which we don't have access to are ignored, as per tradition. */
697 if (r != -EACCES)
698 last_error = r;
699 continue;
c9d954b2
ZJS
700 }
701
ece852c8
YW
702 /* Found it! */
703 if (ret_filename)
4ff361cc 704 *ret_filename = path_simplify(TAKE_PTR(element));
ece852c8
YW
705 if (ret_fd)
706 *ret_fd = TAKE_FD(fd);
707
708 return 0;
c9d954b2 709 }
85eca92e
LP
710
711 return last_error;
c9d954b2 712}
8e184852 713
2ad8416d 714bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
8e184852 715 bool changed = false;
2ad8416d 716 const char* const* i;
8e184852 717
97f2d76d
TG
718 assert(timestamp);
719
234519ae 720 if (!paths)
4087cb9e 721 return false;
8e184852 722
4087cb9e 723 STRV_FOREACH(i, paths) {
8e184852 724 struct stat stats;
4087cb9e 725 usec_t u;
8e184852 726
4087cb9e 727 if (stat(*i, &stats) < 0)
8e184852
TG
728 continue;
729
4087cb9e
LP
730 u = timespec_load(&stats.st_mtim);
731
97f2d76d 732 /* first check */
4087cb9e 733 if (*timestamp >= u)
8e184852
TG
734 continue;
735
9f6445e3 736 log_debug("timestamp of '%s' changed", *i);
8e184852
TG
737
738 /* update timestamp */
4087cb9e
LP
739 if (update) {
740 *timestamp = u;
741 changed = true;
742 } else
743 return true;
8e184852 744 }
4087cb9e 745
8e184852
TG
746 return changed;
747}
eb66db55 748
f7bc0c32 749static int executable_is_good(const char *executable) {
571d0134 750 _cleanup_free_ char *p = NULL, *d = NULL;
571d0134 751 int r;
eb66db55 752
f7bc0c32 753 r = find_executable(executable, &p);
85eca92e
LP
754 if (r == -ENOENT)
755 return 0;
571d0134
LP
756 if (r < 0)
757 return r;
758
f7bc0c32 759 /* An fsck that is linked to /bin/true is a non-existent fsck */
571d0134
LP
760
761 r = readlink_malloc(p, &d);
85eca92e
LP
762 if (r == -EINVAL) /* not a symlink */
763 return 1;
764 if (r < 0)
765 return r;
571d0134 766
3ae5990c
ZJS
767 return !PATH_IN_SET(d, "true"
768 "/bin/true",
769 "/usr/bin/true",
770 "/dev/null");
eb66db55 771}
1d13f648 772
5bcd08db
LP
773int fsck_exists(const char *fstype) {
774 const char *checker;
775
85eca92e 776 assert(fstype);
5bcd08db 777
85eca92e
LP
778 if (streq(fstype, "auto"))
779 return -EINVAL;
780
781 checker = strjoina("fsck.", fstype);
f7bc0c32 782 return executable_is_good(checker);
5bcd08db
LP
783}
784
5f311f8c
LP
785char* dirname_malloc(const char *path) {
786 char *d, *dir, *dir2;
787
788 assert(path);
789
790 d = strdup(path);
791 if (!d)
792 return NULL;
793
794 dir = dirname(d);
795 assert(dir);
796
797 if (dir == d)
798 return d;
799
800 dir2 = strdup(dir);
801 free(d);
802
803 return dir2;
804}
bb15fafe 805
0ee54dd4
YW
806static const char *skip_slash_or_dot(const char *p) {
807 for (; !isempty(p); p++) {
808 if (*p == '/')
809 continue;
810 if (startswith(p, "./")) {
811 p++;
812 continue;
813 }
814 break;
815 }
816 return p;
817}
818
819int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret) {
820 const char *q, *first, *end_first, *next;
821 size_t len;
822
823 assert(p);
824
825 /* When a path is input, then returns the pointer to the first component and its length, and
826 * move the input pointer to the next component or nul. This skips both over any '/'
827 * immediately *before* and *after* the first component before returning.
828 *
829 * Examples
830 * Input: p: "//.//aaa///bbbbb/cc"
831 * Output: p: "bbbbb///cc"
832 * ret: "aaa///bbbbb/cc"
833 * return value: 3 (== strlen("aaa"))
834 *
835 * Input: p: "aaa//"
836 * Output: p: (pointer to NUL)
837 * ret: "aaa//"
838 * return value: 3 (== strlen("aaa"))
839 *
840 * Input: p: "/", ".", ""
841 * Output: p: (pointer to NUL)
842 * ret: NULL
843 * return value: 0
844 *
845 * Input: p: NULL
846 * Output: p: NULL
847 * ret: NULL
848 * return value: 0
849 *
850 * Input: p: "(too long component)"
851 * Output: return value: -EINVAL
852 *
853 * (when accept_dot_dot is false)
854 * Input: p: "//..//aaa///bbbbb/cc"
855 * Output: return value: -EINVAL
856 */
857
858 q = *p;
859
860 first = skip_slash_or_dot(q);
861 if (isempty(first)) {
862 *p = first;
863 if (ret)
864 *ret = NULL;
865 return 0;
866 }
867 if (streq(first, ".")) {
868 *p = first + 1;
869 if (ret)
870 *ret = NULL;
871 return 0;
872 }
873
874 end_first = strchrnul(first, '/');
875 len = end_first - first;
876
877 if (len > NAME_MAX)
878 return -EINVAL;
879 if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.')
880 return -EINVAL;
881
882 next = skip_slash_or_dot(end_first);
883
884 *p = next + streq(next, ".");
885 if (ret)
886 *ret = first;
887 return len;
888}
889
484cd43c
YW
890static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
891 assert(path);
892
893 for (; q >= path; q--) {
894 if (*q == '/')
895 continue;
896 if (q > path && strneq(q - 1, "/.", 2))
897 continue;
898 break;
899 }
900 return q;
901}
902
903int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret) {
904 const char *q, *last_end, *last_begin;
905 size_t len;
906
907 /* Similar to path_find_first_component(), but search components from the end.
908 *
909 * Examples
910 * Input: path: "//.//aaa///bbbbb/cc//././"
911 * next: NULL
912 * Output: next: "/cc//././"
913 * ret: "cc//././"
914 * return value: 2 (== strlen("cc"))
915 *
916 * Input: path: "//.//aaa///bbbbb/cc//././"
917 * next: "/cc//././"
918 * Output: next: "///bbbbb/cc//././"
919 * ret: "bbbbb/cc//././"
920 * return value: 5 (== strlen("bbbbb"))
921 *
922 * Input: path: "/", ".", "", or NULL
923 * Output: next: equivalent to path
924 * ret: NULL
925 * return value: 0
926 *
927 * Input: path: "(too long component)"
928 * Output: return value: -EINVAL
929 *
930 * (when accept_dot_dot is false)
931 * Input: path: "//..//aaa///bbbbb/cc/..//"
932 * Output: return value: -EINVAL
933 */
934
935 if (isempty(path)) {
936 if (next)
937 *next = path;
938 if (ret)
939 *ret = NULL;
940 return 0;
941 }
942
943 if (next && *next) {
944 if (*next < path || *next > path + strlen(path))
945 return -EINVAL;
946 if (*next == path) {
947 if (ret)
948 *ret = NULL;
949 return 0;
950 }
951 if (!IN_SET(**next, '\0', '/'))
952 return -EINVAL;
953 q = *next - 1;
954 } else
955 q = path + strlen(path) - 1;
956
957 q = skip_slash_or_dot_backward(path, q);
958 if ((q < path) || /* the root directory */
959 (q == path && *q == '.')) { /* path is "." or "./" */
960 if (next)
961 *next = path;
962 if (ret)
963 *ret = NULL;
964 return 0;
965 }
966
967 last_end = q + 1;
968
969 while (q >= path && *q != '/')
970 q--;
971
972 last_begin = q + 1;
973 len = last_end - last_begin;
974
975 if (len > NAME_MAX)
976 return -EINVAL;
977 if (!accept_dot_dot && len == 2 && strneq(last_begin, "..", 2))
978 return -EINVAL;
979
980 if (next) {
981 q = skip_slash_or_dot_backward(path, q);
982 if (q < path)
983 *next = path;
984 else
985 *next = q + 1;
986 }
987
988 if (ret)
989 *ret = last_begin;
990 return len;
991}
992
b12d25a8 993const char *last_path_component(const char *path) {
8460289f
LP
994
995 /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
996 *
b12d25a8
ZJS
997 * a/b/c → c
998 * a/b/c/ → c/
8460289f
LP
999 * x → x
1000 * x/ → x/
1001 * /y → y
1002 * /y/ → y/
b12d25a8
ZJS
1003 * / → /
1004 * // → /
1005 * /foo/a → a
1006 * /foo/a/ → a/
8460289f
LP
1007 *
1008 * Also, the empty string is mapped to itself.
1009 *
1010 * This is different than basename(), which returns "" when a trailing slash is present.
3cdcbdd3
LP
1011 *
1012 * This always succeeds (except if you pass NULL in which case it returns NULL, too).
b12d25a8
ZJS
1013 */
1014
1015 unsigned l, k;
1016
77e0a1b5
LP
1017 if (!path)
1018 return NULL;
1019
b12d25a8 1020 l = k = strlen(path);
69f9ccf1
ZJS
1021 if (l == 0) /* special case — an empty string */
1022 return path;
1023
b12d25a8
ZJS
1024 while (k > 0 && path[k-1] == '/')
1025 k--;
1026
1027 if (k == 0) /* the root directory */
1028 return path + l - 1;
1029
1030 while (k > 0 && path[k-1] != '/')
1031 k--;
1032
1033 return path + k;
1034}
1035
01950464 1036int path_extract_filename(const char *path, char **ret) {
a60c8eee 1037 _cleanup_free_ char *a = NULL;
01950464
YW
1038 const char *c, *next = NULL;
1039 int r;
a60c8eee
LP
1040
1041 /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
ee277c6b
LP
1042 * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
1043 * slashes. Returns:
1044 *
01950464
YW
1045 * -EINVAL → if the path is not valid
1046 * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir
1047 * itself or "." is specified
ee277c6b
LP
1048 * -ENOMEM → no memory
1049 *
01950464
YW
1050 * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to
1051 * indicate the referenced file must be a directory.
ee277c6b
LP
1052 *
1053 * This function guarantees to return a fully valid filename, i.e. one that passes
1054 * filename_is_valid() – this means "." and ".." are not accepted. */
a60c8eee 1055
01950464 1056 if (!path_is_valid(path))
a60c8eee
LP
1057 return -EINVAL;
1058
01950464
YW
1059 r = path_find_last_component(path, false, &next, &c);
1060 if (r < 0)
1061 return r;
1062 if (r == 0) /* root directory */
3cdcbdd3 1063 return -EADDRNOTAVAIL;
a60c8eee 1064
01950464 1065 a = strndup(c, r);
a60c8eee
LP
1066 if (!a)
1067 return -ENOMEM;
1068
a60c8eee 1069 *ret = TAKE_PTR(a);
01950464 1070 return strlen(c) > (size_t)r ? O_DIRECTORY : 0;
a60c8eee
LP
1071}
1072
01950464 1073int path_extract_directory(const char *path, char **ret) {
8dcb891c 1074 _cleanup_free_ char *a = NULL;
01950464
YW
1075 const char *c, *next = NULL;
1076 int r;
8dcb891c
LP
1077
1078 /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
1079 *
01950464 1080 * -EINVAL → if the path is not valid
8dcb891c 1081 * -EDESTADDRREQ → if no directory was specified in the passed in path, i.e. only a filename was passed
01950464
YW
1082 * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e.
1083 * the root dir itself or "." was specified
8dcb891c
LP
1084 * -ENOMEM → no memory (surprise!)
1085 *
1086 * This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
1087 */
1088
01950464
YW
1089 r = path_find_last_component(path, false, &next, &c);
1090 if (r < 0)
1091 return r;
1092 if (r == 0) /* empty or root */
1093 return isempty(path) ? -EINVAL : -EADDRNOTAVAIL;
1094 if (next == path) {
1095 if (*path != '/') /* filename only */
1096 return -EDESTADDRREQ;
1097
1098 a = strdup("/");
1099 if (!a)
1100 return -ENOMEM;
1101 *ret = TAKE_PTR(a);
1102 return 0;
1103 }
8dcb891c 1104
01950464 1105 a = strndup(path, next - path);
8dcb891c
LP
1106 if (!a)
1107 return -ENOMEM;
1108
4ff361cc 1109 path_simplify(a);
01950464 1110
8dcb891c
LP
1111 if (!path_is_valid(a))
1112 return -EINVAL;
1113
1114 *ret = TAKE_PTR(a);
1115 return 0;
1116}
1117
bb15fafe
LP
1118bool filename_is_valid(const char *p) {
1119 const char *e;
1120
1121 if (isempty(p))
1122 return false;
1123
49bfc877 1124 if (dot_or_dot_dot(p))
bb15fafe
LP
1125 return false;
1126
1127 e = strchrnul(p, '/');
1128 if (*e != 0)
1129 return false;
1130
2ef2376d 1131 if (e - p > NAME_MAX) /* NAME_MAX is counted *without* the trailing NUL byte */
bb15fafe
LP
1132 return false;
1133
1134 return true;
1135}
1136
32df2e14 1137bool path_is_valid_full(const char *p, bool accept_dot_dot) {
bb15fafe
LP
1138 if (isempty(p))
1139 return false;
1140
2ef2376d 1141 for (const char *e = p;;) {
66368835
YW
1142 int r;
1143
32df2e14 1144 r = path_find_first_component(&e, accept_dot_dot, NULL);
66368835
YW
1145 if (r < 0)
1146 return false;
49bfc877 1147
2ef2376d
LP
1148 if (e - p >= PATH_MAX) /* Already reached the maximum length for a path? (PATH_MAX is counted
1149 * *with* the trailing NUL byte) */
1150 return false;
1151 if (*e == 0) /* End of string? Yay! */
1152 return true;
2ef2376d 1153 }
656552eb
LP
1154}
1155
1156bool path_is_normalized(const char *p) {
0b869625 1157 if (!path_is_safe(p))
bb15fafe
LP
1158 return false;
1159
0b869625 1160 if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
bb15fafe
LP
1161 return false;
1162
1163 if (strstr(p, "//"))
1164 return false;
1165
1166 return true;
1167}
a0956174
LP
1168
1169char *file_in_same_dir(const char *path, const char *filename) {
1170 char *e, *ret;
1171 size_t k;
1172
1173 assert(path);
1174 assert(filename);
1175
1176 /* This removes the last component of path and appends
1177 * filename, unless the latter is absolute anyway or the
1178 * former isn't */
1179
1180 if (path_is_absolute(filename))
1181 return strdup(filename);
1182
1183 e = strrchr(path, '/');
1184 if (!e)
1185 return strdup(filename);
1186
1187 k = strlen(filename);
1188 ret = new(char, (e + 1 - path) + k + 1);
1189 if (!ret)
1190 return NULL;
1191
1192 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
1193 return ret;
1194}
1195
55cdd057
ZJS
1196bool hidden_or_backup_file(const char *filename) {
1197 const char *p;
a0956174 1198
a0956174
LP
1199 assert(filename);
1200
55cdd057
ZJS
1201 if (filename[0] == '.' ||
1202 streq(filename, "lost+found") ||
1203 streq(filename, "aquota.user") ||
1204 streq(filename, "aquota.group") ||
1205 endswith(filename, "~"))
a0956174
LP
1206 return true;
1207
55cdd057
ZJS
1208 p = strrchr(filename, '.');
1209 if (!p)
1210 return false;
1211
941060bf
LP
1212 /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
1213 * with always new suffixes and that everybody else should just adjust to that, then it really should be on
1214 * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
1215 * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
1216 * string. Specifically: there's now:
1217 *
1218 * The generic suffixes "~" and ".bak" for backup files
1219 * The generic prefix "." for hidden files
1220 *
94a0ef6e
ZJS
1221 * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
1222 * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
941060bf
LP
1223 */
1224
55cdd057
ZJS
1225 return STR_IN_SET(p + 1,
1226 "rpmnew",
1227 "rpmsave",
1228 "rpmorig",
1229 "dpkg-old",
1230 "dpkg-new",
1231 "dpkg-tmp",
1232 "dpkg-dist",
1233 "dpkg-bak",
1234 "dpkg-backup",
1235 "dpkg-remove",
1236 "ucf-new",
1237 "ucf-old",
1238 "ucf-dist",
941060bf 1239 "swp",
94a0ef6e
ZJS
1240 "bak",
1241 "old",
1242 "new");
a0956174
LP
1243}
1244
1245bool is_device_path(const char *path) {
1246
bae47ba7 1247 /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */
a0956174 1248
bae47ba7 1249 return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
3ccb8862
ZJS
1250}
1251
57e84e75
LP
1252bool valid_device_node_path(const char *path) {
1253
1254 /* Some superficial checks whether the specified path is a valid device node path, all without looking at the
1255 * actual device node. */
1256
1257 if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
1258 return false;
1259
1260 if (endswith(path, "/")) /* can't be a device node if it ends in a slash */
1261 return false;
1262
1263 return path_is_normalized(path);
1264}
1265
1266bool valid_device_allow_pattern(const char *path) {
1267 assert(path);
1268
1269 /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
1270 * accept it */
1271
49fe5c09 1272 if (STARTSWITH_SET(path, "block-", "char-"))
57e84e75
LP
1273 return true;
1274
1275 return valid_device_node_path(path);
a0956174 1276}
5a46d55f
ZJS
1277
1278int systemd_installation_has_version(const char *root, unsigned minimal_version) {
1279 const char *pattern;
1280 int r;
1281
1282 /* Try to guess if systemd installation is later than the specified version. This
1283 * is hacky and likely to yield false negatives, particularly if the installation
1284 * is non-standard. False positives should be relatively rare.
1285 */
1286
1287 NULSTR_FOREACH(pattern,
1288 /* /lib works for systems without usr-merge, and for systems with a sane
1289 * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
1290 * for Gentoo which does a merge without making /lib a symlink.
1291 */
1292 "lib/systemd/libsystemd-shared-*.so\0"
28faeda4
LP
1293 "lib64/systemd/libsystemd-shared-*.so\0"
1294 "usr/lib/systemd/libsystemd-shared-*.so\0"
1295 "usr/lib64/systemd/libsystemd-shared-*.so\0") {
5a46d55f
ZJS
1296
1297 _cleanup_strv_free_ char **names = NULL;
1298 _cleanup_free_ char *path = NULL;
1299 char *c, **name;
1300
c6134d3e 1301 path = path_join(root, pattern);
5a46d55f
ZJS
1302 if (!path)
1303 return -ENOMEM;
1304
544e146b 1305 r = glob_extend(&names, path, 0);
5a46d55f
ZJS
1306 if (r == -ENOENT)
1307 continue;
1308 if (r < 0)
1309 return r;
1310
dcd6361e 1311 assert_se(c = endswith(path, "*.so"));
5a46d55f
ZJS
1312 *c = '\0'; /* truncate the glob part */
1313
1314 STRV_FOREACH(name, names) {
1315 /* This is most likely to run only once, hence let's not optimize anything. */
1316 char *t, *t2;
1317 unsigned version;
1318
1319 t = startswith(*name, path);
1320 if (!t)
1321 continue;
1322
1323 t2 = endswith(t, ".so");
1324 if (!t2)
1325 continue;
1326
1327 t2[0] = '\0'; /* truncate the suffix */
1328
1329 r = safe_atou(t, &version);
1330 if (r < 0) {
1331 log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
1332 continue;
1333 }
1334
1335 log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
1336 *name, version,
1337 version >= minimal_version ? "OK" : "too old");
1338 if (version >= minimal_version)
1339 return true;
1340 }
1341 }
1342
1343 return false;
1344}
49bfc877
LP
1345
1346bool dot_or_dot_dot(const char *path) {
1347 if (!path)
1348 return false;
1349 if (path[0] != '.')
1350 return false;
1351 if (path[1] == 0)
1352 return true;
1353 if (path[1] != '.')
1354 return false;
1355
1356 return path[2] == 0;
1357}
57ea45e1 1358
15bac3e8 1359bool empty_or_root(const char *path) {
57ea45e1
LP
1360
1361 /* For operations relative to some root directory, returns true if the specified root directory is redundant,
1362 * i.e. either / or NULL or the empty string or any equivalent. */
1363
15bac3e8 1364 if (isempty(path))
57ea45e1
LP
1365 return true;
1366
15bac3e8 1367 return path_equal(path, "/");
57ea45e1 1368}
3841fee8
LP
1369
1370bool path_strv_contains(char **l, const char *path) {
1371 char **i;
1372
1373 STRV_FOREACH(i, l)
1374 if (path_equal(*i, path))
1375 return true;
1376
1377 return false;
1378}
de46b2be
TM
1379
1380bool prefixed_path_strv_contains(char **l, const char *path) {
1381 char **i, *j;
1382
1383 STRV_FOREACH(i, l) {
1384 j = *i;
1385 if (*j == '-')
1386 j++;
1387 if (*j == '+')
1388 j++;
1389 if (path_equal(j, path))
1390 return true;
1391 }
1392
1393 return false;
1394}