]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/path-util.c
path-util: don't add extra "/" when prefix already is suffixed by slash
[thirdparty/systemd.git] / src / basic / path-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010-2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 /* When we include libgen.h because we need dirname() we immediately
30 * undefine basename() since libgen.h defines it as a macro to the
31 * POSIX version which is really broken. We prefer GNU basename(). */
32 #include <libgen.h>
33 #undef basename
34
35 #include "alloc-util.h"
36 #include "extract-word.h"
37 #include "fs-util.h"
38 #include "glob-util.h"
39 #include "log.h"
40 #include "macro.h"
41 #include "missing.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "stat-util.h"
45 #include "string-util.h"
46 #include "strv.h"
47 #include "time-util.h"
48
49 bool path_is_absolute(const char *p) {
50 return p[0] == '/';
51 }
52
53 bool is_path(const char *p) {
54 return !!strchr(p, '/');
55 }
56
57 int path_split_and_make_absolute(const char *p, char ***ret) {
58 char **l;
59 int r;
60
61 assert(p);
62 assert(ret);
63
64 l = strv_split(p, ":");
65 if (!l)
66 return -ENOMEM;
67
68 r = path_strv_make_absolute_cwd(l);
69 if (r < 0) {
70 strv_free(l);
71 return r;
72 }
73
74 *ret = l;
75 return r;
76 }
77
78 char *path_make_absolute(const char *p, const char *prefix) {
79 assert(p);
80
81 /* Makes every item in the list an absolute path by prepending
82 * the prefix, if specified and necessary */
83
84 if (path_is_absolute(p) || isempty(prefix))
85 return strdup(p);
86
87 if (endswith(prefix, "/"))
88 return strjoin(prefix, p);
89 else
90 return strjoin(prefix, "/", p);
91 }
92
93 int path_make_absolute_cwd(const char *p, char **ret) {
94 char *c;
95
96 assert(p);
97 assert(ret);
98
99 /* Similar to path_make_absolute(), but prefixes with the
100 * current working directory. */
101
102 if (path_is_absolute(p))
103 c = strdup(p);
104 else {
105 _cleanup_free_ char *cwd = NULL;
106
107 cwd = get_current_dir_name();
108 if (!cwd)
109 return negative_errno();
110
111 c = strjoin(cwd, "/", p);
112 }
113 if (!c)
114 return -ENOMEM;
115
116 *ret = c;
117 return 0;
118 }
119
120 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
121 char *r, *p;
122 unsigned n_parents;
123
124 assert(from_dir);
125 assert(to_path);
126 assert(_r);
127
128 /* Strips the common part, and adds ".." elements as necessary. */
129
130 if (!path_is_absolute(from_dir))
131 return -EINVAL;
132
133 if (!path_is_absolute(to_path))
134 return -EINVAL;
135
136 /* Skip the common part. */
137 for (;;) {
138 size_t a, b;
139
140 from_dir += strspn(from_dir, "/");
141 to_path += strspn(to_path, "/");
142
143 if (!*from_dir) {
144 if (!*to_path)
145 /* from_dir equals to_path. */
146 r = strdup(".");
147 else
148 /* from_dir is a parent directory of to_path. */
149 r = strdup(to_path);
150 if (!r)
151 return -ENOMEM;
152
153 path_kill_slashes(r);
154
155 *_r = r;
156 return 0;
157 }
158
159 if (!*to_path)
160 break;
161
162 a = strcspn(from_dir, "/");
163 b = strcspn(to_path, "/");
164
165 if (a != b)
166 break;
167
168 if (memcmp(from_dir, to_path, a) != 0)
169 break;
170
171 from_dir += a;
172 to_path += b;
173 }
174
175 /* If we're here, then "from_dir" has one or more elements that need to
176 * be replaced with "..". */
177
178 /* Count the number of necessary ".." elements. */
179 for (n_parents = 0;;) {
180 size_t w;
181
182 from_dir += strspn(from_dir, "/");
183
184 if (!*from_dir)
185 break;
186
187 w = strcspn(from_dir, "/");
188
189 /* If this includes ".." we can't do a simple series of "..", refuse */
190 if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.')
191 return -EINVAL;
192
193 /* Count number of elements, except if they are "." */
194 if (w != 1 || from_dir[0] != '.')
195 n_parents++;
196
197 from_dir += w;
198 }
199
200 r = new(char, n_parents * 3 + strlen(to_path) + 1);
201 if (!r)
202 return -ENOMEM;
203
204 for (p = r; n_parents > 0; n_parents--)
205 p = mempcpy(p, "../", 3);
206
207 strcpy(p, to_path);
208 path_kill_slashes(r);
209
210 *_r = r;
211 return 0;
212 }
213
214 int path_strv_make_absolute_cwd(char **l) {
215 char **s;
216 int r;
217
218 /* Goes through every item in the string list and makes it
219 * absolute. This works in place and won't rollback any
220 * changes on failure. */
221
222 STRV_FOREACH(s, l) {
223 char *t;
224
225 r = path_make_absolute_cwd(*s, &t);
226 if (r < 0)
227 return r;
228
229 path_kill_slashes(t);
230 free_and_replace(*s, t);
231 }
232
233 return 0;
234 }
235
236 char **path_strv_resolve(char **l, const char *root) {
237 char **s;
238 unsigned k = 0;
239 bool enomem = false;
240 int r;
241
242 if (strv_isempty(l))
243 return l;
244
245 /* Goes through every item in the string list and canonicalize
246 * the path. This works in place and won't rollback any
247 * changes on failure. */
248
249 STRV_FOREACH(s, l) {
250 _cleanup_free_ char *orig = NULL;
251 char *t, *u;
252
253 if (!path_is_absolute(*s)) {
254 free(*s);
255 continue;
256 }
257
258 if (root) {
259 orig = *s;
260 t = prefix_root(root, orig);
261 if (!t) {
262 enomem = true;
263 continue;
264 }
265 } else
266 t = *s;
267
268 r = chase_symlinks(t, root, 0, &u);
269 if (r == -ENOENT) {
270 if (root) {
271 u = orig;
272 orig = NULL;
273 free(t);
274 } else
275 u = t;
276 } else if (r < 0) {
277 free(t);
278
279 if (r == -ENOMEM)
280 enomem = true;
281
282 continue;
283 } else if (root) {
284 char *x;
285
286 free(t);
287 x = path_startswith(u, root);
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 */
303 free_and_replace(u, orig);
304 }
305 } else
306 free(t);
307
308 l[k++] = u;
309 }
310
311 l[k] = NULL;
312
313 if (enomem)
314 return NULL;
315
316 return l;
317 }
318
319 char **path_strv_resolve_uniq(char **l, const char *root) {
320
321 if (strv_isempty(l))
322 return l;
323
324 if (!path_strv_resolve(l, root))
325 return NULL;
326
327 return strv_uniq(l);
328 }
329
330 char *path_kill_slashes(char *path) {
331 char *f, *t;
332 bool slash = false;
333
334 /* Removes redundant inner and trailing slashes. Modifies the
335 * passed string in-place.
336 *
337 * ///foo///bar/ becomes /foo/bar
338 */
339
340 for (f = path, t = path; *f; f++) {
341
342 if (*f == '/') {
343 slash = true;
344 continue;
345 }
346
347 if (slash) {
348 slash = false;
349 *(t++) = '/';
350 }
351
352 *(t++) = *f;
353 }
354
355 /* Special rule, if we are talking of the root directory, a
356 trailing slash is good */
357
358 if (t == path && slash)
359 *(t++) = '/';
360
361 *t = 0;
362 return path;
363 }
364
365 char* path_startswith(const char *path, const char *prefix) {
366 assert(path);
367 assert(prefix);
368
369 /* Returns a pointer to the start of the first component after the parts matched by
370 * the prefix, iff
371 * - both paths are absolute or both paths are relative,
372 * and
373 * - each component in prefix in turn matches a component in path at the same position.
374 * An empty string will be returned when the prefix and path are equivalent.
375 *
376 * Returns NULL otherwise.
377 */
378
379 if ((path[0] == '/') != (prefix[0] == '/'))
380 return NULL;
381
382 for (;;) {
383 size_t a, b;
384
385 path += strspn(path, "/");
386 prefix += strspn(prefix, "/");
387
388 if (*prefix == 0)
389 return (char*) path;
390
391 if (*path == 0)
392 return NULL;
393
394 a = strcspn(path, "/");
395 b = strcspn(prefix, "/");
396
397 if (a != b)
398 return NULL;
399
400 if (memcmp(path, prefix, a) != 0)
401 return NULL;
402
403 path += a;
404 prefix += b;
405 }
406 }
407
408 int path_compare(const char *a, const char *b) {
409 int d;
410
411 assert(a);
412 assert(b);
413
414 /* A relative path and an abolute path must not compare as equal.
415 * Which one is sorted before the other does not really matter.
416 * Here a relative path is ordered before an absolute path. */
417 d = (a[0] == '/') - (b[0] == '/');
418 if (d != 0)
419 return d;
420
421 for (;;) {
422 size_t j, k;
423
424 a += strspn(a, "/");
425 b += strspn(b, "/");
426
427 if (*a == 0 && *b == 0)
428 return 0;
429
430 /* Order prefixes first: "/foo" before "/foo/bar" */
431 if (*a == 0)
432 return -1;
433 if (*b == 0)
434 return 1;
435
436 j = strcspn(a, "/");
437 k = strcspn(b, "/");
438
439 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
440 d = memcmp(a, b, MIN(j, k));
441 if (d != 0)
442 return (d > 0) - (d < 0); /* sign of d */
443
444 /* Sort "/foo/a" before "/foo/aaa" */
445 d = (j > k) - (j < k); /* sign of (j - k) */
446 if (d != 0)
447 return d;
448
449 a += j;
450 b += k;
451 }
452 }
453
454 bool path_equal(const char *a, const char *b) {
455 return path_compare(a, b) == 0;
456 }
457
458 bool path_equal_or_files_same(const char *a, const char *b, int flags) {
459 return path_equal(a, b) || files_same(a, b, flags) > 0;
460 }
461
462 char* path_join(const char *root, const char *path, const char *rest) {
463 assert(path);
464
465 if (!isempty(root))
466 return strjoin(root, endswith(root, "/") ? "" : "/",
467 path[0] == '/' ? path+1 : path,
468 rest ? (endswith(path, "/") ? "" : "/") : NULL,
469 rest && rest[0] == '/' ? rest+1 : rest);
470 else
471 return strjoin(path,
472 rest ? (endswith(path, "/") ? "" : "/") : NULL,
473 rest && rest[0] == '/' ? rest+1 : rest);
474 }
475
476 int find_binary(const char *name, char **ret) {
477 int last_error, r;
478 const char *p;
479
480 assert(name);
481
482 if (is_path(name)) {
483 if (access(name, X_OK) < 0)
484 return -errno;
485
486 if (ret) {
487 r = path_make_absolute_cwd(name, ret);
488 if (r < 0)
489 return r;
490 }
491
492 return 0;
493 }
494
495 /**
496 * Plain getenv, not secure_getenv, because we want
497 * to actually allow the user to pick the binary.
498 */
499 p = getenv("PATH");
500 if (!p)
501 p = DEFAULT_PATH;
502
503 last_error = -ENOENT;
504
505 for (;;) {
506 _cleanup_free_ char *j = NULL, *element = NULL;
507
508 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
509 if (r < 0)
510 return r;
511 if (r == 0)
512 break;
513
514 if (!path_is_absolute(element))
515 continue;
516
517 j = strjoin(element, "/", name);
518 if (!j)
519 return -ENOMEM;
520
521 if (access(j, X_OK) >= 0) {
522 /* Found it! */
523
524 if (ret) {
525 *ret = path_kill_slashes(j);
526 j = NULL;
527 }
528
529 return 0;
530 }
531
532 last_error = -errno;
533 }
534
535 return last_error;
536 }
537
538 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
539 bool changed = false;
540 const char* const* i;
541
542 assert(timestamp);
543
544 if (!paths)
545 return false;
546
547 STRV_FOREACH(i, paths) {
548 struct stat stats;
549 usec_t u;
550
551 if (stat(*i, &stats) < 0)
552 continue;
553
554 u = timespec_load(&stats.st_mtim);
555
556 /* first check */
557 if (*timestamp >= u)
558 continue;
559
560 log_debug("timestamp of '%s' changed", *i);
561
562 /* update timestamp */
563 if (update) {
564 *timestamp = u;
565 changed = true;
566 } else
567 return true;
568 }
569
570 return changed;
571 }
572
573 static int binary_is_good(const char *binary) {
574 _cleanup_free_ char *p = NULL, *d = NULL;
575 int r;
576
577 r = find_binary(binary, &p);
578 if (r == -ENOENT)
579 return 0;
580 if (r < 0)
581 return r;
582
583 /* An fsck that is linked to /bin/true is a non-existent
584 * fsck */
585
586 r = readlink_malloc(p, &d);
587 if (r == -EINVAL) /* not a symlink */
588 return 1;
589 if (r < 0)
590 return r;
591
592 return !PATH_IN_SET(d, "true"
593 "/bin/true",
594 "/usr/bin/true",
595 "/dev/null");
596 }
597
598 int fsck_exists(const char *fstype) {
599 const char *checker;
600
601 assert(fstype);
602
603 if (streq(fstype, "auto"))
604 return -EINVAL;
605
606 checker = strjoina("fsck.", fstype);
607 return binary_is_good(checker);
608 }
609
610 int mkfs_exists(const char *fstype) {
611 const char *mkfs;
612
613 assert(fstype);
614
615 if (streq(fstype, "auto"))
616 return -EINVAL;
617
618 mkfs = strjoina("mkfs.", fstype);
619 return binary_is_good(mkfs);
620 }
621
622 char *prefix_root(const char *root, const char *path) {
623 char *n, *p;
624 size_t l;
625
626 /* If root is passed, prefixes path with it. Otherwise returns
627 * it as is. */
628
629 assert(path);
630
631 /* First, drop duplicate prefixing slashes from the path */
632 while (path[0] == '/' && path[1] == '/')
633 path++;
634
635 if (isempty(root) || path_equal(root, "/"))
636 return strdup(path);
637
638 l = strlen(root) + 1 + strlen(path) + 1;
639
640 n = new(char, l);
641 if (!n)
642 return NULL;
643
644 p = stpcpy(n, root);
645
646 while (p > n && p[-1] == '/')
647 p--;
648
649 if (path[0] != '/')
650 *(p++) = '/';
651
652 strcpy(p, path);
653 return n;
654 }
655
656 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
657 char *p;
658 int r;
659
660 /*
661 * This function is intended to be used in command line
662 * parsers, to handle paths that are passed in. It makes the
663 * path absolute, and reduces it to NULL if omitted or
664 * root (the latter optionally).
665 *
666 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
667 * SUCCESS! Hence, do not pass in uninitialized pointers.
668 */
669
670 if (isempty(path)) {
671 *arg = mfree(*arg);
672 return 0;
673 }
674
675 r = path_make_absolute_cwd(path, &p);
676 if (r < 0)
677 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
678
679 path_kill_slashes(p);
680 if (suppress_root && path_equal(p, "/"))
681 p = mfree(p);
682
683 free(*arg);
684 *arg = p;
685 return 0;
686 }
687
688 char* dirname_malloc(const char *path) {
689 char *d, *dir, *dir2;
690
691 assert(path);
692
693 d = strdup(path);
694 if (!d)
695 return NULL;
696
697 dir = dirname(d);
698 assert(dir);
699
700 if (dir == d)
701 return d;
702
703 dir2 = strdup(dir);
704 free(d);
705
706 return dir2;
707 }
708
709 const char *last_path_component(const char *path) {
710 /* Finds the last component of the path, preserving the
711 * optional trailing slash that signifies a directory.
712 * a/b/c → c
713 * a/b/c/ → c/
714 * / → /
715 * // → /
716 * /foo/a → a
717 * /foo/a/ → a/
718 * This is different than basename, which returns "" when
719 * a trailing slash is present.
720 */
721
722 unsigned l, k;
723
724 l = k = strlen(path);
725 if (l == 0) /* special case — an empty string */
726 return path;
727
728 while (k > 0 && path[k-1] == '/')
729 k--;
730
731 if (k == 0) /* the root directory */
732 return path + l - 1;
733
734 while (k > 0 && path[k-1] != '/')
735 k--;
736
737 return path + k;
738 }
739
740 bool filename_is_valid(const char *p) {
741 const char *e;
742
743 if (isempty(p))
744 return false;
745
746 if (dot_or_dot_dot(p))
747 return false;
748
749 e = strchrnul(p, '/');
750 if (*e != 0)
751 return false;
752
753 if (e - p > FILENAME_MAX)
754 return false;
755
756 return true;
757 }
758
759 bool path_is_normalized(const char *p) {
760
761 if (isempty(p))
762 return false;
763
764 if (dot_or_dot_dot(p))
765 return false;
766
767 if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
768 return false;
769
770 if (strlen(p)+1 > PATH_MAX)
771 return false;
772
773 if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
774 return false;
775
776 if (strstr(p, "//"))
777 return false;
778
779 return true;
780 }
781
782 char *file_in_same_dir(const char *path, const char *filename) {
783 char *e, *ret;
784 size_t k;
785
786 assert(path);
787 assert(filename);
788
789 /* This removes the last component of path and appends
790 * filename, unless the latter is absolute anyway or the
791 * former isn't */
792
793 if (path_is_absolute(filename))
794 return strdup(filename);
795
796 e = strrchr(path, '/');
797 if (!e)
798 return strdup(filename);
799
800 k = strlen(filename);
801 ret = new(char, (e + 1 - path) + k + 1);
802 if (!ret)
803 return NULL;
804
805 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
806 return ret;
807 }
808
809 bool hidden_or_backup_file(const char *filename) {
810 const char *p;
811
812 assert(filename);
813
814 if (filename[0] == '.' ||
815 streq(filename, "lost+found") ||
816 streq(filename, "aquota.user") ||
817 streq(filename, "aquota.group") ||
818 endswith(filename, "~"))
819 return true;
820
821 p = strrchr(filename, '.');
822 if (!p)
823 return false;
824
825 /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
826 * with always new suffixes and that everybody else should just adjust to that, then it really should be on
827 * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
828 * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
829 * string. Specifically: there's now:
830 *
831 * The generic suffixes "~" and ".bak" for backup files
832 * The generic prefix "." for hidden files
833 *
834 * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
835 * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
836 */
837
838 return STR_IN_SET(p + 1,
839 "rpmnew",
840 "rpmsave",
841 "rpmorig",
842 "dpkg-old",
843 "dpkg-new",
844 "dpkg-tmp",
845 "dpkg-dist",
846 "dpkg-bak",
847 "dpkg-backup",
848 "dpkg-remove",
849 "ucf-new",
850 "ucf-old",
851 "ucf-dist",
852 "swp",
853 "bak",
854 "old",
855 "new");
856 }
857
858 bool is_device_path(const char *path) {
859
860 /* Returns true on paths that refer to a device, either in
861 * sysfs or in /dev */
862
863 return path_startswith(path, "/dev/") ||
864 path_startswith(path, "/sys/");
865 }
866
867 bool is_deviceallow_pattern(const char *path) {
868 return path_startswith(path, "/dev/") ||
869 startswith(path, "block-") ||
870 startswith(path, "char-");
871 }
872
873 int systemd_installation_has_version(const char *root, unsigned minimal_version) {
874 const char *pattern;
875 int r;
876
877 /* Try to guess if systemd installation is later than the specified version. This
878 * is hacky and likely to yield false negatives, particularly if the installation
879 * is non-standard. False positives should be relatively rare.
880 */
881
882 NULSTR_FOREACH(pattern,
883 /* /lib works for systems without usr-merge, and for systems with a sane
884 * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
885 * for Gentoo which does a merge without making /lib a symlink.
886 */
887 "lib/systemd/libsystemd-shared-*.so\0"
888 "lib64/systemd/libsystemd-shared-*.so\0"
889 "usr/lib/systemd/libsystemd-shared-*.so\0"
890 "usr/lib64/systemd/libsystemd-shared-*.so\0") {
891
892 _cleanup_strv_free_ char **names = NULL;
893 _cleanup_free_ char *path = NULL;
894 char *c, **name;
895
896 path = prefix_root(root, pattern);
897 if (!path)
898 return -ENOMEM;
899
900 r = glob_extend(&names, path);
901 if (r == -ENOENT)
902 continue;
903 if (r < 0)
904 return r;
905
906 assert_se((c = endswith(path, "*.so")));
907 *c = '\0'; /* truncate the glob part */
908
909 STRV_FOREACH(name, names) {
910 /* This is most likely to run only once, hence let's not optimize anything. */
911 char *t, *t2;
912 unsigned version;
913
914 t = startswith(*name, path);
915 if (!t)
916 continue;
917
918 t2 = endswith(t, ".so");
919 if (!t2)
920 continue;
921
922 t2[0] = '\0'; /* truncate the suffix */
923
924 r = safe_atou(t, &version);
925 if (r < 0) {
926 log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
927 continue;
928 }
929
930 log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
931 *name, version,
932 version >= minimal_version ? "OK" : "too old");
933 if (version >= minimal_version)
934 return true;
935 }
936 }
937
938 return false;
939 }
940
941 bool dot_or_dot_dot(const char *path) {
942 if (!path)
943 return false;
944 if (path[0] != '.')
945 return false;
946 if (path[1] == 0)
947 return true;
948 if (path[1] != '.')
949 return false;
950
951 return path[2] == 0;
952 }