]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/path-util.c
Merge pull request #13888 from ssahani/qdisc
[thirdparty/systemd.git] / src / basic / path-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <limits.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 /* When we include libgen.h because we need dirname() we immediately
12 * undefine basename() since libgen.h defines it as a macro to the
13 * POSIX version which is really broken. We prefer GNU basename(). */
14 #include <libgen.h>
15 #undef basename
16
17 #include "alloc-util.h"
18 #include "extract-word.h"
19 #include "fs-util.h"
20 #include "glob-util.h"
21 #include "log.h"
22 #include "macro.h"
23 #include "nulstr-util.h"
24 #include "parse-util.h"
25 #include "path-util.h"
26 #include "stat-util.h"
27 #include "string-util.h"
28 #include "strv.h"
29 #include "time-util.h"
30 #include "utf8.h"
31
32 bool path_is_absolute(const char *p) {
33 return p[0] == '/';
34 }
35
36 bool is_path(const char *p) {
37 return !!strchr(p, '/');
38 }
39
40 int path_split_and_make_absolute(const char *p, char ***ret) {
41 char **l;
42 int r;
43
44 assert(p);
45 assert(ret);
46
47 l = strv_split(p, ":");
48 if (!l)
49 return -ENOMEM;
50
51 r = path_strv_make_absolute_cwd(l);
52 if (r < 0) {
53 strv_free(l);
54 return r;
55 }
56
57 *ret = l;
58 return r;
59 }
60
61 char *path_make_absolute(const char *p, const char *prefix) {
62 assert(p);
63
64 /* Makes every item in the list an absolute path by prepending
65 * the prefix, if specified and necessary */
66
67 if (path_is_absolute(p) || isempty(prefix))
68 return strdup(p);
69
70 return path_join(prefix, p);
71 }
72
73 int safe_getcwd(char **ret) {
74 char *cwd;
75
76 cwd = get_current_dir_name();
77 if (!cwd)
78 return negative_errno();
79
80 /* Let's make sure the directory is really absolute, to protect us from the logic behind
81 * CVE-2018-1000001 */
82 if (cwd[0] != '/') {
83 free(cwd);
84 return -ENOMEDIUM;
85 }
86
87 *ret = cwd;
88 return 0;
89 }
90
91 int path_make_absolute_cwd(const char *p, char **ret) {
92 char *c;
93 int r;
94
95 assert(p);
96 assert(ret);
97
98 /* Similar to path_make_absolute(), but prefixes with the
99 * current working directory. */
100
101 if (path_is_absolute(p))
102 c = strdup(p);
103 else {
104 _cleanup_free_ char *cwd = NULL;
105
106 r = safe_getcwd(&cwd);
107 if (r < 0)
108 return r;
109
110 c = path_join(cwd, p);
111 }
112 if (!c)
113 return -ENOMEM;
114
115 *ret = c;
116 return 0;
117 }
118
119 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
120 char *f, *t, *r, *p;
121 unsigned n_parents = 0;
122
123 assert(from_dir);
124 assert(to_path);
125 assert(_r);
126
127 /* Strips the common part, and adds ".." elements as necessary. */
128
129 if (!path_is_absolute(from_dir) || !path_is_absolute(to_path))
130 return -EINVAL;
131
132 f = strdupa(from_dir);
133 t = strdupa(to_path);
134
135 path_simplify(f, true);
136 path_simplify(t, true);
137
138 /* Skip the common part. */
139 for (;;) {
140 size_t a, b;
141
142 f += *f == '/';
143 t += *t == '/';
144
145 if (!*f) {
146 if (!*t)
147 /* from_dir equals to_path. */
148 r = strdup(".");
149 else
150 /* from_dir is a parent directory of to_path. */
151 r = strdup(t);
152 if (!r)
153 return -ENOMEM;
154
155 *_r = r;
156 return 0;
157 }
158
159 if (!*t)
160 break;
161
162 a = strcspn(f, "/");
163 b = strcspn(t, "/");
164
165 if (a != b || memcmp(f, t, a) != 0)
166 break;
167
168 f += a;
169 t += b;
170 }
171
172 /* If we're here, then "from_dir" has one or more elements that need to
173 * be replaced with "..". */
174
175 /* Count the number of necessary ".." elements. */
176 for (; *f;) {
177 size_t w;
178
179 w = strcspn(f, "/");
180
181 /* If this includes ".." we can't do a simple series of "..", refuse */
182 if (w == 2 && f[0] == '.' && f[1] == '.')
183 return -EINVAL;
184
185 /* Count number of elements */
186 n_parents++;
187
188 f += w;
189 f += *f == '/';
190 }
191
192 r = new(char, n_parents * 3 + strlen(t) + 1);
193 if (!r)
194 return -ENOMEM;
195
196 for (p = r; n_parents > 0; n_parents--)
197 p = mempcpy(p, "../", 3);
198
199 if (*t)
200 strcpy(p, t);
201 else
202 /* Remove trailing slash */
203 *(--p) = 0;
204
205 *_r = r;
206 return 0;
207 }
208
209 char* path_startswith_strv(const char *p, char **set) {
210 char **s, *t;
211
212 STRV_FOREACH(s, set) {
213 t = path_startswith(p, *s);
214 if (t)
215 return t;
216 }
217
218 return NULL;
219 }
220
221 int path_strv_make_absolute_cwd(char **l) {
222 char **s;
223 int r;
224
225 /* Goes through every item in the string list and makes it
226 * absolute. This works in place and won't rollback any
227 * changes on failure. */
228
229 STRV_FOREACH(s, l) {
230 char *t;
231
232 r = path_make_absolute_cwd(*s, &t);
233 if (r < 0)
234 return r;
235
236 path_simplify(t, false);
237 free_and_replace(*s, t);
238 }
239
240 return 0;
241 }
242
243 char **path_strv_resolve(char **l, const char *root) {
244 char **s;
245 unsigned k = 0;
246 bool enomem = false;
247 int r;
248
249 if (strv_isempty(l))
250 return l;
251
252 /* Goes through every item in the string list and canonicalize
253 * the path. This works in place and won't rollback any
254 * changes on failure. */
255
256 STRV_FOREACH(s, l) {
257 _cleanup_free_ char *orig = NULL;
258 char *t, *u;
259
260 if (!path_is_absolute(*s)) {
261 free(*s);
262 continue;
263 }
264
265 if (root) {
266 orig = *s;
267 t = path_join(root, orig);
268 if (!t) {
269 enomem = true;
270 continue;
271 }
272 } else
273 t = *s;
274
275 r = chase_symlinks(t, root, 0, &u, NULL);
276 if (r == -ENOENT) {
277 if (root) {
278 u = TAKE_PTR(orig);
279 free(t);
280 } else
281 u = t;
282 } else if (r < 0) {
283 free(t);
284
285 if (r == -ENOMEM)
286 enomem = true;
287
288 continue;
289 } else if (root) {
290 char *x;
291
292 free(t);
293 x = path_startswith(u, root);
294 if (x) {
295 /* restore the slash if it was lost */
296 if (!startswith(x, "/"))
297 *(--x) = '/';
298
299 t = strdup(x);
300 free(u);
301 if (!t) {
302 enomem = true;
303 continue;
304 }
305 u = t;
306 } else {
307 /* canonicalized path goes outside of
308 * prefix, keep the original path instead */
309 free_and_replace(u, orig);
310 }
311 } else
312 free(t);
313
314 l[k++] = u;
315 }
316
317 l[k] = NULL;
318
319 if (enomem)
320 return NULL;
321
322 return l;
323 }
324
325 char **path_strv_resolve_uniq(char **l, const char *root) {
326
327 if (strv_isempty(l))
328 return l;
329
330 if (!path_strv_resolve(l, root))
331 return NULL;
332
333 return strv_uniq(l);
334 }
335
336 char *path_simplify(char *path, bool kill_dots) {
337 char *f, *t;
338 bool slash = false, ignore_slash = false, absolute;
339
340 assert(path);
341
342 /* Removes redundant inner and trailing slashes. Also removes unnecessary dots
343 * if kill_dots is true. Modifies the passed string in-place.
344 *
345 * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false)
346 * ///foo//./bar/. becomes /foo/bar (if kill_dots is true)
347 * .//./foo//./bar/. becomes ././foo/./bar/. (if kill_dots is false)
348 * .//./foo//./bar/. becomes foo/bar (if kill_dots is true)
349 */
350
351 if (isempty(path))
352 return path;
353
354 absolute = path_is_absolute(path);
355
356 f = path;
357 if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) {
358 ignore_slash = true;
359 f++;
360 }
361
362 for (t = path; *f; f++) {
363
364 if (*f == '/') {
365 slash = true;
366 continue;
367 }
368
369 if (slash) {
370 if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/'))
371 continue;
372
373 slash = false;
374 if (ignore_slash)
375 ignore_slash = false;
376 else
377 *(t++) = '/';
378 }
379
380 *(t++) = *f;
381 }
382
383 /* Special rule, if we stripped everything, we either need a "/" (for the root directory)
384 * or "." for the current directory */
385 if (t == path) {
386 if (absolute)
387 *(t++) = '/';
388 else
389 *(t++) = '.';
390 }
391
392 *t = 0;
393 return path;
394 }
395
396 int path_simplify_and_warn(
397 char *path,
398 unsigned flag,
399 const char *unit,
400 const char *filename,
401 unsigned line,
402 const char *lvalue) {
403
404 bool fatal = flag & PATH_CHECK_FATAL;
405
406 assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
407
408 if (!utf8_is_valid(path))
409 return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
410
411 if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
412 bool absolute;
413
414 absolute = path_is_absolute(path);
415
416 if (!absolute && (flag & PATH_CHECK_ABSOLUTE))
417 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
418 "%s= path is not absolute%s: %s",
419 lvalue, fatal ? "" : ", ignoring", path);
420
421 if (absolute && (flag & PATH_CHECK_RELATIVE))
422 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
423 "%s= path is absolute%s: %s",
424 lvalue, fatal ? "" : ", ignoring", path);
425 }
426
427 path_simplify(path, true);
428
429 if (!path_is_valid(path))
430 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
431 "%s= path has invalid length (%zu bytes)%s.",
432 lvalue, strlen(path), fatal ? "" : ", ignoring");
433
434 if (!path_is_normalized(path))
435 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
436 "%s= path is not normalized%s: %s",
437 lvalue, fatal ? "" : ", ignoring", path);
438
439 return 0;
440 }
441
442 char* path_startswith(const char *path, const char *prefix) {
443 assert(path);
444 assert(prefix);
445
446 /* Returns a pointer to the start of the first component after the parts matched by
447 * the prefix, iff
448 * - both paths are absolute or both paths are relative,
449 * and
450 * - each component in prefix in turn matches a component in path at the same position.
451 * An empty string will be returned when the prefix and path are equivalent.
452 *
453 * Returns NULL otherwise.
454 */
455
456 if ((path[0] == '/') != (prefix[0] == '/'))
457 return NULL;
458
459 for (;;) {
460 size_t a, b;
461
462 path += strspn(path, "/");
463 prefix += strspn(prefix, "/");
464
465 if (*prefix == 0)
466 return (char*) path;
467
468 if (*path == 0)
469 return NULL;
470
471 a = strcspn(path, "/");
472 b = strcspn(prefix, "/");
473
474 if (a != b)
475 return NULL;
476
477 if (memcmp(path, prefix, a) != 0)
478 return NULL;
479
480 path += a;
481 prefix += b;
482 }
483 }
484
485 int path_compare(const char *a, const char *b) {
486 int d;
487
488 assert(a);
489 assert(b);
490
491 /* A relative path and an absolute path must not compare as equal.
492 * Which one is sorted before the other does not really matter.
493 * Here a relative path is ordered before an absolute path. */
494 d = (a[0] == '/') - (b[0] == '/');
495 if (d != 0)
496 return d;
497
498 for (;;) {
499 size_t j, k;
500
501 a += strspn(a, "/");
502 b += strspn(b, "/");
503
504 if (*a == 0 && *b == 0)
505 return 0;
506
507 /* Order prefixes first: "/foo" before "/foo/bar" */
508 if (*a == 0)
509 return -1;
510 if (*b == 0)
511 return 1;
512
513 j = strcspn(a, "/");
514 k = strcspn(b, "/");
515
516 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
517 d = memcmp(a, b, MIN(j, k));
518 if (d != 0)
519 return (d > 0) - (d < 0); /* sign of d */
520
521 /* Sort "/foo/a" before "/foo/aaa" */
522 d = (j > k) - (j < k); /* sign of (j - k) */
523 if (d != 0)
524 return d;
525
526 a += j;
527 b += k;
528 }
529 }
530
531 bool path_equal(const char *a, const char *b) {
532 return path_compare(a, b) == 0;
533 }
534
535 bool path_equal_or_files_same(const char *a, const char *b, int flags) {
536 return path_equal(a, b) || files_same(a, b, flags) > 0;
537 }
538
539 char* path_join_internal(const char *first, ...) {
540 char *joined, *q;
541 const char *p;
542 va_list ap;
543 bool slash;
544 size_t sz;
545
546 /* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
547 * already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
548 * removed. The string returned is hence always equal to or longer than the sum of the lengths of each
549 * individual string.
550 *
551 * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
552 * are optional.
553 *
554 * Examples:
555 *
556 * path_join("foo", "bar") → "foo/bar"
557 * path_join("foo/", "bar") → "foo/bar"
558 * path_join("", "foo", "", "bar", "") → "foo/bar" */
559
560 sz = strlen_ptr(first);
561 va_start(ap, first);
562 while ((p = va_arg(ap, char*)) != (const char*) -1)
563 if (!isempty(p))
564 sz += 1 + strlen(p);
565 va_end(ap);
566
567 joined = new(char, sz + 1);
568 if (!joined)
569 return NULL;
570
571 if (!isempty(first)) {
572 q = stpcpy(joined, first);
573 slash = endswith(first, "/");
574 } else {
575 /* Skip empty items */
576 joined[0] = 0;
577 q = joined;
578 slash = true; /* no need to generate a slash anymore */
579 }
580
581 va_start(ap, first);
582 while ((p = va_arg(ap, char*)) != (const char*) -1) {
583 if (isempty(p))
584 continue;
585
586 if (!slash && p[0] != '/')
587 *(q++) = '/';
588
589 q = stpcpy(q, p);
590 slash = endswith(p, "/");
591 }
592 va_end(ap);
593
594 return joined;
595 }
596
597 int find_binary(const char *name, char **ret) {
598 int last_error, r;
599 const char *p;
600
601 assert(name);
602
603 if (is_path(name)) {
604 if (access(name, X_OK) < 0)
605 return -errno;
606
607 if (ret) {
608 r = path_make_absolute_cwd(name, ret);
609 if (r < 0)
610 return r;
611 }
612
613 return 0;
614 }
615
616 /**
617 * Plain getenv, not secure_getenv, because we want
618 * to actually allow the user to pick the binary.
619 */
620 p = getenv("PATH");
621 if (!p)
622 p = DEFAULT_PATH;
623
624 last_error = -ENOENT;
625
626 for (;;) {
627 _cleanup_free_ char *j = NULL, *element = NULL;
628
629 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
630 if (r < 0)
631 return r;
632 if (r == 0)
633 break;
634
635 if (!path_is_absolute(element))
636 continue;
637
638 j = path_join(element, name);
639 if (!j)
640 return -ENOMEM;
641
642 if (access(j, X_OK) >= 0) {
643 /* Found it! */
644
645 if (ret) {
646 *ret = path_simplify(j, false);
647 j = NULL;
648 }
649
650 return 0;
651 }
652
653 /* PATH entries which we don't have access to are ignored, as per tradition. */
654 if (errno != EACCES)
655 last_error = -errno;
656 }
657
658 return last_error;
659 }
660
661 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
662 bool changed = false;
663 const char* const* i;
664
665 assert(timestamp);
666
667 if (!paths)
668 return false;
669
670 STRV_FOREACH(i, paths) {
671 struct stat stats;
672 usec_t u;
673
674 if (stat(*i, &stats) < 0)
675 continue;
676
677 u = timespec_load(&stats.st_mtim);
678
679 /* first check */
680 if (*timestamp >= u)
681 continue;
682
683 log_debug("timestamp of '%s' changed", *i);
684
685 /* update timestamp */
686 if (update) {
687 *timestamp = u;
688 changed = true;
689 } else
690 return true;
691 }
692
693 return changed;
694 }
695
696 static int binary_is_good(const char *binary) {
697 _cleanup_free_ char *p = NULL, *d = NULL;
698 int r;
699
700 r = find_binary(binary, &p);
701 if (r == -ENOENT)
702 return 0;
703 if (r < 0)
704 return r;
705
706 /* An fsck that is linked to /bin/true is a non-existent
707 * fsck */
708
709 r = readlink_malloc(p, &d);
710 if (r == -EINVAL) /* not a symlink */
711 return 1;
712 if (r < 0)
713 return r;
714
715 return !PATH_IN_SET(d, "true"
716 "/bin/true",
717 "/usr/bin/true",
718 "/dev/null");
719 }
720
721 int fsck_exists(const char *fstype) {
722 const char *checker;
723
724 assert(fstype);
725
726 if (streq(fstype, "auto"))
727 return -EINVAL;
728
729 checker = strjoina("fsck.", fstype);
730 return binary_is_good(checker);
731 }
732
733 int mkfs_exists(const char *fstype) {
734 const char *mkfs;
735
736 assert(fstype);
737
738 if (streq(fstype, "auto"))
739 return -EINVAL;
740
741 mkfs = strjoina("mkfs.", fstype);
742 return binary_is_good(mkfs);
743 }
744
745 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
746 char *p;
747 int r;
748
749 /*
750 * This function is intended to be used in command line
751 * parsers, to handle paths that are passed in. It makes the
752 * path absolute, and reduces it to NULL if omitted or
753 * root (the latter optionally).
754 *
755 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
756 * SUCCESS! Hence, do not pass in uninitialized pointers.
757 */
758
759 if (isempty(path)) {
760 *arg = mfree(*arg);
761 return 0;
762 }
763
764 r = path_make_absolute_cwd(path, &p);
765 if (r < 0)
766 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
767
768 path_simplify(p, false);
769 if (suppress_root && empty_or_root(p))
770 p = mfree(p);
771
772 free_and_replace(*arg, p);
773
774 return 0;
775 }
776
777 char* dirname_malloc(const char *path) {
778 char *d, *dir, *dir2;
779
780 assert(path);
781
782 d = strdup(path);
783 if (!d)
784 return NULL;
785
786 dir = dirname(d);
787 assert(dir);
788
789 if (dir == d)
790 return d;
791
792 dir2 = strdup(dir);
793 free(d);
794
795 return dir2;
796 }
797
798 const char *last_path_component(const char *path) {
799
800 /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
801 *
802 * a/b/c → c
803 * a/b/c/ → c/
804 * x → x
805 * x/ → x/
806 * /y → y
807 * /y/ → y/
808 * / → /
809 * // → /
810 * /foo/a → a
811 * /foo/a/ → a/
812 *
813 * Also, the empty string is mapped to itself.
814 *
815 * This is different than basename(), which returns "" when a trailing slash is present.
816 */
817
818 unsigned l, k;
819
820 if (!path)
821 return NULL;
822
823 l = k = strlen(path);
824 if (l == 0) /* special case — an empty string */
825 return path;
826
827 while (k > 0 && path[k-1] == '/')
828 k--;
829
830 if (k == 0) /* the root directory */
831 return path + l - 1;
832
833 while (k > 0 && path[k-1] != '/')
834 k--;
835
836 return path + k;
837 }
838
839 int path_extract_filename(const char *p, char **ret) {
840 _cleanup_free_ char *a = NULL;
841 const char *c, *e = NULL, *q;
842
843 /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
844 * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
845
846 if (!p)
847 return -EINVAL;
848
849 c = last_path_component(p);
850
851 for (q = c; *q != 0; q++)
852 if (*q != '/')
853 e = q + 1;
854
855 if (!e) /* no valid character? */
856 return -EINVAL;
857
858 a = strndup(c, e - c);
859 if (!a)
860 return -ENOMEM;
861
862 if (!filename_is_valid(a))
863 return -EINVAL;
864
865 *ret = TAKE_PTR(a);
866
867 return 0;
868 }
869
870 bool filename_is_valid(const char *p) {
871 const char *e;
872
873 if (isempty(p))
874 return false;
875
876 if (dot_or_dot_dot(p))
877 return false;
878
879 e = strchrnul(p, '/');
880 if (*e != 0)
881 return false;
882
883 if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */
884 return false;
885
886 return true;
887 }
888
889 bool path_is_valid(const char *p) {
890
891 if (isempty(p))
892 return false;
893
894 if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */
895 return false;
896
897 return true;
898 }
899
900 bool path_is_normalized(const char *p) {
901
902 if (!path_is_valid(p))
903 return false;
904
905 if (dot_or_dot_dot(p))
906 return false;
907
908 if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
909 return false;
910
911 if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
912 return false;
913
914 if (strstr(p, "//"))
915 return false;
916
917 return true;
918 }
919
920 char *file_in_same_dir(const char *path, const char *filename) {
921 char *e, *ret;
922 size_t k;
923
924 assert(path);
925 assert(filename);
926
927 /* This removes the last component of path and appends
928 * filename, unless the latter is absolute anyway or the
929 * former isn't */
930
931 if (path_is_absolute(filename))
932 return strdup(filename);
933
934 e = strrchr(path, '/');
935 if (!e)
936 return strdup(filename);
937
938 k = strlen(filename);
939 ret = new(char, (e + 1 - path) + k + 1);
940 if (!ret)
941 return NULL;
942
943 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
944 return ret;
945 }
946
947 bool hidden_or_backup_file(const char *filename) {
948 const char *p;
949
950 assert(filename);
951
952 if (filename[0] == '.' ||
953 streq(filename, "lost+found") ||
954 streq(filename, "aquota.user") ||
955 streq(filename, "aquota.group") ||
956 endswith(filename, "~"))
957 return true;
958
959 p = strrchr(filename, '.');
960 if (!p)
961 return false;
962
963 /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
964 * with always new suffixes and that everybody else should just adjust to that, then it really should be on
965 * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
966 * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
967 * string. Specifically: there's now:
968 *
969 * The generic suffixes "~" and ".bak" for backup files
970 * The generic prefix "." for hidden files
971 *
972 * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
973 * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
974 */
975
976 return STR_IN_SET(p + 1,
977 "rpmnew",
978 "rpmsave",
979 "rpmorig",
980 "dpkg-old",
981 "dpkg-new",
982 "dpkg-tmp",
983 "dpkg-dist",
984 "dpkg-bak",
985 "dpkg-backup",
986 "dpkg-remove",
987 "ucf-new",
988 "ucf-old",
989 "ucf-dist",
990 "swp",
991 "bak",
992 "old",
993 "new");
994 }
995
996 bool is_device_path(const char *path) {
997
998 /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */
999
1000 return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
1001 }
1002
1003 bool valid_device_node_path(const char *path) {
1004
1005 /* Some superficial checks whether the specified path is a valid device node path, all without looking at the
1006 * actual device node. */
1007
1008 if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
1009 return false;
1010
1011 if (endswith(path, "/")) /* can't be a device node if it ends in a slash */
1012 return false;
1013
1014 return path_is_normalized(path);
1015 }
1016
1017 bool valid_device_allow_pattern(const char *path) {
1018 assert(path);
1019
1020 /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
1021 * accept it */
1022
1023 if (STARTSWITH_SET(path, "block-", "char-"))
1024 return true;
1025
1026 return valid_device_node_path(path);
1027 }
1028
1029 int systemd_installation_has_version(const char *root, unsigned minimal_version) {
1030 const char *pattern;
1031 int r;
1032
1033 /* Try to guess if systemd installation is later than the specified version. This
1034 * is hacky and likely to yield false negatives, particularly if the installation
1035 * is non-standard. False positives should be relatively rare.
1036 */
1037
1038 NULSTR_FOREACH(pattern,
1039 /* /lib works for systems without usr-merge, and for systems with a sane
1040 * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
1041 * for Gentoo which does a merge without making /lib a symlink.
1042 */
1043 "lib/systemd/libsystemd-shared-*.so\0"
1044 "lib64/systemd/libsystemd-shared-*.so\0"
1045 "usr/lib/systemd/libsystemd-shared-*.so\0"
1046 "usr/lib64/systemd/libsystemd-shared-*.so\0") {
1047
1048 _cleanup_strv_free_ char **names = NULL;
1049 _cleanup_free_ char *path = NULL;
1050 char *c, **name;
1051
1052 path = path_join(root, pattern);
1053 if (!path)
1054 return -ENOMEM;
1055
1056 r = glob_extend(&names, path);
1057 if (r == -ENOENT)
1058 continue;
1059 if (r < 0)
1060 return r;
1061
1062 assert_se(c = endswith(path, "*.so"));
1063 *c = '\0'; /* truncate the glob part */
1064
1065 STRV_FOREACH(name, names) {
1066 /* This is most likely to run only once, hence let's not optimize anything. */
1067 char *t, *t2;
1068 unsigned version;
1069
1070 t = startswith(*name, path);
1071 if (!t)
1072 continue;
1073
1074 t2 = endswith(t, ".so");
1075 if (!t2)
1076 continue;
1077
1078 t2[0] = '\0'; /* truncate the suffix */
1079
1080 r = safe_atou(t, &version);
1081 if (r < 0) {
1082 log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
1083 continue;
1084 }
1085
1086 log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
1087 *name, version,
1088 version >= minimal_version ? "OK" : "too old");
1089 if (version >= minimal_version)
1090 return true;
1091 }
1092 }
1093
1094 return false;
1095 }
1096
1097 bool dot_or_dot_dot(const char *path) {
1098 if (!path)
1099 return false;
1100 if (path[0] != '.')
1101 return false;
1102 if (path[1] == 0)
1103 return true;
1104 if (path[1] != '.')
1105 return false;
1106
1107 return path[2] == 0;
1108 }
1109
1110 bool empty_or_root(const char *root) {
1111
1112 /* For operations relative to some root directory, returns true if the specified root directory is redundant,
1113 * i.e. either / or NULL or the empty string or any equivalent. */
1114
1115 if (!root)
1116 return true;
1117
1118 return root[strspn(root, "/")] == 0;
1119 }