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/>.
27 #include <sys/statvfs.h>
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "string-util.h"
41 bool path_is_absolute(const char *p
) {
45 bool is_path(const char *p
) {
46 return !!strchr(p
, '/');
49 int path_get_parent(const char *path
, char **_r
) {
50 const char *e
, *a
= NULL
, *b
= NULL
, *p
;
60 for (e
= path
; *e
; e
++) {
62 if (!slash
&& *e
== '/') {
66 } else if (slash
&& *e
!= '/')
81 r
= strndup(path
, p
-path
);
90 int path_split_and_make_absolute(const char *p
, char ***ret
) {
97 l
= strv_split(p
, ":");
101 r
= path_strv_make_absolute_cwd(l
);
111 char *path_make_absolute(const char *p
, const char *prefix
) {
114 /* Makes every item in the list an absolute path by prepending
115 * the prefix, if specified and necessary */
117 if (path_is_absolute(p
) || !prefix
)
120 return strjoin(prefix
, "/", p
, NULL
);
123 int path_make_absolute_cwd(const char *p
, char **ret
) {
129 /* Similar to path_make_absolute(), but prefixes with the
130 * current working directory. */
132 if (path_is_absolute(p
))
135 _cleanup_free_
char *cwd
= NULL
;
137 cwd
= get_current_dir_name();
141 c
= strjoin(cwd
, "/", p
, NULL
);
150 int path_make_relative(const char *from_dir
, const char *to_path
, char **_r
) {
158 /* Strips the common part, and adds ".." elements as necessary. */
160 if (!path_is_absolute(from_dir
))
163 if (!path_is_absolute(to_path
))
166 /* Skip the common part. */
171 from_dir
+= strspn(from_dir
, "/");
172 to_path
+= strspn(to_path
, "/");
176 /* from_dir equals to_path. */
179 /* from_dir is a parent directory of to_path. */
185 path_kill_slashes(r
);
194 a
= strcspn(from_dir
, "/");
195 b
= strcspn(to_path
, "/");
200 if (memcmp(from_dir
, to_path
, a
) != 0)
207 /* If we're here, then "from_dir" has one or more elements that need to
208 * be replaced with "..". */
210 /* Count the number of necessary ".." elements. */
211 for (n_parents
= 0;;) {
212 from_dir
+= strspn(from_dir
, "/");
217 from_dir
+= strcspn(from_dir
, "/");
221 r
= malloc(n_parents
* 3 + strlen(to_path
) + 1);
225 for (p
= r
; n_parents
> 0; n_parents
--, p
+= 3)
229 path_kill_slashes(r
);
235 int path_strv_make_absolute_cwd(char **l
) {
239 /* Goes through every item in the string list and makes it
240 * absolute. This works in place and won't rollback any
241 * changes on failure. */
246 r
= path_make_absolute_cwd(*s
, &t
);
257 char **path_strv_resolve(char **l
, const char *prefix
) {
265 /* Goes through every item in the string list and canonicalize
266 * the path. This works in place and won't rollback any
267 * changes on failure. */
271 _cleanup_free_
char *orig
= NULL
;
273 if (!path_is_absolute(*s
)) {
280 t
= strappend(prefix
, orig
);
289 u
= canonicalize_file_name(t
);
291 if (errno
== ENOENT
) {
300 if (errno
== ENOMEM
|| errno
== 0)
309 x
= path_startswith(u
, prefix
);
311 /* restore the slash if it was lost */
312 if (!startswith(x
, "/"))
323 /* canonicalized path goes outside of
324 * prefix, keep the original path instead */
343 char **path_strv_resolve_uniq(char **l
, const char *prefix
) {
348 if (!path_strv_resolve(l
, prefix
))
354 char *path_kill_slashes(char *path
) {
358 /* Removes redundant inner and trailing slashes. Modifies the
359 * passed string in-place.
361 * ///foo///bar/ becomes /foo/bar
364 for (f
= path
, t
= path
; *f
; f
++) {
379 /* Special rule, if we are talking of the root directory, a
380 trailing slash is good */
382 if (t
== path
&& slash
)
389 char* path_startswith(const char *path
, const char *prefix
) {
393 if ((path
[0] == '/') != (prefix
[0] == '/'))
399 path
+= strspn(path
, "/");
400 prefix
+= strspn(prefix
, "/");
408 a
= strcspn(path
, "/");
409 b
= strcspn(prefix
, "/");
414 if (memcmp(path
, prefix
, a
) != 0)
422 int path_compare(const char *a
, const char *b
) {
428 /* A relative path and an abolute path must not compare as equal.
429 * Which one is sorted before the other does not really matter.
430 * Here a relative path is ordered before an absolute path. */
431 d
= (a
[0] == '/') - (b
[0] == '/');
441 if (*a
== 0 && *b
== 0)
444 /* Order prefixes first: "/foo" before "/foo/bar" */
453 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
454 d
= memcmp(a
, b
, MIN(j
, k
));
456 return (d
> 0) - (d
< 0); /* sign of d */
458 /* Sort "/foo/a" before "/foo/aaa" */
459 d
= (j
> k
) - (j
< k
); /* sign of (j - k) */
468 bool path_equal(const char *a
, const char *b
) {
469 return path_compare(a
, b
) == 0;
472 bool path_equal_or_files_same(const char *a
, const char *b
) {
473 return path_equal(a
, b
) || files_same(a
, b
) > 0;
476 char* path_join(const char *root
, const char *path
, const char *rest
) {
480 return strjoin(root
, endswith(root
, "/") ? "" : "/",
481 path
[0] == '/' ? path
+1 : path
,
482 rest
? (endswith(path
, "/") ? "" : "/") : NULL
,
483 rest
&& rest
[0] == '/' ? rest
+1 : rest
,
487 rest
? (endswith(path
, "/") ? "" : "/") : NULL
,
488 rest
&& rest
[0] == '/' ? rest
+1 : rest
,
492 static int fd_fdinfo_mnt_id(int fd
, const char *filename
, int flags
, int *mnt_id
) {
493 char path
[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
494 _cleanup_free_
char *fdinfo
= NULL
;
495 _cleanup_close_
int subfd
= -1;
499 if ((flags
& AT_EMPTY_PATH
) && isempty(filename
))
500 xsprintf(path
, "/proc/self/fdinfo/%i", fd
);
502 subfd
= openat(fd
, filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_PATH
);
506 xsprintf(path
, "/proc/self/fdinfo/%i", subfd
);
509 r
= read_full_file(path
, &fdinfo
, NULL
);
510 if (r
== -ENOENT
) /* The fdinfo directory is a relatively new addition */
515 p
= startswith(fdinfo
, "mnt_id:");
517 p
= strstr(fdinfo
, "\nmnt_id:");
518 if (!p
) /* The mnt_id field is a relatively new addition */
524 p
+= strspn(p
, WHITESPACE
);
525 p
[strcspn(p
, WHITESPACE
)] = 0;
527 return safe_atoi(p
, mnt_id
);
530 int fd_is_mount_point(int fd
, const char *filename
, int flags
) {
531 union file_handle_union h
= FILE_HANDLE_INIT
, h_parent
= FILE_HANDLE_INIT
;
532 int mount_id
= -1, mount_id_parent
= -1;
533 bool nosupp
= false, check_st_dev
= true;
540 /* First we will try the name_to_handle_at() syscall, which
541 * tells us the mount id and an opaque file "handle". It is
542 * not supported everywhere though (kernel compile-time
543 * option, not all file systems are hooked up). If it works
544 * the mount id is usually good enough to tell us whether
545 * something is a mount point.
547 * If that didn't work we will try to read the mount id from
548 * /proc/self/fdinfo/<fd>. This is almost as good as
549 * name_to_handle_at(), however, does not return the
550 * opaque file handle. The opaque file handle is pretty useful
551 * to detect the root directory, which we should always
552 * consider a mount point. Hence we use this only as
553 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
556 * As last fallback we do traditional fstat() based st_dev
557 * comparisons. This is how things were traditionally done,
558 * but unionfs breaks breaks this since it exposes file
559 * systems with a variety of st_dev reported. Also, btrfs
560 * subvolumes have different st_dev, even though they aren't
561 * real mounts of their own. */
563 r
= name_to_handle_at(fd
, filename
, &h
.handle
, &mount_id
, flags
);
566 /* This kernel does not support name_to_handle_at()
567 * fall back to simpler logic. */
568 goto fallback_fdinfo
;
569 else if (errno
== EOPNOTSUPP
)
570 /* This kernel or file system does not support
571 * name_to_handle_at(), hence let's see if the
572 * upper fs supports it (in which case it is a
573 * mount point), otherwise fallback to the
574 * traditional stat() logic */
580 r
= name_to_handle_at(fd
, "", &h_parent
.handle
, &mount_id_parent
, AT_EMPTY_PATH
);
582 if (errno
== EOPNOTSUPP
) {
584 /* Neither parent nor child do name_to_handle_at()?
585 We have no choice but to fall back. */
586 goto fallback_fdinfo
;
588 /* The parent can't do name_to_handle_at() but the
589 * directory we are interested in can?
590 * If so, it must be a mount point. */
596 /* The parent can do name_to_handle_at() but the
597 * directory we are interested in can't? If so, it
598 * must be a mount point. */
602 /* If the file handle for the directory we are
603 * interested in and its parent are identical, we
604 * assume this is the root directory, which is a mount
607 if (h
.handle
.handle_bytes
== h_parent
.handle
.handle_bytes
&&
608 h
.handle
.handle_type
== h_parent
.handle
.handle_type
&&
609 memcmp(h
.handle
.f_handle
, h_parent
.handle
.f_handle
, h
.handle
.handle_bytes
) == 0)
612 return mount_id
!= mount_id_parent
;
615 r
= fd_fdinfo_mnt_id(fd
, filename
, flags
, &mount_id
);
616 if (r
== -EOPNOTSUPP
)
621 r
= fd_fdinfo_mnt_id(fd
, "", AT_EMPTY_PATH
, &mount_id_parent
);
625 if (mount_id
!= mount_id_parent
)
628 /* Hmm, so, the mount ids are the same. This leaves one
629 * special case though for the root file system. For that,
630 * let's see if the parent directory has the same inode as we
631 * are interested in. Hence, let's also do fstat() checks now,
632 * too, but avoid the st_dev comparisons, since they aren't
633 * that useful on unionfs mounts. */
634 check_st_dev
= false;
637 /* yay for fstatat() taking a different set of flags than the other
639 if (flags
& AT_SYMLINK_FOLLOW
)
640 flags
&= ~AT_SYMLINK_FOLLOW
;
642 flags
|= AT_SYMLINK_NOFOLLOW
;
643 if (fstatat(fd
, filename
, &a
, flags
) < 0)
646 if (fstatat(fd
, "", &b
, AT_EMPTY_PATH
) < 0)
649 /* A directory with same device and inode as its parent? Must
650 * be the root directory */
651 if (a
.st_dev
== b
.st_dev
&&
652 a
.st_ino
== b
.st_ino
)
655 return check_st_dev
&& (a
.st_dev
!= b
.st_dev
);
658 /* flags can be AT_SYMLINK_FOLLOW or 0 */
659 int path_is_mount_point(const char *t
, int flags
) {
660 _cleanup_close_
int fd
= -1;
661 _cleanup_free_
char *canonical
= NULL
, *parent
= NULL
;
666 if (path_equal(t
, "/"))
669 /* we need to resolve symlinks manually, we can't just rely on
670 * fd_is_mount_point() to do that for us; if we have a structure like
671 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
672 * look at needs to be /usr, not /. */
673 if (flags
& AT_SYMLINK_FOLLOW
) {
674 canonical
= canonicalize_file_name(t
);
681 r
= path_get_parent(t
, &parent
);
685 fd
= openat(AT_FDCWD
, parent
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_PATH
);
689 return fd_is_mount_point(fd
, basename(t
), flags
);
692 int path_is_read_only_fs(const char *path
) {
697 if (statvfs(path
, &st
) < 0)
700 if (st
.f_flag
& ST_RDONLY
)
703 /* On NFS, statvfs() might not reflect whether we can actually
704 * write to the remote share. Let's try again with
705 * access(W_OK) which is more reliable, at least sometimes. */
706 if (access(path
, W_OK
) < 0 && errno
== EROFS
)
712 int path_is_os_tree(const char *path
) {
716 /* We use /usr/lib/os-release as flag file if something is an OS */
717 p
= strjoina(path
, "/usr/lib/os-release");
722 /* Also check for the old location in /etc, just in case. */
723 p
= strjoina(path
, "/etc/os-release");
729 int find_binary(const char *name
, char **ret
) {
736 if (access(name
, X_OK
) < 0)
740 r
= path_make_absolute_cwd(name
, ret
);
749 * Plain getenv, not secure_getenv, because we want
750 * to actually allow the user to pick the binary.
756 last_error
= -ENOENT
;
759 _cleanup_free_
char *j
= NULL
, *element
= NULL
;
761 r
= extract_first_word(&p
, &element
, ":", EXTRACT_RELAX
|EXTRACT_DONT_COALESCE_SEPARATORS
);
767 if (!path_is_absolute(element
))
770 j
= strjoin(element
, "/", name
, NULL
);
774 if (access(j
, X_OK
) >= 0) {
778 *ret
= path_kill_slashes(j
);
791 bool paths_check_timestamp(const char* const* paths
, usec_t
*timestamp
, bool update
) {
792 bool changed
= false;
793 const char* const* i
;
800 STRV_FOREACH(i
, paths
) {
804 if (stat(*i
, &stats
) < 0)
807 u
= timespec_load(&stats
.st_mtim
);
813 log_debug("timestamp of '%s' changed", *i
);
815 /* update timestamp */
826 static int binary_is_good(const char *binary
) {
827 _cleanup_free_
char *p
= NULL
, *d
= NULL
;
830 r
= find_binary(binary
, &p
);
836 /* An fsck that is linked to /bin/true is a non-existent
839 r
= readlink_malloc(p
, &d
);
840 if (r
== -EINVAL
) /* not a symlink */
845 return !path_equal(d
, "true") &&
846 !path_equal(d
, "/bin/true") &&
847 !path_equal(d
, "/usr/bin/true") &&
848 !path_equal(d
, "/dev/null");
851 int fsck_exists(const char *fstype
) {
856 if (streq(fstype
, "auto"))
859 checker
= strjoina("fsck.", fstype
);
860 return binary_is_good(checker
);
863 int mkfs_exists(const char *fstype
) {
868 if (streq(fstype
, "auto"))
871 mkfs
= strjoina("mkfs.", fstype
);
872 return binary_is_good(mkfs
);
875 char *prefix_root(const char *root
, const char *path
) {
879 /* If root is passed, prefixes path with it. Otherwise returns
884 /* First, drop duplicate prefixing slashes from the path */
885 while (path
[0] == '/' && path
[1] == '/')
888 if (isempty(root
) || path_equal(root
, "/"))
891 l
= strlen(root
) + 1 + strlen(path
) + 1;
899 while (p
> n
&& p
[-1] == '/')
909 int parse_path_argument_and_warn(const char *path
, bool suppress_root
, char **arg
) {
914 * This function is intended to be used in command line
915 * parsers, to handle paths that are passed in. It makes the
916 * path absolute, and reduces it to NULL if omitted or
917 * root (the latter optionally).
919 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
920 * SUCCESS! Hence, do not pass in uninitialized pointers.
928 r
= path_make_absolute_cwd(path
, &p
);
930 return log_error_errno(r
, "Failed to parse path \"%s\" and make it absolute: %m", path
);
932 path_kill_slashes(p
);
933 if (suppress_root
&& path_equal(p
, "/"))