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 char **path_split_and_make_absolute(const char *p) {
91 l = strv_split(p, ":");
95 if (!path_strv_make_absolute_cwd(l)) {
103 char *path_make_absolute(const char *p, const char *prefix) {
106 /* Makes every item in the list an absolute path by prepending
107 * the prefix, if specified and necessary */
109 if (path_is_absolute(p) || !prefix)
112 return strjoin(prefix, "/", p, NULL);
115 char *path_make_absolute_cwd(const char *p) {
116 _cleanup_free_ char *cwd = NULL;
120 /* Similar to path_make_absolute(), but prefixes with the
121 * current working directory. */
123 if (path_is_absolute(p))
126 cwd = get_current_dir_name();
130 return strjoin(cwd, "/", p, NULL);
133 /// UNNEEDED by elogind
135 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
143 /* Strips the common part, and adds ".." elements as necessary. */
145 if (!path_is_absolute(from_dir))
148 if (!path_is_absolute(to_path))
151 /* Skip the common part. */
156 from_dir += strspn(from_dir, "/");
157 to_path += strspn(to_path, "/");
161 /* from_dir equals to_path. */
164 /* from_dir is a parent directory of to_path. */
170 path_kill_slashes(r);
179 a = strcspn(from_dir, "/");
180 b = strcspn(to_path, "/");
185 if (memcmp(from_dir, to_path, a) != 0)
192 /* If we're here, then "from_dir" has one or more elements that need to
193 * be replaced with "..". */
195 /* Count the number of necessary ".." elements. */
196 for (n_parents = 0;;) {
197 from_dir += strspn(from_dir, "/");
202 from_dir += strcspn(from_dir, "/");
206 r = malloc(n_parents * 3 + strlen(to_path) + 1);
210 for (p = r; n_parents > 0; n_parents--, p += 3)
214 path_kill_slashes(r);
221 char **path_strv_make_absolute_cwd(char **l) {
224 /* Goes through every item in the string list and makes it
225 * absolute. This works in place and won't rollback any
226 * changes on failure. */
231 t = path_make_absolute_cwd(*s);
242 char **path_strv_resolve(char **l, const char *prefix) {
250 /* Goes through every item in the string list and canonicalize
251 * the path. This works in place and won't rollback any
252 * changes on failure. */
256 _cleanup_free_ char *orig = NULL;
258 if (!path_is_absolute(*s)) {
265 t = strappend(prefix, orig);
274 u = canonicalize_file_name(t);
276 if (errno == ENOENT) {
285 if (errno == ENOMEM || errno == 0)
294 x = path_startswith(u, prefix);
296 /* restore the slash if it was lost */
297 if (!startswith(x, "/"))
308 /* canonicalized path goes outside of
309 * prefix, keep the original path instead */
328 char **path_strv_resolve_uniq(char **l, const char *prefix) {
333 if (!path_strv_resolve(l, prefix))
339 char *path_kill_slashes(char *path) {
343 /* Removes redundant inner and trailing slashes. Modifies the
344 * passed string in-place.
346 * ///foo///bar/ becomes /foo/bar
349 for (f = path, t = path; *f; f++) {
364 /* Special rule, if we are talking of the root directory, a
365 trailing slash is good */
367 if (t == path && slash)
374 char* path_startswith(const char *path, const char *prefix) {
378 if ((path[0] == '/') != (prefix[0] == '/'))
384 path += strspn(path, "/");
385 prefix += strspn(prefix, "/");
393 a = strcspn(path, "/");
394 b = strcspn(prefix, "/");
399 if (memcmp(path, prefix, a) != 0)
407 int path_compare(const char *a, const char *b) {
413 /* A relative path and an abolute path must not compare as equal.
414 * Which one is sorted before the other does not really matter.
415 * Here a relative path is ordered before an absolute path. */
416 d = (a[0] == '/') - (b[0] == '/');
426 if (*a == 0 && *b == 0)
429 /* Order prefixes first: "/foo" before "/foo/bar" */
438 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
439 d = memcmp(a, b, MIN(j, k));
441 return (d > 0) - (d < 0); /* sign of d */
443 /* Sort "/foo/a" before "/foo/aaa" */
444 d = (j > k) - (j < k); /* sign of (j - k) */
453 bool path_equal(const char *a, const char *b) {
454 return path_compare(a, b) == 0;
457 bool path_equal_or_files_same(const char *a, const char *b) {
458 return path_equal(a, b) || files_same(a, b) > 0;
461 char* path_join(const char *root, const char *path, const char *rest) {
465 return strjoin(root, endswith(root, "/") ? "" : "/",
466 path[0] == '/' ? path+1 : path,
467 rest ? (endswith(path, "/") ? "" : "/") : NULL,
468 rest && rest[0] == '/' ? rest+1 : rest,
472 rest ? (endswith(path, "/") ? "" : "/") : NULL,
473 rest && rest[0] == '/' ? rest+1 : rest,
477 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
478 char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
479 _cleanup_free_ char *fdinfo = NULL;
480 _cleanup_close_ int subfd = -1;
484 if ((flags & AT_EMPTY_PATH) && isempty(filename))
485 xsprintf(path, "/proc/self/fdinfo/%i", fd);
487 subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
491 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
494 r = read_full_file(path, &fdinfo, NULL);
495 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
500 p = startswith(fdinfo, "mnt_id:");
502 p = strstr(fdinfo, "\nmnt_id:");
503 if (!p) /* The mnt_id field is a relatively new addition */
509 p += strspn(p, WHITESPACE);
510 p[strcspn(p, WHITESPACE)] = 0;
512 return safe_atoi(p, mnt_id);
515 int fd_is_mount_point(int fd, const char *filename, int flags) {
516 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
517 int mount_id = -1, mount_id_parent = -1;
518 bool nosupp = false, check_st_dev = true;
525 /* First we will try the name_to_handle_at() syscall, which
526 * tells us the mount id and an opaque file "handle". It is
527 * not supported everywhere though (kernel compile-time
528 * option, not all file systems are hooked up). If it works
529 * the mount id is usually good enough to tell us whether
530 * something is a mount point.
532 * If that didn't work we will try to read the mount id from
533 * /proc/self/fdinfo/<fd>. This is almost as good as
534 * name_to_handle_at(), however, does not return the
535 * opaque file handle. The opaque file handle is pretty useful
536 * to detect the root directory, which we should always
537 * consider a mount point. Hence we use this only as
538 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
541 * As last fallback we do traditional fstat() based st_dev
542 * comparisons. This is how things were traditionally done,
543 * but unionfs breaks breaks this since it exposes file
544 * systems with a variety of st_dev reported. Also, btrfs
545 * subvolumes have different st_dev, even though they aren't
546 * real mounts of their own. */
548 r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
551 /* This kernel does not support name_to_handle_at()
552 * fall back to simpler logic. */
553 goto fallback_fdinfo;
554 else if (errno == EOPNOTSUPP)
555 /* This kernel or file system does not support
556 * name_to_handle_at(), hence let's see if the
557 * upper fs supports it (in which case it is a
558 * mount point), otherwise fallback to the
559 * traditional stat() logic */
565 r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
567 if (errno == EOPNOTSUPP) {
569 /* Neither parent nor child do name_to_handle_at()?
570 We have no choice but to fall back. */
571 goto fallback_fdinfo;
573 /* The parent can't do name_to_handle_at() but the
574 * directory we are interested in can?
575 * If so, it must be a mount point. */
581 /* The parent can do name_to_handle_at() but the
582 * directory we are interested in can't? If so, it
583 * must be a mount point. */
587 /* If the file handle for the directory we are
588 * interested in and its parent are identical, we
589 * assume this is the root directory, which is a mount
592 if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
593 h.handle.handle_type == h_parent.handle.handle_type &&
594 memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
597 return mount_id != mount_id_parent;
600 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
601 if (r == -EOPNOTSUPP)
606 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
610 if (mount_id != mount_id_parent)
613 /* Hmm, so, the mount ids are the same. This leaves one
614 * special case though for the root file system. For that,
615 * let's see if the parent directory has the same inode as we
616 * are interested in. Hence, let's also do fstat() checks now,
617 * too, but avoid the st_dev comparisons, since they aren't
618 * that useful on unionfs mounts. */
619 check_st_dev = false;
622 /* yay for fstatat() taking a different set of flags than the other
624 if (flags & AT_SYMLINK_FOLLOW)
625 flags &= ~AT_SYMLINK_FOLLOW;
627 flags |= AT_SYMLINK_NOFOLLOW;
628 if (fstatat(fd, filename, &a, flags) < 0)
631 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
634 /* A directory with same device and inode as its parent? Must
635 * be the root directory */
636 if (a.st_dev == b.st_dev &&
637 a.st_ino == b.st_ino)
640 return check_st_dev && (a.st_dev != b.st_dev);
643 /* flags can be AT_SYMLINK_FOLLOW or 0 */
644 int path_is_mount_point(const char *t, int flags) {
645 _cleanup_close_ int fd = -1;
646 _cleanup_free_ char *canonical = NULL, *parent = NULL;
651 if (path_equal(t, "/"))
654 /* we need to resolve symlinks manually, we can't just rely on
655 * fd_is_mount_point() to do that for us; if we have a structure like
656 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
657 * look at needs to be /usr, not /. */
658 if (flags & AT_SYMLINK_FOLLOW) {
659 canonical = canonicalize_file_name(t);
664 r = path_get_parent(canonical ?: t, &parent);
668 fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
672 return fd_is_mount_point(fd, basename(canonical ?: t), flags);
675 int path_is_read_only_fs(const char *path) {
680 if (statvfs(path, &st) < 0)
683 if (st.f_flag & ST_RDONLY)
686 /* On NFS, statvfs() might not reflect whether we can actually
687 * write to the remote share. Let's try again with
688 * access(W_OK) which is more reliable, at least sometimes. */
689 if (access(path, W_OK) < 0 && errno == EROFS)
695 int path_is_os_tree(const char *path) {
699 /* We use /usr/lib/os-release as flag file if something is an OS */
700 p = strjoina(path, "/usr/lib/os-release");
706 /* Also check for the old location in /etc, just in case. */
707 p = strjoina(path, "/etc/os-release");
713 int find_binary(const char *name, bool local, char **filename) {
717 if (local && access(name, X_OK) < 0)
723 p = path_make_absolute_cwd(name);
733 const char *word, *state;
737 * Plain getenv, not secure_getenv, because we want
738 * to actually allow the user to pick the binary.
740 path = getenv("PATH");
744 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
745 _cleanup_free_ char *p = NULL;
747 if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
750 if (access(p, X_OK) < 0)
754 *filename = path_kill_slashes(p);
765 /// UNNEEDED by elogind
767 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
768 bool changed = false;
769 const char* const* i;
776 STRV_FOREACH(i, paths) {
780 if (stat(*i, &stats) < 0)
783 u = timespec_load(&stats.st_mtim);
789 log_debug("timestamp of '%s' changed", *i);
791 /* update timestamp */
803 int fsck_exists(const char *fstype) {
804 _cleanup_free_ char *p = NULL, *d = NULL;
808 checker = strjoina("fsck.", fstype);
810 r = find_binary(checker, true, &p);
814 /* An fsck that is linked to /bin/true is a non-existent
817 r = readlink_malloc(p, &d);
819 (path_equal(d, "/bin/true") ||
820 path_equal(d, "/usr/bin/true") ||
821 path_equal(d, "/dev/null")))
827 char *prefix_root(const char *root, const char *path) {
831 /* If root is passed, prefixes path with it. Otherwise returns
836 /* First, drop duplicate prefixing slashes from the path */
837 while (path[0] == '/' && path[1] == '/')
840 if (isempty(root) || path_equal(root, "/"))
843 l = strlen(root) + 1 + strlen(path) + 1;
851 while (p > n && p[-1] == '/')