LCOV - code coverage report
Current view: top level - basic - path-util.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 353 417 84.7 %
Date: 2015-07-29 18:47:03 Functions: 24 25 96.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2010-2012 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <string.h>
      23             : #include <unistd.h>
      24             : #include <errno.h>
      25             : #include <stdlib.h>
      26             : #include <stdio.h>
      27             : #include <fcntl.h>
      28             : #include <sys/statvfs.h>
      29             : 
      30             : #include "macro.h"
      31             : #include "util.h"
      32             : #include "log.h"
      33             : #include "strv.h"
      34             : #include "path-util.h"
      35             : #include "missing.h"
      36             : #include "fileio.h"
      37             : 
      38       16570 : bool path_is_absolute(const char *p) {
      39       16570 :         return p[0] == '/';
      40             : }
      41             : 
      42          95 : bool is_path(const char *p) {
      43          95 :         return !!strchr(p, '/');
      44             : }
      45             : 
      46          99 : int path_get_parent(const char *path, char **_r) {
      47          99 :         const char *e, *a = NULL, *b = NULL, *p;
      48             :         char *r;
      49          99 :         bool slash = false;
      50             : 
      51          99 :         assert(path);
      52          99 :         assert(_r);
      53             : 
      54          99 :         if (!*path)
      55           0 :                 return -EINVAL;
      56             : 
      57        1758 :         for (e = path; *e; e++) {
      58             : 
      59        1659 :                 if (!slash && *e == '/') {
      60         254 :                         a = b;
      61         254 :                         b = e;
      62         254 :                         slash = true;
      63        1405 :                 } else if (slash && *e != '/')
      64         253 :                         slash = false;
      65             :         }
      66             : 
      67          99 :         if (*(e-1) == '/')
      68           1 :                 p = a;
      69             :         else
      70          98 :                 p = b;
      71             : 
      72          99 :         if (!p)
      73           1 :                 return -EINVAL;
      74             : 
      75          98 :         if (p == path)
      76          15 :                 r = strdup("/");
      77             :         else
      78          83 :                 r = strndup(path, p-path);
      79             : 
      80          98 :         if (!r)
      81           0 :                 return -ENOMEM;
      82             : 
      83          98 :         *_r = r;
      84          98 :         return 0;
      85             : }
      86             : 
      87          67 : char **path_split_and_make_absolute(const char *p) {
      88             :         char **l;
      89          67 :         assert(p);
      90             : 
      91          67 :         l = strv_split(p, ":");
      92          67 :         if (!l)
      93           0 :                 return NULL;
      94             : 
      95          67 :         if (!path_strv_make_absolute_cwd(l)) {
      96           0 :                 strv_free(l);
      97           0 :                 return NULL;
      98             :         }
      99             : 
     100          67 :         return l;
     101             : }
     102             : 
     103        5871 : char *path_make_absolute(const char *p, const char *prefix) {
     104        5871 :         assert(p);
     105             : 
     106             :         /* Makes every item in the list an absolute path by prepending
     107             :          * the prefix, if specified and necessary */
     108             : 
     109        5871 :         if (path_is_absolute(p) || !prefix)
     110           0 :                 return strdup(p);
     111             : 
     112        5871 :         return strjoin(prefix, "/", p, NULL);
     113             : }
     114             : 
     115          88 : char *path_make_absolute_cwd(const char *p) {
     116         176 :         _cleanup_free_ char *cwd = NULL;
     117             : 
     118          88 :         assert(p);
     119             : 
     120             :         /* Similar to path_make_absolute(), but prefixes with the
     121             :          * current working directory. */
     122             : 
     123          88 :         if (path_is_absolute(p))
     124          86 :                 return strdup(p);
     125             : 
     126           2 :         cwd = get_current_dir_name();
     127           2 :         if (!cwd)
     128           0 :                 return NULL;
     129             : 
     130           2 :         return strjoin(cwd, "/", p, NULL);
     131             : }
     132             : 
     133           9 : int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
     134             :         char *r, *p;
     135             :         unsigned n_parents;
     136             : 
     137           9 :         assert(from_dir);
     138           9 :         assert(to_path);
     139           9 :         assert(_r);
     140             : 
     141             :         /* Strips the common part, and adds ".." elements as necessary. */
     142             : 
     143           9 :         if (!path_is_absolute(from_dir))
     144           1 :                 return -EINVAL;
     145             : 
     146           8 :         if (!path_is_absolute(to_path))
     147           1 :                 return -EINVAL;
     148             : 
     149             :         /* Skip the common part. */
     150             :         for (;;) {
     151             :                 size_t a;
     152             :                 size_t b;
     153             : 
     154          14 :                 from_dir += strspn(from_dir, "/");
     155          14 :                 to_path += strspn(to_path, "/");
     156             : 
     157          14 :                 if (!*from_dir) {
     158           4 :                         if (!*to_path)
     159             :                                 /* from_dir equals to_path. */
     160           2 :                                 r = strdup(".");
     161             :                         else
     162             :                                 /* from_dir is a parent directory of to_path. */
     163           2 :                                 r = strdup(to_path);
     164             : 
     165           4 :                         if (!r)
     166           0 :                                 return -ENOMEM;
     167             : 
     168           4 :                         path_kill_slashes(r);
     169             : 
     170           4 :                         *_r = r;
     171           4 :                         return 0;
     172             :                 }
     173             : 
     174          10 :                 if (!*to_path)
     175           1 :                         break;
     176             : 
     177           9 :                 a = strcspn(from_dir, "/");
     178           9 :                 b = strcspn(to_path, "/");
     179             : 
     180           9 :                 if (a != b)
     181           2 :                         break;
     182             : 
     183           7 :                 if (memcmp(from_dir, to_path, a) != 0)
     184           0 :                         break;
     185             : 
     186           7 :                 from_dir += a;
     187           7 :                 to_path += b;
     188           7 :         }
     189             : 
     190             :         /* If we're here, then "from_dir" has one or more elements that need to
     191             :          * be replaced with "..". */
     192             : 
     193             :         /* Count the number of necessary ".." elements. */
     194           3 :         for (n_parents = 0;;) {
     195           9 :                 from_dir += strspn(from_dir, "/");
     196             : 
     197           9 :                 if (!*from_dir)
     198           3 :                         break;
     199             : 
     200           6 :                 from_dir += strcspn(from_dir, "/");
     201           6 :                 n_parents++;
     202           6 :         }
     203             : 
     204           3 :         r = malloc(n_parents * 3 + strlen(to_path) + 1);
     205           3 :         if (!r)
     206           0 :                 return -ENOMEM;
     207             : 
     208           9 :         for (p = r; n_parents > 0; n_parents--, p += 3)
     209           6 :                 memcpy(p, "../", 3);
     210             : 
     211           3 :         strcpy(p, to_path);
     212           3 :         path_kill_slashes(r);
     213             : 
     214           3 :         *_r = r;
     215           3 :         return 0;
     216             : }
     217             : 
     218          68 : char **path_strv_make_absolute_cwd(char **l) {
     219             :         char **s;
     220             : 
     221             :         /* Goes through every item in the string list and makes it
     222             :          * absolute. This works in place and won't rollback any
     223             :          * changes on failure. */
     224             : 
     225         151 :         STRV_FOREACH(s, l) {
     226             :                 char *t;
     227             : 
     228          83 :                 t = path_make_absolute_cwd(*s);
     229          83 :                 if (!t)
     230           0 :                         return NULL;
     231             : 
     232          83 :                 free(*s);
     233          83 :                 *s = t;
     234             :         }
     235             : 
     236          68 :         return l;
     237             : }
     238             : 
     239          95 : char **path_strv_resolve(char **l, const char *prefix) {
     240             :         char **s;
     241          95 :         unsigned k = 0;
     242          95 :         bool enomem = false;
     243             : 
     244          95 :         if (strv_isempty(l))
     245           0 :                 return l;
     246             : 
     247             :         /* Goes through every item in the string list and canonicalize
     248             :          * the path. This works in place and won't rollback any
     249             :          * changes on failure. */
     250             : 
     251         260 :         STRV_FOREACH(s, l) {
     252             :                 char *t, *u;
     253         330 :                 _cleanup_free_ char *orig = NULL;
     254             : 
     255         165 :                 if (!path_is_absolute(*s)) {
     256           0 :                         free(*s);
     257           0 :                         continue;
     258             :                 }
     259             : 
     260         165 :                 if (prefix) {
     261           7 :                         orig = *s;
     262           7 :                         t = strappend(prefix, orig);
     263           7 :                         if (!t) {
     264           0 :                                 enomem = true;
     265           0 :                                 continue;
     266             :                         }
     267             :                 } else
     268         158 :                         t = *s;
     269             : 
     270         165 :                 errno = 0;
     271         165 :                 u = canonicalize_file_name(t);
     272         165 :                 if (!u) {
     273          51 :                         if (errno == ENOENT) {
     274          51 :                                 if (prefix) {
     275           1 :                                         u = orig;
     276           1 :                                         orig = NULL;
     277           1 :                                         free(t);
     278             :                                 } else
     279          50 :                                         u = t;
     280             :                         } else {
     281           0 :                                 free(t);
     282           0 :                                 if (errno == ENOMEM || errno == 0)
     283           0 :                                         enomem = true;
     284             : 
     285           0 :                                 continue;
     286             :                         }
     287         114 :                 } else if (prefix) {
     288             :                         char *x;
     289             : 
     290           6 :                         free(t);
     291           6 :                         x = path_startswith(u, prefix);
     292           6 :                         if (x) {
     293             :                                 /* restore the slash if it was lost */
     294           6 :                                 if (!startswith(x, "/"))
     295           6 :                                         *(--x) = '/';
     296             : 
     297           6 :                                 t = strdup(x);
     298           6 :                                 free(u);
     299           6 :                                 if (!t) {
     300           0 :                                         enomem = true;
     301           0 :                                         continue;
     302             :                                 }
     303           6 :                                 u = t;
     304             :                         } else {
     305             :                                 /* canonicalized path goes outside of
     306             :                                  * prefix, keep the original path instead */
     307           0 :                                 free(u);
     308           0 :                                 u = orig;
     309           0 :                                 orig = NULL;
     310             :                         }
     311             :                 } else
     312         108 :                         free(t);
     313             : 
     314         165 :                 l[k++] = u;
     315             :         }
     316             : 
     317          95 :         l[k] = NULL;
     318             : 
     319          95 :         if (enomem)
     320           0 :                 return NULL;
     321             : 
     322          95 :         return l;
     323             : }
     324             : 
     325          95 : char **path_strv_resolve_uniq(char **l, const char *prefix) {
     326             : 
     327          95 :         if (strv_isempty(l))
     328           1 :                 return l;
     329             : 
     330          94 :         if (!path_strv_resolve(l, prefix))
     331           0 :                 return NULL;
     332             : 
     333          94 :         return strv_uniq(l);
     334             : }
     335             : 
     336        5697 : char *path_kill_slashes(char *path) {
     337             :         char *f, *t;
     338        5697 :         bool slash = false;
     339             : 
     340             :         /* Removes redundant inner and trailing slashes. Modifies the
     341             :          * passed string in-place.
     342             :          *
     343             :          * ///foo///bar/ becomes /foo/bar
     344             :          */
     345             : 
     346      196935 :         for (f = path, t = path; *f; f++) {
     347             : 
     348      191238 :                 if (*f == '/') {
     349       23470 :                         slash = true;
     350       23470 :                         continue;
     351             :                 }
     352             : 
     353      167768 :                 if (slash) {
     354       23217 :                         slash = false;
     355       23217 :                         *(t++) = '/';
     356             :                 }
     357             : 
     358      167768 :                 *(t++) = *f;
     359             :         }
     360             : 
     361             :         /* Special rule, if we are talking of the root directory, a
     362             :         trailing slash is good */
     363             : 
     364        5697 :         if (t == path && slash)
     365          86 :                 *(t++) = '/';
     366             : 
     367        5697 :         *t = 0;
     368        5697 :         return path;
     369             : }
     370             : 
     371        4779 : char* path_startswith(const char *path, const char *prefix) {
     372        4779 :         assert(path);
     373        4779 :         assert(prefix);
     374             : 
     375        4779 :         if ((path[0] == '/') != (prefix[0] == '/'))
     376         704 :                 return NULL;
     377             : 
     378             :         for (;;) {
     379             :                 size_t a, b;
     380             : 
     381        7836 :                 path += strspn(path, "/");
     382        7836 :                 prefix += strspn(prefix, "/");
     383             : 
     384        7836 :                 if (*prefix == 0)
     385        3488 :                         return (char*) path;
     386             : 
     387        4348 :                 if (*path == 0)
     388          20 :                         return NULL;
     389             : 
     390        4328 :                 a = strcspn(path, "/");
     391        4328 :                 b = strcspn(prefix, "/");
     392             : 
     393        4328 :                 if (a != b)
     394         296 :                         return NULL;
     395             : 
     396        4032 :                 if (memcmp(path, prefix, a) != 0)
     397         271 :                         return NULL;
     398             : 
     399        3761 :                 path += a;
     400        3761 :                 prefix += b;
     401        3761 :         }
     402             : }
     403             : 
     404        6084 : int path_compare(const char *a, const char *b) {
     405             :         int d;
     406             : 
     407        6084 :         assert(a);
     408        6084 :         assert(b);
     409             : 
     410             :         /* A relative path and an abolute path must not compare as equal.
     411             :          * Which one is sorted before the other does not really matter.
     412             :          * Here a relative path is ordered before an absolute path. */
     413        6084 :         d = (a[0] == '/') - (b[0] == '/');
     414        6084 :         if (d)
     415          18 :                 return d;
     416             : 
     417             :         for (;;) {
     418             :                 size_t j, k;
     419             : 
     420        9179 :                 a += strspn(a, "/");
     421        9179 :                 b += strspn(b, "/");
     422             : 
     423        9179 :                 if (*a == 0 && *b == 0)
     424         232 :                         return 0;
     425             : 
     426             :                 /* Order prefixes first: "/foo" before "/foo/bar" */
     427        8947 :                 if (*a == 0)
     428         402 :                         return -1;
     429        8545 :                 if (*b == 0)
     430         598 :                         return 1;
     431             : 
     432        7947 :                 j = strcspn(a, "/");
     433        7947 :                 k = strcspn(b, "/");
     434             : 
     435             :                 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
     436        7947 :                 d = memcmp(a, b, MIN(j, k));
     437        7947 :                 if (d)
     438        4826 :                         return (d > 0) - (d < 0); /* sign of d */
     439             : 
     440             :                 /* Sort "/foo/a" before "/foo/aaa" */
     441        3121 :                 d = (j > k) - (j < k);  /* sign of (j - k) */
     442        3121 :                 if (d)
     443           8 :                         return d;
     444             : 
     445        3113 :                 a += j;
     446        3113 :                 b += k;
     447        3113 :         }
     448             : }
     449             : 
     450        6048 : bool path_equal(const char *a, const char *b) {
     451        6048 :         return path_compare(a, b) == 0;
     452             : }
     453             : 
     454           1 : bool path_equal_or_files_same(const char *a, const char *b) {
     455           1 :         return path_equal(a, b) || files_same(a, b) > 0;
     456             : }
     457             : 
     458         623 : char* path_join(const char *root, const char *path, const char *rest) {
     459         623 :         assert(path);
     460             : 
     461         623 :         if (!isempty(root))
     462          15 :                 return strjoin(root, endswith(root, "/") ? "" : "/",
     463           5 :                                path[0] == '/' ? path+1 : path,
     464           4 :                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
     465           4 :                                rest && rest[0] == '/' ? rest+1 : rest,
     466             :                                NULL);
     467             :         else
     468         658 :                 return strjoin(path,
     469          39 :                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
     470          39 :                                rest && rest[0] == '/' ? rest+1 : rest,
     471             :                                NULL);
     472             : }
     473             : 
     474           4 : static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
     475           4 :         char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
     476           8 :         _cleanup_free_ char *fdinfo = NULL;
     477           8 :         _cleanup_close_ int subfd = -1;
     478             :         char *p;
     479             :         int r;
     480             : 
     481           4 :         if ((flags & AT_EMPTY_PATH) && isempty(filename))
     482           2 :                 xsprintf(path, "/proc/self/fdinfo/%i", fd);
     483             :         else {
     484           2 :                 subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
     485           2 :                 if (subfd < 0)
     486           0 :                         return -errno;
     487             : 
     488           2 :                 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
     489             :         }
     490             : 
     491           4 :         r = read_full_file(path, &fdinfo, NULL);
     492           4 :         if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
     493           0 :                 return -EOPNOTSUPP;
     494           4 :         if (r < 0)
     495           0 :                 return -errno;
     496             : 
     497           4 :         p = startswith(fdinfo, "mnt_id:");
     498           4 :         if (!p) {
     499           4 :                 p = strstr(fdinfo, "\nmnt_id:");
     500           4 :                 if (!p) /* The mnt_id field is a relatively new addition */
     501           0 :                         return -EOPNOTSUPP;
     502             : 
     503           4 :                 p += 8;
     504             :         }
     505             : 
     506           4 :         p += strspn(p, WHITESPACE);
     507           4 :         p[strcspn(p, WHITESPACE)] = 0;
     508             : 
     509           4 :         return safe_atoi(p, mnt_id);
     510             : }
     511             : 
     512          48 : int fd_is_mount_point(int fd, const char *filename, int flags) {
     513          48 :         union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
     514          48 :         int mount_id = -1, mount_id_parent = -1;
     515          48 :         bool nosupp = false, check_st_dev = true;
     516             :         struct stat a, b;
     517             :         int r;
     518             : 
     519          48 :         assert(fd >= 0);
     520          48 :         assert(filename);
     521             : 
     522             :         /* First we will try the name_to_handle_at() syscall, which
     523             :          * tells us the mount id and an opaque file "handle". It is
     524             :          * not supported everywhere though (kernel compile-time
     525             :          * option, not all file systems are hooked up). If it works
     526             :          * the mount id is usually good enough to tell us whether
     527             :          * something is a mount point.
     528             :          *
     529             :          * If that didn't work we will try to read the mount id from
     530             :          * /proc/self/fdinfo/<fd>. This is almost as good as
     531             :          * name_to_handle_at(), however, does not return the
     532             :          * opaque file handle. The opaque file handle is pretty useful
     533             :          * to detect the root directory, which we should always
     534             :          * consider a mount point. Hence we use this only as
     535             :          * fallback. Exporting the mnt_id in fdinfo is a pretty recent
     536             :          * kernel addition.
     537             :          *
     538             :          * As last fallback we do traditional fstat() based st_dev
     539             :          * comparisons. This is how things were traditionally done,
     540             :          * but unionfs breaks breaks this since it exposes file
     541             :          * systems with a variety of st_dev reported. Also, btrfs
     542             :          * subvolumes have different st_dev, even though they aren't
     543             :          * real mounts of their own. */
     544             : 
     545          48 :         r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
     546          48 :         if (r < 0) {
     547           7 :                 if (errno == ENOSYS)
     548             :                         /* This kernel does not support name_to_handle_at()
     549             :                          * fall back to simpler logic. */
     550           0 :                         goto fallback_fdinfo;
     551           7 :                 else if (errno == EOPNOTSUPP)
     552             :                         /* This kernel or file system does not support
     553             :                          * name_to_handle_at(), hence let's see if the
     554             :                          * upper fs supports it (in which case it is a
     555             :                          * mount point), otherwise fallback to the
     556             :                          * traditional stat() logic */
     557           7 :                         nosupp = true;
     558             :                 else
     559           0 :                         return -errno;
     560             :         }
     561             : 
     562          48 :         r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
     563          48 :         if (r < 0) {
     564           7 :                 if (errno == EOPNOTSUPP) {
     565           7 :                         if (nosupp)
     566             :                                 /* Neither parent nor child do name_to_handle_at()?
     567             :                                    We have no choice but to fall back. */
     568           2 :                                 goto fallback_fdinfo;
     569             :                         else
     570             :                                 /* The parent can't do name_to_handle_at() but the
     571             :                                  * directory we are interested in can?
     572             :                                  * If so, it must be a mount point. */
     573           5 :                                 return 1;
     574             :                 } else
     575           0 :                         return -errno;
     576             :         }
     577             : 
     578             :         /* The parent can do name_to_handle_at() but the
     579             :          * directory we are interested in can't? If so, it
     580             :          * must be a mount point. */
     581          41 :         if (nosupp)
     582           5 :                 return 1;
     583             : 
     584             :         /* If the file handle for the directory we are
     585             :          * interested in and its parent are identical, we
     586             :          * assume this is the root directory, which is a mount
     587             :          * point. */
     588             : 
     589          72 :         if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
     590          72 :             h.handle.handle_type == h_parent.handle.handle_type &&
     591          36 :             memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
     592           1 :                 return 1;
     593             : 
     594          35 :         return mount_id != mount_id_parent;
     595             : 
     596             : fallback_fdinfo:
     597           2 :         r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
     598           2 :         if (r == -EOPNOTSUPP)
     599           0 :                 goto fallback_fstat;
     600           2 :         if (r < 0)
     601           0 :                 return r;
     602             : 
     603           2 :         r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
     604           2 :         if (r < 0)
     605           0 :                 return r;
     606             : 
     607           2 :         if (mount_id != mount_id_parent)
     608           0 :                 return 1;
     609             : 
     610             :         /* Hmm, so, the mount ids are the same. This leaves one
     611             :          * special case though for the root file system. For that,
     612             :          * let's see if the parent directory has the same inode as we
     613             :          * are interested in. Hence, let's also do fstat() checks now,
     614             :          * too, but avoid the st_dev comparisons, since they aren't
     615             :          * that useful on unionfs mounts. */
     616           2 :         check_st_dev = false;
     617             : 
     618             : fallback_fstat:
     619             :         /* yay for fstatat() taking a different set of flags than the other
     620             :          * _at() above */
     621           2 :         if (flags & AT_SYMLINK_FOLLOW)
     622           1 :                 flags &= ~AT_SYMLINK_FOLLOW;
     623             :         else
     624           1 :                 flags |= AT_SYMLINK_NOFOLLOW;
     625           2 :         if (fstatat(fd, filename, &a, flags) < 0)
     626           0 :                 return -errno;
     627             : 
     628           2 :         if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
     629           0 :                 return -errno;
     630             : 
     631             :         /* A directory with same device and inode as its parent? Must
     632             :          * be the root directory */
     633           4 :         if (a.st_dev == b.st_dev &&
     634           2 :             a.st_ino == b.st_ino)
     635           0 :                 return 1;
     636             : 
     637           2 :         return check_st_dev && (a.st_dev != b.st_dev);
     638             : }
     639             : 
     640             : /* flags can be AT_SYMLINK_FOLLOW or 0 */
     641          28 : int path_is_mount_point(const char *t, int flags) {
     642          56 :         _cleanup_close_ int fd = -1;
     643          56 :         _cleanup_free_ char *canonical = NULL, *parent = NULL;
     644             :         int r;
     645             : 
     646          28 :         assert(t);
     647             : 
     648          28 :         if (path_equal(t, "/"))
     649           3 :                 return 1;
     650             : 
     651             :         /* we need to resolve symlinks manually, we can't just rely on
     652             :          * fd_is_mount_point() to do that for us; if we have a structure like
     653             :          * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
     654             :          * look at needs to be /usr, not /. */
     655          25 :         if (flags & AT_SYMLINK_FOLLOW) {
     656          11 :                 canonical = canonicalize_file_name(t);
     657          11 :                 if (!canonical)
     658           0 :                         return -errno;
     659             : 
     660          11 :                 t = canonical;
     661             :         }
     662             : 
     663          25 :         r = path_get_parent(t, &parent);
     664          25 :         if (r < 0)
     665           0 :                 return r;
     666             : 
     667          25 :         fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
     668          25 :         if (fd < 0)
     669           0 :                 return -errno;
     670             : 
     671          25 :         return fd_is_mount_point(fd, basename(t), flags);
     672             : }
     673             : 
     674           7 : int path_is_read_only_fs(const char *path) {
     675             :         struct statvfs st;
     676             : 
     677           7 :         assert(path);
     678             : 
     679           7 :         if (statvfs(path, &st) < 0)
     680           0 :                 return -errno;
     681             : 
     682           7 :         if (st.f_flag & ST_RDONLY)
     683           0 :                 return true;
     684             : 
     685             :         /* On NFS, statvfs() might not reflect whether we can actually
     686             :          * write to the remote share. Let's try again with
     687             :          * access(W_OK) which is more reliable, at least sometimes. */
     688           7 :         if (access(path, W_OK) < 0 && errno == EROFS)
     689           0 :                 return true;
     690             : 
     691           7 :         return false;
     692             : }
     693             : 
     694           0 : int path_is_os_tree(const char *path) {
     695             :         char *p;
     696             :         int r;
     697             : 
     698             :         /* We use /usr/lib/os-release as flag file if something is an OS */
     699           0 :         p = strjoina(path, "/usr/lib/os-release");
     700           0 :         r = access(p, F_OK);
     701             : 
     702           0 :         if (r >= 0)
     703           0 :                 return 1;
     704             : 
     705             :         /* Also check for the old location in /etc, just in case. */
     706           0 :         p = strjoina(path, "/etc/os-release");
     707           0 :         r = access(p, F_OK);
     708             : 
     709           0 :         return r >= 0;
     710             : }
     711             : 
     712          12 : int find_binary(const char *name, bool local, char **filename) {
     713          12 :         assert(name);
     714             : 
     715          12 :         if (is_path(name)) {
     716           6 :                 if (local && access(name, X_OK) < 0)
     717           1 :                         return -errno;
     718             : 
     719           5 :                 if (filename) {
     720             :                         char *p;
     721             : 
     722           5 :                         p = path_make_absolute_cwd(name);
     723           5 :                         if (!p)
     724           0 :                                 return -ENOMEM;
     725             : 
     726           5 :                         *filename = p;
     727             :                 }
     728             : 
     729           5 :                 return 0;
     730             :         } else {
     731             :                 const char *path;
     732             :                 const char *word, *state;
     733             :                 size_t l;
     734             : 
     735             :                 /**
     736             :                  * Plain getenv, not secure_getenv, because we want
     737             :                  * to actually allow the user to pick the binary.
     738             :                  */
     739           6 :                 path = getenv("PATH");
     740           6 :                 if (!path)
     741           2 :                         path = DEFAULT_PATH;
     742             : 
     743          44 :                 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
     744          82 :                         _cleanup_free_ char *p = NULL;
     745             : 
     746          41 :                         if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
     747           0 :                                 return -ENOMEM;
     748             : 
     749          41 :                         if (access(p, X_OK) < 0)
     750          38 :                                 continue;
     751             : 
     752           3 :                         if (filename) {
     753           3 :                                 *filename = path_kill_slashes(p);
     754           3 :                                 p = NULL;
     755             :                         }
     756             : 
     757           3 :                         return 0;
     758             :                 }
     759             : 
     760           3 :                 return -ENOENT;
     761             :         }
     762             : }
     763             : 
     764           2 : bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
     765           2 :         bool changed = false;
     766             :         const char* const* i;
     767             : 
     768           2 :         assert(timestamp);
     769             : 
     770           2 :         if (paths == NULL)
     771           0 :                 return false;
     772             : 
     773           8 :         STRV_FOREACH(i, paths) {
     774             :                 struct stat stats;
     775             :                 usec_t u;
     776             : 
     777           6 :                 if (stat(*i, &stats) < 0)
     778           6 :                         continue;
     779             : 
     780           4 :                 u = timespec_load(&stats.st_mtim);
     781             : 
     782             :                 /* first check */
     783           4 :                 if (*timestamp >= u)
     784           2 :                         continue;
     785             : 
     786           2 :                 log_debug("timestamp of '%s' changed", *i);
     787             : 
     788             :                 /* update timestamp */
     789           2 :                 if (update) {
     790           2 :                         *timestamp = u;
     791           2 :                         changed = true;
     792             :                 } else
     793           0 :                         return true;
     794             :         }
     795             : 
     796           2 :         return changed;
     797             : }
     798             : 
     799           2 : int fsck_exists(const char *fstype) {
     800           4 :         _cleanup_free_ char *p = NULL, *d = NULL;
     801             :         const char *checker;
     802             :         int r;
     803             : 
     804           2 :         checker = strjoina("fsck.", fstype);
     805             : 
     806           2 :         r = find_binary(checker, true, &p);
     807           2 :         if (r < 0)
     808           1 :                 return r;
     809             : 
     810             :         /* An fsck that is linked to /bin/true is a non-existent
     811             :          * fsck */
     812             : 
     813           1 :         r = readlink_malloc(p, &d);
     814           1 :         if (r >= 0 &&
     815           0 :             (path_equal(d, "/bin/true") ||
     816           0 :              path_equal(d, "/usr/bin/true") ||
     817           0 :              path_equal(d, "/dev/null")))
     818           0 :                 return -ENOENT;
     819             : 
     820           1 :         return 0;
     821             : }
     822             : 
     823          12 : char *prefix_root(const char *root, const char *path) {
     824             :         char *n, *p;
     825             :         size_t l;
     826             : 
     827             :         /* If root is passed, prefixes path with it. Otherwise returns
     828             :          * it as is. */
     829             : 
     830          12 :         assert(path);
     831             : 
     832             :         /* First, drop duplicate prefixing slashes from the path */
     833          32 :         while (path[0] == '/' && path[1] == '/')
     834           8 :                 path++;
     835             : 
     836          12 :         if (isempty(root) || path_equal(root, "/"))
     837           6 :                 return strdup(path);
     838             : 
     839           6 :         l = strlen(root) + 1 + strlen(path) + 1;
     840             : 
     841           6 :         n = new(char, l);
     842           6 :         if (!n)
     843           0 :                 return NULL;
     844             : 
     845           6 :         p = stpcpy(n, root);
     846             : 
     847          17 :         while (p > n && p[-1] == '/')
     848           5 :                 p--;
     849             : 
     850           6 :         if (path[0] != '/')
     851           2 :                 *(p++) = '/';
     852             : 
     853           6 :         strcpy(p, path);
     854           6 :         return n;
     855             : }

Generated by: LCOV version 1.11