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