1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010-2012 Lennart Poettering
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.
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.
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/>.
28 #include <sys/statvfs.h>
34 #include "path-util.h"
38 bool path_is_absolute(const char *p) {
42 bool is_path(const char *p) {
43 return !!strchr(p, '/');
46 int path_get_parent(const char *path, char **_r) {
47 const char *e, *a = NULL, *b = NULL, *p;
57 for (e = path; *e; e++) {
59 if (!slash && *e == '/') {
63 } else if (slash && *e != '/')
78 r = strndup(path, p-path);
87 /// UNNEEDED by elogind
89 char **path_split_and_make_absolute(const char *p) {
93 l = strv_split(p, ":");
97 if (!path_strv_make_absolute_cwd(l)) {
106 char *path_make_absolute(const char *p, const char *prefix) {
109 /* Makes every item in the list an absolute path by prepending
110 * the prefix, if specified and necessary */
112 if (path_is_absolute(p) || !prefix)
115 return strjoin(prefix, "/", p, NULL);
118 char *path_make_absolute_cwd(const char *p) {
119 _cleanup_free_ char *cwd = NULL;
123 /* Similar to path_make_absolute(), but prefixes with the
124 * current working directory. */
126 if (path_is_absolute(p))
129 cwd = get_current_dir_name();
133 return strjoin(cwd, "/", p, NULL);
136 /// UNNEEDED by elogind
138 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
146 /* Strips the common part, and adds ".." elements as necessary. */
148 if (!path_is_absolute(from_dir))
151 if (!path_is_absolute(to_path))
154 /* Skip the common part. */
159 from_dir += strspn(from_dir, "/");
160 to_path += strspn(to_path, "/");
164 /* from_dir equals to_path. */
167 /* from_dir is a parent directory of to_path. */
173 path_kill_slashes(r);
182 a = strcspn(from_dir, "/");
183 b = strcspn(to_path, "/");
188 if (memcmp(from_dir, to_path, a) != 0)
195 /* If we're here, then "from_dir" has one or more elements that need to
196 * be replaced with "..". */
198 /* Count the number of necessary ".." elements. */
199 for (n_parents = 0;;) {
200 from_dir += strspn(from_dir, "/");
205 from_dir += strcspn(from_dir, "/");
209 r = malloc(n_parents * 3 + strlen(to_path) + 1);
213 for (p = r; n_parents > 0; n_parents--, p += 3)
217 path_kill_slashes(r);
224 char **path_strv_make_absolute_cwd(char **l) {
227 /* Goes through every item in the string list and makes it
228 * absolute. This works in place and won't rollback any
229 * changes on failure. */
234 t = path_make_absolute_cwd(*s);
245 char **path_strv_resolve(char **l, const char *prefix) {
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. */
259 _cleanup_free_ char *orig = NULL;
261 if (!path_is_absolute(*s)) {
268 t = strappend(prefix, orig);
277 u = canonicalize_file_name(t);
279 if (errno == ENOENT) {
288 if (errno == ENOMEM || errno == 0)
297 x = path_startswith(u, prefix);
299 /* restore the slash if it was lost */
300 if (!startswith(x, "/"))
311 /* canonicalized path goes outside of
312 * prefix, keep the original path instead */
331 char **path_strv_resolve_uniq(char **l, const char *prefix) {
336 if (!path_strv_resolve(l, prefix))
342 char *path_kill_slashes(char *path) {
346 /* Removes redundant inner and trailing slashes. Modifies the
347 * passed string in-place.
349 * ///foo///bar/ becomes /foo/bar
352 for (f = path, t = path; *f; f++) {
367 /* Special rule, if we are talking of the root directory, a
368 trailing slash is good */
370 if (t == path && slash)
377 char* path_startswith(const char *path, const char *prefix) {
381 if ((path[0] == '/') != (prefix[0] == '/'))
387 path += strspn(path, "/");
388 prefix += strspn(prefix, "/");
396 a = strcspn(path, "/");
397 b = strcspn(prefix, "/");
402 if (memcmp(path, prefix, a) != 0)
410 int path_compare(const char *a, const char *b) {
416 /* A relative path and an abolute path must not compare as equal.
417 * Which one is sorted before the other does not really matter.
418 * Here a relative path is ordered before an absolute path. */
419 d = (a[0] == '/') - (b[0] == '/');
429 if (*a == 0 && *b == 0)
432 /* Order prefixes first: "/foo" before "/foo/bar" */
441 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
442 d = memcmp(a, b, MIN(j, k));
444 return (d > 0) - (d < 0); /* sign of d */
446 /* Sort "/foo/a" before "/foo/aaa" */
447 d = (j > k) - (j < k); /* sign of (j - k) */
456 bool path_equal(const char *a, const char *b) {
457 return path_compare(a, b) == 0;
460 /// UNNEEDED by elogind
462 bool path_equal_or_files_same(const char *a, const char *b) {
463 return path_equal(a, b) || files_same(a, b) > 0;
466 char* path_join(const char *root, const char *path, const char *rest) {
470 return strjoin(root, endswith(root, "/") ? "" : "/",
471 path[0] == '/' ? path+1 : path,
472 rest ? (endswith(path, "/") ? "" : "/") : NULL,
473 rest && rest[0] == '/' ? rest+1 : rest,
477 rest ? (endswith(path, "/") ? "" : "/") : NULL,
478 rest && rest[0] == '/' ? rest+1 : rest,
483 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
484 char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
485 _cleanup_free_ char *fdinfo = NULL;
486 _cleanup_close_ int subfd = -1;
490 if ((flags & AT_EMPTY_PATH) && isempty(filename))
491 xsprintf(path, "/proc/self/fdinfo/%i", fd);
493 subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
497 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
500 r = read_full_file(path, &fdinfo, NULL);
501 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
506 p = startswith(fdinfo, "mnt_id:");
508 p = strstr(fdinfo, "\nmnt_id:");
509 if (!p) /* The mnt_id field is a relatively new addition */
515 p += strspn(p, WHITESPACE);
516 p[strcspn(p, WHITESPACE)] = 0;
518 return safe_atoi(p, mnt_id);
521 int fd_is_mount_point(int fd, const char *filename, int flags) {
522 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
523 int mount_id = -1, mount_id_parent = -1;
524 bool nosupp = false, check_st_dev = true;
531 /* First we will try the name_to_handle_at() syscall, which
532 * tells us the mount id and an opaque file "handle". It is
533 * not supported everywhere though (kernel compile-time
534 * option, not all file systems are hooked up). If it works
535 * the mount id is usually good enough to tell us whether
536 * something is a mount point.
538 * If that didn't work we will try to read the mount id from
539 * /proc/self/fdinfo/<fd>. This is almost as good as
540 * name_to_handle_at(), however, does not return the
541 * opaque file handle. The opaque file handle is pretty useful
542 * to detect the root directory, which we should always
543 * consider a mount point. Hence we use this only as
544 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
547 * As last fallback we do traditional fstat() based st_dev
548 * comparisons. This is how things were traditionally done,
549 * but unionfs breaks breaks this since it exposes file
550 * systems with a variety of st_dev reported. Also, btrfs
551 * subvolumes have different st_dev, even though they aren't
552 * real mounts of their own. */
554 r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
557 /* This kernel does not support name_to_handle_at()
558 * fall back to simpler logic. */
559 goto fallback_fdinfo;
560 else if (errno == EOPNOTSUPP)
561 /* This kernel or file system does not support
562 * name_to_handle_at(), hence let's see if the
563 * upper fs supports it (in which case it is a
564 * mount point), otherwise fallback to the
565 * traditional stat() logic */
571 r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
573 if (errno == EOPNOTSUPP) {
575 /* Neither parent nor child do name_to_handle_at()?
576 We have no choice but to fall back. */
577 goto fallback_fdinfo;
579 /* The parent can't do name_to_handle_at() but the
580 * directory we are interested in can?
581 * If so, it must be a mount point. */
587 /* The parent can do name_to_handle_at() but the
588 * directory we are interested in can't? If so, it
589 * must be a mount point. */
593 /* If the file handle for the directory we are
594 * interested in and its parent are identical, we
595 * assume this is the root directory, which is a mount
598 if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
599 h.handle.handle_type == h_parent.handle.handle_type &&
600 memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
603 return mount_id != mount_id_parent;
606 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
607 if (r == -EOPNOTSUPP)
612 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
616 if (mount_id != mount_id_parent)
619 /* Hmm, so, the mount ids are the same. This leaves one
620 * special case though for the root file system. For that,
621 * let's see if the parent directory has the same inode as we
622 * are interested in. Hence, let's also do fstat() checks now,
623 * too, but avoid the st_dev comparisons, since they aren't
624 * that useful on unionfs mounts. */
625 check_st_dev = false;
628 /* yay for fstatat() taking a different set of flags than the other
630 if (flags & AT_SYMLINK_FOLLOW)
631 flags &= ~AT_SYMLINK_FOLLOW;
633 flags |= AT_SYMLINK_NOFOLLOW;
634 if (fstatat(fd, filename, &a, flags) < 0)
637 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
640 /* A directory with same device and inode as its parent? Must
641 * be the root directory */
642 if (a.st_dev == b.st_dev &&
643 a.st_ino == b.st_ino)
646 return check_st_dev && (a.st_dev != b.st_dev);
649 /* flags can be AT_SYMLINK_FOLLOW or 0 */
650 int path_is_mount_point(const char *t, int flags) {
651 _cleanup_close_ int fd = -1;
652 _cleanup_free_ char *canonical = NULL, *parent = NULL;
657 if (path_equal(t, "/"))
660 /* we need to resolve symlinks manually, we can't just rely on
661 * fd_is_mount_point() to do that for us; if we have a structure like
662 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
663 * look at needs to be /usr, not /. */
664 if (flags & AT_SYMLINK_FOLLOW) {
665 canonical = canonicalize_file_name(t);
672 r = path_get_parent(t, &parent);
676 fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
680 return fd_is_mount_point(fd, basename(t), flags);
683 int path_is_read_only_fs(const char *path) {
688 if (statvfs(path, &st) < 0)
691 if (st.f_flag & ST_RDONLY)
694 /* On NFS, statvfs() might not reflect whether we can actually
695 * write to the remote share. Let's try again with
696 * access(W_OK) which is more reliable, at least sometimes. */
697 if (access(path, W_OK) < 0 && errno == EROFS)
703 /// UNNEEDED by elogind
705 int path_is_os_tree(const char *path) {
709 /* We use /usr/lib/os-release as flag file if something is an OS */
710 p = strjoina(path, "/usr/lib/os-release");
716 /* Also check for the old location in /etc, just in case. */
717 p = strjoina(path, "/etc/os-release");
723 int find_binary(const char *name, bool local, char **filename) {
727 if (local && access(name, X_OK) < 0)
733 p = path_make_absolute_cwd(name);
743 const char *word, *state;
747 * Plain getenv, not secure_getenv, because we want
748 * to actually allow the user to pick the binary.
750 path = getenv("PATH");
754 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
755 _cleanup_free_ char *p = NULL;
757 if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
760 if (access(p, X_OK) < 0)
764 *filename = path_kill_slashes(p);
775 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
776 bool changed = false;
777 const char* const* i;
784 STRV_FOREACH(i, paths) {
788 if (stat(*i, &stats) < 0)
791 u = timespec_load(&stats.st_mtim);
797 log_debug("timestamp of '%s' changed", *i);
799 /* update timestamp */
810 int fsck_exists(const char *fstype) {
811 _cleanup_free_ char *p = NULL, *d = NULL;
815 checker = strjoina("fsck.", fstype);
817 r = find_binary(checker, true, &p);
821 /* An fsck that is linked to /bin/true is a non-existent
824 r = readlink_malloc(p, &d);
826 (path_equal(d, "/bin/true") ||
827 path_equal(d, "/usr/bin/true") ||
828 path_equal(d, "/dev/null")))
834 char *prefix_root(const char *root, const char *path) {
838 /* If root is passed, prefixes path with it. Otherwise returns
843 /* First, drop duplicate prefixing slashes from the path */
844 while (path[0] == '/' && path[1] == '/')
847 if (isempty(root) || path_equal(root, "/"))
850 l = strlen(root) + 1 + strlen(path) + 1;
858 while (p > n && p[-1] == '/')