]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-util.c
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 int path_make_relative(const char *from_dir
, const char *to_path
, char **_r
) {
141 /* Strips the common part, and adds ".." elements as necessary. */
143 if (!path_is_absolute(from_dir
))
146 if (!path_is_absolute(to_path
))
149 /* Skip the common part. */
154 from_dir
+= strspn(from_dir
, "/");
155 to_path
+= strspn(to_path
, "/");
159 /* from_dir equals to_path. */
162 /* from_dir is a parent directory of to_path. */
168 path_kill_slashes(r
);
177 a
= strcspn(from_dir
, "/");
178 b
= strcspn(to_path
, "/");
183 if (memcmp(from_dir
, to_path
, a
) != 0)
190 /* If we're here, then "from_dir" has one or more elements that need to
191 * be replaced with "..". */
193 /* Count the number of necessary ".." elements. */
194 for (n_parents
= 0;;) {
195 from_dir
+= strspn(from_dir
, "/");
200 from_dir
+= strcspn(from_dir
, "/");
204 r
= malloc(n_parents
* 3 + strlen(to_path
) + 1);
208 for (p
= r
; n_parents
> 0; n_parents
--, p
+= 3)
212 path_kill_slashes(r
);
218 char **path_strv_make_absolute_cwd(char **l
) {
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. */
228 t
= path_make_absolute_cwd(*s
);
239 char **path_strv_resolve(char **l
, const char *prefix
) {
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. */
253 _cleanup_free_
char *orig
= NULL
;
255 if (!path_is_absolute(*s
)) {
262 t
= strappend(prefix
, orig
);
271 u
= canonicalize_file_name(t
);
273 if (errno
== ENOENT
) {
282 if (errno
== ENOMEM
|| errno
== 0)
291 x
= path_startswith(u
, prefix
);
293 /* restore the slash if it was lost */
294 if (!startswith(x
, "/"))
305 /* canonicalized path goes outside of
306 * prefix, keep the original path instead */
325 char **path_strv_resolve_uniq(char **l
, const char *prefix
) {
330 if (!path_strv_resolve(l
, prefix
))
336 char *path_kill_slashes(char *path
) {
340 /* Removes redundant inner and trailing slashes. Modifies the
341 * passed string in-place.
343 * ///foo///bar/ becomes /foo/bar
346 for (f
= path
, t
= path
; *f
; f
++) {
361 /* Special rule, if we are talking of the root directory, a
362 trailing slash is good */
364 if (t
== path
&& slash
)
371 char* path_startswith(const char *path
, const char *prefix
) {
375 if ((path
[0] == '/') != (prefix
[0] == '/'))
381 path
+= strspn(path
, "/");
382 prefix
+= strspn(prefix
, "/");
390 a
= strcspn(path
, "/");
391 b
= strcspn(prefix
, "/");
396 if (memcmp(path
, prefix
, a
) != 0)
404 int path_compare(const char *a
, const char *b
) {
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 d
= (a
[0] == '/') - (b
[0] == '/');
423 if (*a
== 0 && *b
== 0)
426 /* Order prefixes first: "/foo" before "/foo/bar" */
435 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
436 d
= memcmp(a
, b
, MIN(j
, k
));
438 return (d
> 0) - (d
< 0); /* sign of d */
440 /* Sort "/foo/a" before "/foo/aaa" */
441 d
= (j
> k
) - (j
< k
); /* sign of (j - k) */
450 bool path_equal(const char *a
, const char *b
) {
451 return path_compare(a
, b
) == 0;
454 bool path_equal_or_files_same(const char *a
, const char *b
) {
455 return path_equal(a
, b
) || files_same(a
, b
) > 0;
458 char* path_join(const char *root
, const char *path
, const char *rest
) {
462 return strjoin(root
, endswith(root
, "/") ? "" : "/",
463 path
[0] == '/' ? path
+1 : path
,
464 rest
? (endswith(path
, "/") ? "" : "/") : NULL
,
465 rest
&& rest
[0] == '/' ? rest
+1 : rest
,
469 rest
? (endswith(path
, "/") ? "" : "/") : NULL
,
470 rest
&& rest
[0] == '/' ? rest
+1 : rest
,
474 static int fd_fdinfo_mnt_id(int fd
, const char *filename
, int flags
, int *mnt_id
) {
475 char path
[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
476 _cleanup_free_
char *fdinfo
= NULL
;
477 _cleanup_close_
int subfd
= -1;
481 if ((flags
& AT_EMPTY_PATH
) && isempty(filename
))
482 xsprintf(path
, "/proc/self/fdinfo/%i", fd
);
484 subfd
= openat(fd
, filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_PATH
);
488 xsprintf(path
, "/proc/self/fdinfo/%i", subfd
);
491 r
= read_full_file(path
, &fdinfo
, NULL
);
492 if (r
== -ENOENT
) /* The fdinfo directory is a relatively new addition */
497 p
= startswith(fdinfo
, "mnt_id:");
499 p
= strstr(fdinfo
, "\nmnt_id:");
500 if (!p
) /* The mnt_id field is a relatively new addition */
506 p
+= strspn(p
, WHITESPACE
);
507 p
[strcspn(p
, WHITESPACE
)] = 0;
509 return safe_atoi(p
, mnt_id
);
512 int fd_is_mount_point(int fd
) {
513 union file_handle_union h
= FILE_HANDLE_INIT
, h_parent
= FILE_HANDLE_INIT
;
514 int mount_id
= -1, mount_id_parent
= -1;
515 bool nosupp
= false, check_st_dev
= false;
521 /* First we will try the name_to_handle_at() syscall, which
522 * tells us the mount id and an opaque file "handle". It is
523 * not supported everywhere though (kernel compile-time
524 * option, not all file systems are hooked up). If it works
525 * the mount id is usually good enough to tell us whether
526 * something is a mount point.
528 * If that didn't work we will try to read the mount id from
529 * /proc/self/fdinfo/<fd>. This is almost as good as
530 * name_to_handle_at(), however, does not return the the
531 * opaque file handle. The opaque file handle is pretty useful
532 * to detect the root directory, which we should always
533 * consider a mount point. Hence we use this only as
534 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
537 * As last fallback we do traditional fstat() based st_dev
538 * comparisons. This is how things were traditionally done,
539 * but unionfs breaks breaks this since it exposes file
540 * systems with a variety of st_dev reported. Also, btrfs
541 * subvolumes have different st_dev, even though they aren't
542 * real mounts of their own. */
544 r
= name_to_handle_at(fd
, "", &h
.handle
, &mount_id
, AT_EMPTY_PATH
);
547 /* This kernel does not support name_to_handle_at()
548 * fall back to simpler logic. */
549 goto fallback_fdinfo
;
550 else if (errno
== EOPNOTSUPP
)
551 /* This kernel or file system does not support
552 * name_to_handle_at(), hence let's see if the
553 * upper fs supports it (in which case it is a
554 * mount point), otherwise fallback to the
555 * traditional stat() logic */
561 r
= name_to_handle_at(fd
, "..", &h_parent
.handle
, &mount_id_parent
, 0);
563 if (errno
== EOPNOTSUPP
) {
565 /* Neither parent nor child do name_to_handle_at()?
566 We have no choice but to fall back. */
567 goto fallback_fdinfo
;
569 /* The parent can't do name_to_handle_at() but the
570 * directory we are interested in can?
571 * If so, it must be a mount point. */
577 /* The parent can do name_to_handle_at() but the
578 * directory we are interested in can't? If so, it
579 * must be a mount point. */
583 /* If the file handle for the directory we are
584 * interested in and its parent are identical, we
585 * assume this is the root directory, which is a mount
588 if (h
.handle
.handle_bytes
== h_parent
.handle
.handle_bytes
&&
589 h
.handle
.handle_type
== h_parent
.handle
.handle_type
&&
590 memcmp(h
.handle
.f_handle
, h_parent
.handle
.f_handle
, h
.handle
.handle_bytes
) == 0)
593 return mount_id
!= mount_id_parent
;
596 r
= fd_fdinfo_mnt_id(fd
, "", AT_EMPTY_PATH
, &mount_id
);
597 if (r
== -EOPNOTSUPP
)
602 r
= fd_fdinfo_mnt_id(fd
, "..", 0, &mount_id_parent
);
606 if (mount_id
!= mount_id_parent
)
609 /* Hmm, so, the mount ids are the same. This leaves one
610 * special case though for the root file system. For that,
611 * let's see if the parent directory has the same inode as we
612 * are interested in. Hence, let's also do fstat() checks now,
613 * too, but avoid the st_dev comparisons, since they aren't
614 * that useful on unionfs mounts. */
615 check_st_dev
= false;
618 if (fstatat(fd
, "", &a
, AT_EMPTY_PATH
) < 0)
621 if (fstatat(fd
, "..", &b
, 0) < 0)
624 /* A directory with same device and inode as its parent? Must
625 * be the root directory */
626 if (a
.st_dev
== b
.st_dev
&&
627 a
.st_ino
== b
.st_ino
)
630 return check_st_dev
&& (a
.st_dev
!= b
.st_dev
);
633 int path_is_mount_point(const char *t
, bool allow_symlink
) {
634 _cleanup_close_
int fd
= -1;
638 if (path_equal(t
, "/"))
641 fd
= openat(AT_FDCWD
, t
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|(allow_symlink
? 0 : O_PATH
));
645 return fd_is_mount_point(fd
);
648 int path_is_read_only_fs(const char *path
) {
653 if (statvfs(path
, &st
) < 0)
656 if (st
.f_flag
& ST_RDONLY
)
659 /* On NFS, statvfs() might not reflect whether we can actually
660 * write to the remote share. Let's try again with
661 * access(W_OK) which is more reliable, at least sometimes. */
662 if (access(path
, W_OK
) < 0 && errno
== EROFS
)
668 int path_is_os_tree(const char *path
) {
672 /* We use /usr/lib/os-release as flag file if something is an OS */
673 p
= strjoina(path
, "/usr/lib/os-release");
679 /* Also check for the old location in /etc, just in case. */
680 p
= strjoina(path
, "/etc/os-release");
686 int find_binary(const char *name
, bool local
, char **filename
) {
690 if (local
&& access(name
, X_OK
) < 0)
696 p
= path_make_absolute_cwd(name
);
706 const char *word
, *state
;
710 * Plain getenv, not secure_getenv, because we want
711 * to actually allow the user to pick the binary.
713 path
= getenv("PATH");
717 FOREACH_WORD_SEPARATOR(word
, l
, path
, ":", state
) {
718 _cleanup_free_
char *p
= NULL
;
720 if (asprintf(&p
, "%.*s/%s", (int) l
, word
, name
) < 0)
723 if (access(p
, X_OK
) < 0)
727 *filename
= path_kill_slashes(p
);
738 bool paths_check_timestamp(const char* const* paths
, usec_t
*timestamp
, bool update
) {
739 bool changed
= false;
740 const char* const* i
;
747 STRV_FOREACH(i
, paths
) {
751 if (stat(*i
, &stats
) < 0)
754 u
= timespec_load(&stats
.st_mtim
);
760 log_debug("timestamp of '%s' changed", *i
);
762 /* update timestamp */
773 int fsck_exists(const char *fstype
) {
774 _cleanup_free_
char *p
= NULL
, *d
= NULL
;
778 checker
= strjoina("fsck.", fstype
);
780 r
= find_binary(checker
, true, &p
);
784 /* An fsck that is linked to /bin/true is a non-existent
787 r
= readlink_malloc(p
, &d
);
789 (path_equal(d
, "/bin/true") ||
790 path_equal(d
, "/usr/bin/true") ||
791 path_equal(d
, "/dev/null")))