199ca880aSopenharmony_ci/***
299ca880aSopenharmony_ci  This file is part of eudev, forked from systemd.
399ca880aSopenharmony_ci
499ca880aSopenharmony_ci  Copyright 2010-2012 Lennart Poettering
599ca880aSopenharmony_ci
699ca880aSopenharmony_ci  systemd is free software; you can redistribute it and/or modify it
799ca880aSopenharmony_ci  under the terms of the GNU Lesser General Public License as published by
899ca880aSopenharmony_ci  the Free Software Foundation; either version 2.1 of the License, or
999ca880aSopenharmony_ci  (at your option) any later version.
1099ca880aSopenharmony_ci
1199ca880aSopenharmony_ci  systemd is distributed in the hope that it will be useful, but
1299ca880aSopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1399ca880aSopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1499ca880aSopenharmony_ci  Lesser General Public License for more details.
1599ca880aSopenharmony_ci
1699ca880aSopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1799ca880aSopenharmony_ci  along with systemd; If not, see <http://www.gnu.org/licenses/>.
1899ca880aSopenharmony_ci***/
1999ca880aSopenharmony_ci
2099ca880aSopenharmony_ci#include <assert.h>
2199ca880aSopenharmony_ci#include <string.h>
2299ca880aSopenharmony_ci#include <unistd.h>
2399ca880aSopenharmony_ci#include <errno.h>
2499ca880aSopenharmony_ci#include <stdlib.h>
2599ca880aSopenharmony_ci#include <signal.h>
2699ca880aSopenharmony_ci#include <stdio.h>
2799ca880aSopenharmony_ci#include <fcntl.h>
2899ca880aSopenharmony_ci#include <dirent.h>
2999ca880aSopenharmony_ci#include <sys/statvfs.h>
3099ca880aSopenharmony_ci
3199ca880aSopenharmony_ci#include "macro.h"
3299ca880aSopenharmony_ci#include "util.h"
3399ca880aSopenharmony_ci#include "log.h"
3499ca880aSopenharmony_ci#include "strv.h"
3599ca880aSopenharmony_ci#include "mkdir.h"
3699ca880aSopenharmony_ci#include "path-util.h"
3799ca880aSopenharmony_ci#include "missing.h"
3899ca880aSopenharmony_ci#include "fileio.h"
3999ca880aSopenharmony_ci
4099ca880aSopenharmony_cibool path_is_absolute(const char *p) {
4199ca880aSopenharmony_ci        return p[0] == '/';
4299ca880aSopenharmony_ci}
4399ca880aSopenharmony_ci
4499ca880aSopenharmony_cibool is_path(const char *p) {
4599ca880aSopenharmony_ci        return !!strchr(p, '/');
4699ca880aSopenharmony_ci}
4799ca880aSopenharmony_ci
4899ca880aSopenharmony_ciint path_get_parent(const char *path, char **_r) {
4999ca880aSopenharmony_ci        const char *e, *a = NULL, *b = NULL, *p;
5099ca880aSopenharmony_ci        char *r;
5199ca880aSopenharmony_ci        bool slash = false;
5299ca880aSopenharmony_ci
5399ca880aSopenharmony_ci        assert(path);
5499ca880aSopenharmony_ci        assert(_r);
5599ca880aSopenharmony_ci
5699ca880aSopenharmony_ci        if (!*path)
5799ca880aSopenharmony_ci                return -EINVAL;
5899ca880aSopenharmony_ci
5999ca880aSopenharmony_ci        for (e = path; *e; e++) {
6099ca880aSopenharmony_ci
6199ca880aSopenharmony_ci                if (!slash && *e == '/') {
6299ca880aSopenharmony_ci                        a = b;
6399ca880aSopenharmony_ci                        b = e;
6499ca880aSopenharmony_ci                        slash = true;
6599ca880aSopenharmony_ci                } else if (slash && *e != '/')
6699ca880aSopenharmony_ci                        slash = false;
6799ca880aSopenharmony_ci        }
6899ca880aSopenharmony_ci
6999ca880aSopenharmony_ci        if (*(e-1) == '/')
7099ca880aSopenharmony_ci                p = a;
7199ca880aSopenharmony_ci        else
7299ca880aSopenharmony_ci                p = b;
7399ca880aSopenharmony_ci
7499ca880aSopenharmony_ci        if (!p)
7599ca880aSopenharmony_ci                return -EINVAL;
7699ca880aSopenharmony_ci
7799ca880aSopenharmony_ci        if (p == path)
7899ca880aSopenharmony_ci                r = strdup("/");
7999ca880aSopenharmony_ci        else
8099ca880aSopenharmony_ci                r = strndup(path, p-path);
8199ca880aSopenharmony_ci
8299ca880aSopenharmony_ci        if (!r)
8399ca880aSopenharmony_ci                return -ENOMEM;
8499ca880aSopenharmony_ci
8599ca880aSopenharmony_ci        *_r = r;
8699ca880aSopenharmony_ci        return 0;
8799ca880aSopenharmony_ci}
8899ca880aSopenharmony_ci
8999ca880aSopenharmony_cichar *path_make_absolute(const char *p, const char *prefix) {
9099ca880aSopenharmony_ci        assert(p);
9199ca880aSopenharmony_ci
9299ca880aSopenharmony_ci        /* Makes every item in the list an absolute path by prepending
9399ca880aSopenharmony_ci         * the prefix, if specified and necessary */
9499ca880aSopenharmony_ci
9599ca880aSopenharmony_ci        if (path_is_absolute(p) || !prefix)
9699ca880aSopenharmony_ci                return strdup(p);
9799ca880aSopenharmony_ci
9899ca880aSopenharmony_ci        return strjoin(prefix, "/", p, NULL);
9999ca880aSopenharmony_ci}
10099ca880aSopenharmony_ci
10199ca880aSopenharmony_cichar *path_make_absolute_cwd(const char *p) {
10299ca880aSopenharmony_ci        _cleanup_free_ char *cwd = NULL;
10399ca880aSopenharmony_ci
10499ca880aSopenharmony_ci        assert(p);
10599ca880aSopenharmony_ci
10699ca880aSopenharmony_ci        /* Similar to path_make_absolute(), but prefixes with the
10799ca880aSopenharmony_ci         * current working directory. */
10899ca880aSopenharmony_ci
10999ca880aSopenharmony_ci        if (path_is_absolute(p))
11099ca880aSopenharmony_ci                return strdup(p);
11199ca880aSopenharmony_ci
11299ca880aSopenharmony_ci        cwd = get_current_dir_name();
11399ca880aSopenharmony_ci        if (!cwd)
11499ca880aSopenharmony_ci                return NULL;
11599ca880aSopenharmony_ci
11699ca880aSopenharmony_ci        return strjoin(cwd, "/", p, NULL);
11799ca880aSopenharmony_ci}
11899ca880aSopenharmony_ci
11999ca880aSopenharmony_cichar **path_strv_resolve(char **l, const char *prefix) {
12099ca880aSopenharmony_ci        char **s;
12199ca880aSopenharmony_ci        unsigned k = 0;
12299ca880aSopenharmony_ci        bool enomem = false;
12399ca880aSopenharmony_ci
12499ca880aSopenharmony_ci        if (strv_isempty(l))
12599ca880aSopenharmony_ci                return l;
12699ca880aSopenharmony_ci
12799ca880aSopenharmony_ci        /* Goes through every item in the string list and canonicalize
12899ca880aSopenharmony_ci         * the path. This works in place and won't rollback any
12999ca880aSopenharmony_ci         * changes on failure. */
13099ca880aSopenharmony_ci
13199ca880aSopenharmony_ci        STRV_FOREACH(s, l) {
13299ca880aSopenharmony_ci                char *t, *u;
13399ca880aSopenharmony_ci                _cleanup_free_ char *orig = NULL;
13499ca880aSopenharmony_ci
13599ca880aSopenharmony_ci                if (!path_is_absolute(*s)) {
13699ca880aSopenharmony_ci                        free(*s);
13799ca880aSopenharmony_ci                        continue;
13899ca880aSopenharmony_ci                }
13999ca880aSopenharmony_ci
14099ca880aSopenharmony_ci                if (prefix) {
14199ca880aSopenharmony_ci                        orig = *s;
14299ca880aSopenharmony_ci                        t = strappend(prefix, orig);
14399ca880aSopenharmony_ci                        if (!t) {
14499ca880aSopenharmony_ci                                enomem = true;
14599ca880aSopenharmony_ci                                continue;
14699ca880aSopenharmony_ci                        }
14799ca880aSopenharmony_ci                } else
14899ca880aSopenharmony_ci                        t = *s;
14999ca880aSopenharmony_ci
15099ca880aSopenharmony_ci                errno = 0;
15199ca880aSopenharmony_ci                u = realpath(t, 0);
15299ca880aSopenharmony_ci                if (!u) {
15399ca880aSopenharmony_ci                        if (errno == ENOENT) {
15499ca880aSopenharmony_ci                                if (prefix) {
15599ca880aSopenharmony_ci                                        u = orig;
15699ca880aSopenharmony_ci                                        orig = NULL;
15799ca880aSopenharmony_ci                                        free(t);
15899ca880aSopenharmony_ci                                } else
15999ca880aSopenharmony_ci                                        u = t;
16099ca880aSopenharmony_ci                        } else {
16199ca880aSopenharmony_ci                                free(t);
16299ca880aSopenharmony_ci                                if (errno == ENOMEM || errno == 0)
16399ca880aSopenharmony_ci                                        enomem = true;
16499ca880aSopenharmony_ci
16599ca880aSopenharmony_ci                                continue;
16699ca880aSopenharmony_ci                        }
16799ca880aSopenharmony_ci                } else if (prefix) {
16899ca880aSopenharmony_ci                        char *x;
16999ca880aSopenharmony_ci
17099ca880aSopenharmony_ci                        free(t);
17199ca880aSopenharmony_ci                        x = path_startswith(u, prefix);
17299ca880aSopenharmony_ci                        if (x) {
17399ca880aSopenharmony_ci                                /* restore the slash if it was lost */
17499ca880aSopenharmony_ci                                if (!startswith(x, "/"))
17599ca880aSopenharmony_ci                                        *(--x) = '/';
17699ca880aSopenharmony_ci
17799ca880aSopenharmony_ci                                t = strdup(x);
17899ca880aSopenharmony_ci                                free(u);
17999ca880aSopenharmony_ci                                if (!t) {
18099ca880aSopenharmony_ci                                        enomem = true;
18199ca880aSopenharmony_ci                                        continue;
18299ca880aSopenharmony_ci                                }
18399ca880aSopenharmony_ci                                u = t;
18499ca880aSopenharmony_ci                        } else {
18599ca880aSopenharmony_ci                                /* canonicalized path goes outside of
18699ca880aSopenharmony_ci                                 * prefix, keep the original path instead */
18799ca880aSopenharmony_ci                                free(u);
18899ca880aSopenharmony_ci                                u = orig;
18999ca880aSopenharmony_ci                                orig = NULL;
19099ca880aSopenharmony_ci                        }
19199ca880aSopenharmony_ci                } else
19299ca880aSopenharmony_ci                        free(t);
19399ca880aSopenharmony_ci
19499ca880aSopenharmony_ci                l[k++] = u;
19599ca880aSopenharmony_ci        }
19699ca880aSopenharmony_ci
19799ca880aSopenharmony_ci        l[k] = NULL;
19899ca880aSopenharmony_ci
19999ca880aSopenharmony_ci        if (enomem)
20099ca880aSopenharmony_ci                return NULL;
20199ca880aSopenharmony_ci
20299ca880aSopenharmony_ci        return l;
20399ca880aSopenharmony_ci}
20499ca880aSopenharmony_ci
20599ca880aSopenharmony_cichar **path_strv_resolve_uniq(char **l, const char *prefix) {
20699ca880aSopenharmony_ci
20799ca880aSopenharmony_ci        if (strv_isempty(l))
20899ca880aSopenharmony_ci                return l;
20999ca880aSopenharmony_ci
21099ca880aSopenharmony_ci        if (!path_strv_resolve(l, prefix))
21199ca880aSopenharmony_ci                return NULL;
21299ca880aSopenharmony_ci
21399ca880aSopenharmony_ci        return strv_uniq(l);
21499ca880aSopenharmony_ci}
21599ca880aSopenharmony_ci
21699ca880aSopenharmony_cichar *path_kill_slashes(char *path) {
21799ca880aSopenharmony_ci        char *f, *t;
21899ca880aSopenharmony_ci        bool slash = false;
21999ca880aSopenharmony_ci
22099ca880aSopenharmony_ci        /* Removes redundant inner and trailing slashes. Modifies the
22199ca880aSopenharmony_ci         * passed string in-place.
22299ca880aSopenharmony_ci         *
22399ca880aSopenharmony_ci         * ///foo///bar/ becomes /foo/bar
22499ca880aSopenharmony_ci         */
22599ca880aSopenharmony_ci
22699ca880aSopenharmony_ci        for (f = path, t = path; *f; f++) {
22799ca880aSopenharmony_ci
22899ca880aSopenharmony_ci                if (*f == '/') {
22999ca880aSopenharmony_ci                        slash = true;
23099ca880aSopenharmony_ci                        continue;
23199ca880aSopenharmony_ci                }
23299ca880aSopenharmony_ci
23399ca880aSopenharmony_ci                if (slash) {
23499ca880aSopenharmony_ci                        slash = false;
23599ca880aSopenharmony_ci                        *(t++) = '/';
23699ca880aSopenharmony_ci                }
23799ca880aSopenharmony_ci
23899ca880aSopenharmony_ci                *(t++) = *f;
23999ca880aSopenharmony_ci        }
24099ca880aSopenharmony_ci
24199ca880aSopenharmony_ci        /* Special rule, if we are talking of the root directory, a
24299ca880aSopenharmony_ci        trailing slash is good */
24399ca880aSopenharmony_ci
24499ca880aSopenharmony_ci        if (t == path && slash)
24599ca880aSopenharmony_ci                *(t++) = '/';
24699ca880aSopenharmony_ci
24799ca880aSopenharmony_ci        *t = 0;
24899ca880aSopenharmony_ci        return path;
24999ca880aSopenharmony_ci}
25099ca880aSopenharmony_ci
25199ca880aSopenharmony_cichar* path_startswith(const char *path, const char *prefix) {
25299ca880aSopenharmony_ci        assert(path);
25399ca880aSopenharmony_ci        assert(prefix);
25499ca880aSopenharmony_ci
25599ca880aSopenharmony_ci        if ((path[0] == '/') != (prefix[0] == '/'))
25699ca880aSopenharmony_ci                return NULL;
25799ca880aSopenharmony_ci
25899ca880aSopenharmony_ci        for (;;) {
25999ca880aSopenharmony_ci                size_t a, b;
26099ca880aSopenharmony_ci
26199ca880aSopenharmony_ci                path += strspn(path, "/");
26299ca880aSopenharmony_ci                prefix += strspn(prefix, "/");
26399ca880aSopenharmony_ci
26499ca880aSopenharmony_ci                if (*prefix == 0)
26599ca880aSopenharmony_ci                        return (char*) path;
26699ca880aSopenharmony_ci
26799ca880aSopenharmony_ci                if (*path == 0)
26899ca880aSopenharmony_ci                        return NULL;
26999ca880aSopenharmony_ci
27099ca880aSopenharmony_ci                a = strcspn(path, "/");
27199ca880aSopenharmony_ci                b = strcspn(prefix, "/");
27299ca880aSopenharmony_ci
27399ca880aSopenharmony_ci                if (a != b)
27499ca880aSopenharmony_ci                        return NULL;
27599ca880aSopenharmony_ci
27699ca880aSopenharmony_ci                if (memcmp(path, prefix, a) != 0)
27799ca880aSopenharmony_ci                        return NULL;
27899ca880aSopenharmony_ci
27999ca880aSopenharmony_ci                path += a;
28099ca880aSopenharmony_ci                prefix += b;
28199ca880aSopenharmony_ci        }
28299ca880aSopenharmony_ci}
28399ca880aSopenharmony_ci
28499ca880aSopenharmony_ciint path_compare(const char *a, const char *b) {
28599ca880aSopenharmony_ci        int d;
28699ca880aSopenharmony_ci
28799ca880aSopenharmony_ci        assert(a);
28899ca880aSopenharmony_ci        assert(b);
28999ca880aSopenharmony_ci
29099ca880aSopenharmony_ci        /* A relative path and an abolute path must not compare as equal.
29199ca880aSopenharmony_ci         * Which one is sorted before the other does not really matter.
29299ca880aSopenharmony_ci         * Here a relative path is ordered before an absolute path. */
29399ca880aSopenharmony_ci        d = (a[0] == '/') - (b[0] == '/');
29499ca880aSopenharmony_ci        if (d)
29599ca880aSopenharmony_ci                return d;
29699ca880aSopenharmony_ci
29799ca880aSopenharmony_ci        for (;;) {
29899ca880aSopenharmony_ci                size_t j, k;
29999ca880aSopenharmony_ci
30099ca880aSopenharmony_ci                a += strspn(a, "/");
30199ca880aSopenharmony_ci                b += strspn(b, "/");
30299ca880aSopenharmony_ci
30399ca880aSopenharmony_ci                if (*a == 0 && *b == 0)
30499ca880aSopenharmony_ci                        return 0;
30599ca880aSopenharmony_ci
30699ca880aSopenharmony_ci                /* Order prefixes first: "/foo" before "/foo/bar" */
30799ca880aSopenharmony_ci                if (*a == 0)
30899ca880aSopenharmony_ci                        return -1;
30999ca880aSopenharmony_ci                if (*b == 0)
31099ca880aSopenharmony_ci                        return 1;
31199ca880aSopenharmony_ci
31299ca880aSopenharmony_ci                j = strcspn(a, "/");
31399ca880aSopenharmony_ci                k = strcspn(b, "/");
31499ca880aSopenharmony_ci
31599ca880aSopenharmony_ci                /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
31699ca880aSopenharmony_ci                d = memcmp(a, b, MIN(j, k));
31799ca880aSopenharmony_ci                if (d)
31899ca880aSopenharmony_ci                        return (d > 0) - (d < 0); /* sign of d */
31999ca880aSopenharmony_ci
32099ca880aSopenharmony_ci                /* Sort "/foo/a" before "/foo/aaa" */
32199ca880aSopenharmony_ci                d = (j > k) - (j < k);  /* sign of (j - k) */
32299ca880aSopenharmony_ci                if (d)
32399ca880aSopenharmony_ci                        return d;
32499ca880aSopenharmony_ci
32599ca880aSopenharmony_ci                a += j;
32699ca880aSopenharmony_ci                b += k;
32799ca880aSopenharmony_ci        }
32899ca880aSopenharmony_ci}
32999ca880aSopenharmony_ci
33099ca880aSopenharmony_cibool path_equal(const char *a, const char *b) {
33199ca880aSopenharmony_ci        return path_compare(a, b) == 0;
33299ca880aSopenharmony_ci}
33399ca880aSopenharmony_ci
33499ca880aSopenharmony_cistatic int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
33599ca880aSopenharmony_ci        char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
33699ca880aSopenharmony_ci        _cleanup_free_ char *fdinfo = NULL;
33799ca880aSopenharmony_ci        _cleanup_close_ int subfd = -1;
33899ca880aSopenharmony_ci        char *p;
33999ca880aSopenharmony_ci        int r;
34099ca880aSopenharmony_ci
34199ca880aSopenharmony_ci        if ((flags & AT_EMPTY_PATH) && isempty(filename))
34299ca880aSopenharmony_ci                xsprintf(path, "/proc/self/fdinfo/%i", fd);
34399ca880aSopenharmony_ci        else {
34499ca880aSopenharmony_ci                subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
34599ca880aSopenharmony_ci                if (subfd < 0)
34699ca880aSopenharmony_ci                        return -errno;
34799ca880aSopenharmony_ci
34899ca880aSopenharmony_ci                xsprintf(path, "/proc/self/fdinfo/%i", subfd);
34999ca880aSopenharmony_ci        }
35099ca880aSopenharmony_ci
35199ca880aSopenharmony_ci        r = read_full_file(path, &fdinfo, NULL);
35299ca880aSopenharmony_ci        if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
35399ca880aSopenharmony_ci                return -EOPNOTSUPP;
35499ca880aSopenharmony_ci        if (r < 0)
35599ca880aSopenharmony_ci                return -errno;
35699ca880aSopenharmony_ci
35799ca880aSopenharmony_ci        p = startswith(fdinfo, "mnt_id:");
35899ca880aSopenharmony_ci        if (!p) {
35999ca880aSopenharmony_ci                p = strstr(fdinfo, "\nmnt_id:");
36099ca880aSopenharmony_ci                if (!p) /* The mnt_id field is a relatively new addition */
36199ca880aSopenharmony_ci                        return -EOPNOTSUPP;
36299ca880aSopenharmony_ci
36399ca880aSopenharmony_ci                p += 8;
36499ca880aSopenharmony_ci        }
36599ca880aSopenharmony_ci
36699ca880aSopenharmony_ci        p += strspn(p, WHITESPACE);
36799ca880aSopenharmony_ci        p[strcspn(p, WHITESPACE)] = 0;
36899ca880aSopenharmony_ci
36999ca880aSopenharmony_ci        return safe_atoi(p, mnt_id);
37099ca880aSopenharmony_ci}
37199ca880aSopenharmony_ci
37299ca880aSopenharmony_ciint fd_is_mount_point(int fd) {
37399ca880aSopenharmony_ci        union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
37499ca880aSopenharmony_ci        int mount_id = -1, mount_id_parent = -1;
37599ca880aSopenharmony_ci        bool nosupp = false, check_st_dev = true;
37699ca880aSopenharmony_ci        struct stat a, b;
37799ca880aSopenharmony_ci        int r;
37899ca880aSopenharmony_ci
37999ca880aSopenharmony_ci        assert(fd >= 0);
38099ca880aSopenharmony_ci
38199ca880aSopenharmony_ci        /* First we will try the name_to_handle_at() syscall, which
38299ca880aSopenharmony_ci         * tells us the mount id and an opaque file "handle". It is
38399ca880aSopenharmony_ci         * not supported everywhere though (kernel compile-time
38499ca880aSopenharmony_ci         * option, not all file systems are hooked up). If it works
38599ca880aSopenharmony_ci         * the mount id is usually good enough to tell us whether
38699ca880aSopenharmony_ci         * something is a mount point.
38799ca880aSopenharmony_ci         *
38899ca880aSopenharmony_ci         * If that didn't work we will try to read the mount id from
38999ca880aSopenharmony_ci         * /proc/self/fdinfo/<fd>. This is almost as good as
39099ca880aSopenharmony_ci         * name_to_handle_at(), however, does not return the the
39199ca880aSopenharmony_ci         * opaque file handle. The opaque file handle is pretty useful
39299ca880aSopenharmony_ci         * to detect the root directory, which we should always
39399ca880aSopenharmony_ci         * consider a mount point. Hence we use this only as
39499ca880aSopenharmony_ci         * fallback. Exporting the mnt_id in fdinfo is a pretty recent
39599ca880aSopenharmony_ci         * kernel addition.
39699ca880aSopenharmony_ci         *
39799ca880aSopenharmony_ci         * As last fallback we do traditional fstat() based st_dev
39899ca880aSopenharmony_ci         * comparisons. This is how things were traditionally done,
39999ca880aSopenharmony_ci         * but unionfs breaks breaks this since it exposes file
40099ca880aSopenharmony_ci         * systems with a variety of st_dev reported. Also, btrfs
40199ca880aSopenharmony_ci         * subvolumes have different st_dev, even though they aren't
40299ca880aSopenharmony_ci         * real mounts of their own. */
40399ca880aSopenharmony_ci
40499ca880aSopenharmony_ci        r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
40599ca880aSopenharmony_ci        if (r < 0) {
40699ca880aSopenharmony_ci                if (errno == ENOSYS)
40799ca880aSopenharmony_ci                        /* This kernel does not support name_to_handle_at()
40899ca880aSopenharmony_ci                         * fall back to simpler logic. */
40999ca880aSopenharmony_ci                        goto fallback_fdinfo;
41099ca880aSopenharmony_ci                else if (errno == EOPNOTSUPP)
41199ca880aSopenharmony_ci                        /* This kernel or file system does not support
41299ca880aSopenharmony_ci                         * name_to_handle_at(), hence let's see if the
41399ca880aSopenharmony_ci                         * upper fs supports it (in which case it is a
41499ca880aSopenharmony_ci                         * mount point), otherwise fallback to the
41599ca880aSopenharmony_ci                         * traditional stat() logic */
41699ca880aSopenharmony_ci                        nosupp = true;
41799ca880aSopenharmony_ci                else
41899ca880aSopenharmony_ci                        return -errno;
41999ca880aSopenharmony_ci        }
42099ca880aSopenharmony_ci
42199ca880aSopenharmony_ci        r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
42299ca880aSopenharmony_ci        if (r < 0) {
42399ca880aSopenharmony_ci                if (errno == EOPNOTSUPP) {
42499ca880aSopenharmony_ci                        if (nosupp)
42599ca880aSopenharmony_ci                                /* Neither parent nor child do name_to_handle_at()?
42699ca880aSopenharmony_ci                                   We have no choice but to fall back. */
42799ca880aSopenharmony_ci                                goto fallback_fdinfo;
42899ca880aSopenharmony_ci                        else
42999ca880aSopenharmony_ci                                /* The parent can't do name_to_handle_at() but the
43099ca880aSopenharmony_ci                                 * directory we are interested in can?
43199ca880aSopenharmony_ci                                 * If so, it must be a mount point. */
43299ca880aSopenharmony_ci                                return 1;
43399ca880aSopenharmony_ci                } else
43499ca880aSopenharmony_ci                        return -errno;
43599ca880aSopenharmony_ci        }
43699ca880aSopenharmony_ci
43799ca880aSopenharmony_ci        /* The parent can do name_to_handle_at() but the
43899ca880aSopenharmony_ci         * directory we are interested in can't? If so, it
43999ca880aSopenharmony_ci         * must be a mount point. */
44099ca880aSopenharmony_ci        if (nosupp)
44199ca880aSopenharmony_ci                return 1;
44299ca880aSopenharmony_ci
44399ca880aSopenharmony_ci        /* If the file handle for the directory we are
44499ca880aSopenharmony_ci         * interested in and its parent are identical, we
44599ca880aSopenharmony_ci         * assume this is the root directory, which is a mount
44699ca880aSopenharmony_ci         * point. */
44799ca880aSopenharmony_ci
44899ca880aSopenharmony_ci        if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
44999ca880aSopenharmony_ci            h.handle.handle_type == h_parent.handle.handle_type &&
45099ca880aSopenharmony_ci            memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
45199ca880aSopenharmony_ci                return 1;
45299ca880aSopenharmony_ci
45399ca880aSopenharmony_ci        return mount_id != mount_id_parent;
45499ca880aSopenharmony_ci
45599ca880aSopenharmony_cifallback_fdinfo:
45699ca880aSopenharmony_ci        r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id);
45799ca880aSopenharmony_ci        if (r == -EOPNOTSUPP)
45899ca880aSopenharmony_ci                goto fallback_fstat;
45999ca880aSopenharmony_ci        if (r < 0)
46099ca880aSopenharmony_ci                return r;
46199ca880aSopenharmony_ci
46299ca880aSopenharmony_ci        r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent);
46399ca880aSopenharmony_ci        if (r < 0)
46499ca880aSopenharmony_ci                return r;
46599ca880aSopenharmony_ci
46699ca880aSopenharmony_ci        if (mount_id != mount_id_parent)
46799ca880aSopenharmony_ci                return 1;
46899ca880aSopenharmony_ci
46999ca880aSopenharmony_ci        /* Hmm, so, the mount ids are the same. This leaves one
47099ca880aSopenharmony_ci         * special case though for the root file system. For that,
47199ca880aSopenharmony_ci         * let's see if the parent directory has the same inode as we
47299ca880aSopenharmony_ci         * are interested in. Hence, let's also do fstat() checks now,
47399ca880aSopenharmony_ci         * too, but avoid the st_dev comparisons, since they aren't
47499ca880aSopenharmony_ci         * that useful on unionfs mounts. */
47599ca880aSopenharmony_ci        check_st_dev = false;
47699ca880aSopenharmony_ci
47799ca880aSopenharmony_cifallback_fstat:
47899ca880aSopenharmony_ci        if (fstatat(fd, "", &a, AT_EMPTY_PATH) < 0)
47999ca880aSopenharmony_ci                return -errno;
48099ca880aSopenharmony_ci
48199ca880aSopenharmony_ci        if (fstatat(fd, "..", &b, 0) < 0)
48299ca880aSopenharmony_ci                return -errno;
48399ca880aSopenharmony_ci
48499ca880aSopenharmony_ci        /* A directory with same device and inode as its parent? Must
48599ca880aSopenharmony_ci         * be the root directory */
48699ca880aSopenharmony_ci        if (a.st_dev == b.st_dev &&
48799ca880aSopenharmony_ci            a.st_ino == b.st_ino)
48899ca880aSopenharmony_ci                return 1;
48999ca880aSopenharmony_ci
49099ca880aSopenharmony_ci        return check_st_dev && (a.st_dev != b.st_dev);
49199ca880aSopenharmony_ci}
49299ca880aSopenharmony_ci
49399ca880aSopenharmony_ciint path_is_mount_point(const char *t, bool allow_symlink) {
49499ca880aSopenharmony_ci        _cleanup_close_ int fd = -1;
49599ca880aSopenharmony_ci
49699ca880aSopenharmony_ci        assert(t);
49799ca880aSopenharmony_ci
49899ca880aSopenharmony_ci        if (path_equal(t, "/"))
49999ca880aSopenharmony_ci                return 1;
50099ca880aSopenharmony_ci
50199ca880aSopenharmony_ci        fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
50299ca880aSopenharmony_ci        if (fd < 0)
50399ca880aSopenharmony_ci                return -errno;
50499ca880aSopenharmony_ci
50599ca880aSopenharmony_ci        return fd_is_mount_point(fd);
50699ca880aSopenharmony_ci}
50799ca880aSopenharmony_ci
50899ca880aSopenharmony_cibool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
50999ca880aSopenharmony_ci        bool changed = false;
51099ca880aSopenharmony_ci        const char* const* i;
51199ca880aSopenharmony_ci
51299ca880aSopenharmony_ci        assert(timestamp);
51399ca880aSopenharmony_ci
51499ca880aSopenharmony_ci        if (paths == NULL)
51599ca880aSopenharmony_ci                return false;
51699ca880aSopenharmony_ci
51799ca880aSopenharmony_ci        STRV_FOREACH(i, paths) {
51899ca880aSopenharmony_ci                struct stat stats;
51999ca880aSopenharmony_ci                usec_t u;
52099ca880aSopenharmony_ci
52199ca880aSopenharmony_ci                if (stat(*i, &stats) < 0)
52299ca880aSopenharmony_ci                        continue;
52399ca880aSopenharmony_ci
52499ca880aSopenharmony_ci                u = timespec_load(&stats.st_mtim);
52599ca880aSopenharmony_ci
52699ca880aSopenharmony_ci                /* first check */
52799ca880aSopenharmony_ci                if (*timestamp >= u)
52899ca880aSopenharmony_ci                        continue;
52999ca880aSopenharmony_ci
53099ca880aSopenharmony_ci                log_debug("timestamp of '%s' changed", *i);
53199ca880aSopenharmony_ci
53299ca880aSopenharmony_ci                /* update timestamp */
53399ca880aSopenharmony_ci                if (update) {
53499ca880aSopenharmony_ci                        *timestamp = u;
53599ca880aSopenharmony_ci                        changed = true;
53699ca880aSopenharmony_ci                } else
53799ca880aSopenharmony_ci                        return true;
53899ca880aSopenharmony_ci        }
53999ca880aSopenharmony_ci
54099ca880aSopenharmony_ci        return changed;
54199ca880aSopenharmony_ci}
54299ca880aSopenharmony_ci
54399ca880aSopenharmony_cichar *prefix_root(const char *root, const char *path) {
54499ca880aSopenharmony_ci        char *n, *p;
54599ca880aSopenharmony_ci        size_t l;
54699ca880aSopenharmony_ci
54799ca880aSopenharmony_ci        /* If root is passed, prefixes path with it. Otherwise returns
54899ca880aSopenharmony_ci         * it as is. */
54999ca880aSopenharmony_ci
55099ca880aSopenharmony_ci        assert(path);
55199ca880aSopenharmony_ci
55299ca880aSopenharmony_ci        /* First, drop duplicate prefixing slashes from the path */
55399ca880aSopenharmony_ci        while (path[0] == '/' && path[1] == '/')
55499ca880aSopenharmony_ci                path++;
55599ca880aSopenharmony_ci
55699ca880aSopenharmony_ci        if (isempty(root) || path_equal(root, "/"))
55799ca880aSopenharmony_ci                return strdup(path);
55899ca880aSopenharmony_ci
55999ca880aSopenharmony_ci        l = strlen(root) + 1 + strlen(path) + 1;
56099ca880aSopenharmony_ci
56199ca880aSopenharmony_ci        n = new(char, l);
56299ca880aSopenharmony_ci        if (!n)
56399ca880aSopenharmony_ci                return NULL;
56499ca880aSopenharmony_ci
56599ca880aSopenharmony_ci        p = stpcpy(n, root);
56699ca880aSopenharmony_ci
56799ca880aSopenharmony_ci        while (p > n && p[-1] == '/')
56899ca880aSopenharmony_ci                p--;
56999ca880aSopenharmony_ci
57099ca880aSopenharmony_ci        if (path[0] != '/')
57199ca880aSopenharmony_ci                *(p++) = '/';
57299ca880aSopenharmony_ci
57399ca880aSopenharmony_ci        strcpy(p, path);
57499ca880aSopenharmony_ci        return n;
57599ca880aSopenharmony_ci}
576