LCOV - code coverage report
Current view: top level - basic - path-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 416 519 80.2 %
Date: 2019-08-22 15:41:25 Functions: 34 38 89.5 %

          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             : }

Generated by: LCOV version 1.14