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"
37 bool path_is_absolute(const char *p) {
41 bool is_path(const char *p) {
42 return !!strchr(p, '/');
45 int path_get_parent(const char *path, char **_r) {
46 const char *e, *a = NULL, *b = NULL, *p;
56 for (e = path; *e; e++) {
58 if (!slash && *e == '/') {
62 } else if (slash && *e != '/')
77 r = strndup(path, p-path);
86 char **path_split_and_make_absolute(const char *p) {
90 l = strv_split(p, ":");
94 if (!path_strv_make_absolute_cwd(l)) {
102 char *path_make_absolute(const char *p, const char *prefix) {
105 /* Makes every item in the list an absolute path by prepending
106 * the prefix, if specified and necessary */
108 if (path_is_absolute(p) || !prefix)
111 return strjoin(prefix, "/", p, NULL);
114 char *path_make_absolute_cwd(const char *p) {
115 _cleanup_free_ char *cwd = NULL;
119 /* Similar to path_make_absolute(), but prefixes with the
120 * current working directory. */
122 if (path_is_absolute(p))
125 cwd = get_current_dir_name();
129 return strjoin(cwd, "/", p, NULL);
132 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
140 /* Strips the common part, and adds ".." elements as necessary. */
142 if (!path_is_absolute(from_dir))
145 if (!path_is_absolute(to_path))
148 /* Skip the common part. */
153 from_dir += strspn(from_dir, "/");
154 to_path += strspn(to_path, "/");
158 /* from_dir equals to_path. */
161 /* from_dir is a parent directory of to_path. */
167 path_kill_slashes(r);
176 a = strcspn(from_dir, "/");
177 b = strcspn(to_path, "/");
182 if (memcmp(from_dir, to_path, a) != 0)
189 /* If we're here, then "from_dir" has one or more elements that need to
190 * be replaced with "..". */
192 /* Count the number of necessary ".." elements. */
193 for (n_parents = 0;;) {
194 from_dir += strspn(from_dir, "/");
199 from_dir += strcspn(from_dir, "/");
203 r = malloc(n_parents * 3 + strlen(to_path) + 1);
207 for (p = r; n_parents > 0; n_parents--, p += 3)
211 path_kill_slashes(r);
217 char **path_strv_make_absolute_cwd(char **l) {
220 /* Goes through every item in the string list and makes it
221 * absolute. This works in place and won't rollback any
222 * changes on failure. */
227 t = path_make_absolute_cwd(*s);
238 char **path_strv_resolve(char **l, const char *prefix) {
246 /* Goes through every item in the string list and canonicalize
247 * the path. This works in place and won't rollback any
248 * changes on failure. */
252 _cleanup_free_ char *orig = NULL;
254 if (!path_is_absolute(*s)) {
261 t = strappend(prefix, orig);
270 u = canonicalize_file_name(t);
272 if (errno == ENOENT) {
281 if (errno == ENOMEM || errno == 0)
290 x = path_startswith(u, prefix);
292 /* restore the slash if it was lost */
293 if (!startswith(x, "/"))
304 /* canonicalized path goes outside of
305 * prefix, keep the original path instead */
324 char **path_strv_resolve_uniq(char **l, const char *prefix) {
329 if (!path_strv_resolve(l, prefix))
335 char *path_kill_slashes(char *path) {
339 /* Removes redundant inner and trailing slashes. Modifies the
340 * passed string in-place.
342 * ///foo///bar/ becomes /foo/bar
345 for (f = path, t = path; *f; f++) {
360 /* Special rule, if we are talking of the root directory, a
361 trailing slash is good */
363 if (t == path && slash)
370 char* path_startswith(const char *path, const char *prefix) {
374 if ((path[0] == '/') != (prefix[0] == '/'))
380 path += strspn(path, "/");
381 prefix += strspn(prefix, "/");
389 a = strcspn(path, "/");
390 b = strcspn(prefix, "/");
395 if (memcmp(path, prefix, a) != 0)
403 int path_compare(const char *a, const char *b) {
409 /* A relative path and an abolute path must not compare as equal.
410 * Which one is sorted before the other does not really matter.
411 * Here a relative path is ordered before an absolute path. */
412 d = (a[0] == '/') - (b[0] == '/');
422 if (*a == 0 && *b == 0)
425 /* Order prefixes first: "/foo" before "/foo/bar" */
434 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
435 d = memcmp(a, b, MIN(j, k));
437 return (d > 0) - (d < 0); /* sign of d */
439 /* Sort "/foo/a" before "/foo/aaa" */
440 d = (j > k) - (j < k); /* sign of (j - k) */
449 bool path_equal(const char *a, const char *b) {
450 return path_compare(a, b) == 0;
453 bool path_equal_or_files_same(const char *a, const char *b) {
454 return path_equal(a, b) || files_same(a, b) > 0;
457 char* path_join(const char *root, const char *path, const char *rest) {
461 return strjoin(root, endswith(root, "/") ? "" : "/",
462 path[0] == '/' ? path+1 : path,
463 rest ? (endswith(path, "/") ? "" : "/") : NULL,
464 rest && rest[0] == '/' ? rest+1 : rest,
468 rest ? (endswith(path, "/") ? "" : "/") : NULL,
469 rest && rest[0] == '/' ? rest+1 : rest,
473 int fd_is_mount_point(int fd) {
474 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
475 int mount_id = -1, mount_id_parent = -1;
482 /* We are not actually interested in the file handles, but
483 * name_to_handle_at() also passes us the mount ID, hence use
484 * it but throw the handle away */
486 r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
489 /* This kernel does not support name_to_handle_at()
490 * fall back to the traditional stat() logic. */
492 else if (errno == EOPNOTSUPP)
493 /* This kernel or file system does not support
494 * name_to_handle_at(), hence let's see if the
495 * upper fs supports it (in which case it is a
496 * mount point), otherwise fallback to the
497 * traditional stat() logic */
499 else if (errno == ENOENT)
505 r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
507 if (errno == EOPNOTSUPP) {
509 /* Neither parent nor child do name_to_handle_at()?
510 We have no choice but to fall back. */
513 /* The parent can't do name_to_handle_at() but the
514 * directory we are interested in can?
515 * If so, it must be a mount point. */
520 /* The parent can do name_to_handle_at() but the
521 * directory we are interested in can't? If so, it
522 * must be a mount point. */
525 /* If the file handle for the directory we are
526 * interested in and its parent are identical, we
527 * assume this is the root directory, which is a mount
530 if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
531 h.handle.handle_type == h_parent.handle.handle_type &&
532 memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
535 return mount_id != mount_id_parent;
539 r = fstatat(fd, "", &a, AT_EMPTY_PATH);
547 r = fstatat(fd, "..", &b, 0);
551 /* A directory with same device and inode as its parent? Must
552 * be the root directory */
553 if (a.st_dev == b.st_dev &&
554 a.st_ino == b.st_ino)
557 return a.st_dev != b.st_dev;
560 int path_is_mount_point(const char *t, bool allow_symlink) {
561 _cleanup_close_ int fd = -1;
564 if (path_equal(t, "/"))
567 fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
575 return fd_is_mount_point(fd);
578 int path_is_read_only_fs(const char *path) {
583 if (statvfs(path, &st) < 0)
586 if (st.f_flag & ST_RDONLY)
589 /* On NFS, statvfs() might not reflect whether we can actually
590 * write to the remote share. Let's try again with
591 * access(W_OK) which is more reliable, at least sometimes. */
592 if (access(path, W_OK) < 0 && errno == EROFS)
598 int path_is_os_tree(const char *path) {
602 /* We use /usr/lib/os-release as flag file if something is an OS */
603 p = strjoina(path, "/usr/lib/os-release");
609 /* Also check for the old location in /etc, just in case. */
610 p = strjoina(path, "/etc/os-release");
616 int find_binary(const char *name, bool local, char **filename) {
620 if (local && access(name, X_OK) < 0)
626 p = path_make_absolute_cwd(name);
636 const char *word, *state;
640 * Plain getenv, not secure_getenv, because we want
641 * to actually allow the user to pick the binary.
643 path = getenv("PATH");
647 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
648 _cleanup_free_ char *p = NULL;
650 if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
653 if (access(p, X_OK) < 0)
657 *filename = path_kill_slashes(p);
668 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
669 bool changed = false;
670 const char* const* i;
677 STRV_FOREACH(i, paths) {
681 if (stat(*i, &stats) < 0)
684 u = timespec_load(&stats.st_mtim);
690 log_debug("timestamp of '%s' changed", *i);
692 /* update timestamp */
703 int fsck_exists(const char *fstype) {
704 _cleanup_free_ char *p = NULL, *d = NULL;
708 checker = strjoina("fsck.", fstype);
710 r = find_binary(checker, true, &p);
714 /* An fsck that is linked to /bin/true is a non-existent
717 r = readlink_malloc(p, &d);
719 (path_equal(d, "/bin/true") ||
720 path_equal(d, "/usr/bin/true") ||
721 path_equal(d, "/dev/null")))