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>
30 /* When we include libgen.h because we need dirname() we immediately
31 * undefine basename() since libgen.h defines it as a macro to the
32 * POSIX version which is really broken. We prefer GNU basename(). */
36 #include "alloc-util.h"
43 #include "parse-util.h"
44 #include "path-util.h"
45 #include "stat-util.h"
46 #include "string-util.h"
50 bool path_is_absolute(const char *p) {
54 bool is_path(const char *p) {
55 return !!strchr(p, '/');
58 /// UNNEEDED by elogind
60 int path_split_and_make_absolute(const char *p, char ***ret) {
67 l = strv_split(p, ":");
71 r = path_strv_make_absolute_cwd(l);
81 char *path_make_absolute(const char *p, const char *prefix) {
84 /* Makes every item in the list an absolute path by prepending
85 * the prefix, if specified and necessary */
87 if (path_is_absolute(p) || !prefix)
90 return strjoin(prefix, "/", p, NULL);
94 int path_make_absolute_cwd(const char *p, char **ret) {
100 /* Similar to path_make_absolute(), but prefixes with the
101 * current working directory. */
103 if (path_is_absolute(p))
106 _cleanup_free_ char *cwd = NULL;
108 cwd = get_current_dir_name();
112 c = strjoin(cwd, "/", p, NULL);
121 /// UNNEEDED by elogind
123 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
131 /* Strips the common part, and adds ".." elements as necessary. */
133 if (!path_is_absolute(from_dir))
136 if (!path_is_absolute(to_path))
139 /* Skip the common part. */
144 from_dir += strspn(from_dir, "/");
145 to_path += strspn(to_path, "/");
149 /* from_dir equals to_path. */
152 /* from_dir is a parent directory of to_path. */
158 path_kill_slashes(r);
167 a = strcspn(from_dir, "/");
168 b = strcspn(to_path, "/");
173 if (memcmp(from_dir, to_path, a) != 0)
180 /* If we're here, then "from_dir" has one or more elements that need to
181 * be replaced with "..". */
183 /* Count the number of necessary ".." elements. */
184 for (n_parents = 0;;) {
185 from_dir += strspn(from_dir, "/");
190 from_dir += strcspn(from_dir, "/");
194 r = malloc(n_parents * 3 + strlen(to_path) + 1);
198 for (p = r; n_parents > 0; n_parents--, p += 3)
202 path_kill_slashes(r);
208 int path_strv_make_absolute_cwd(char **l) {
212 /* Goes through every item in the string list and makes it
213 * absolute. This works in place and won't rollback any
214 * changes on failure. */
219 r = path_make_absolute_cwd(*s, &t);
231 char **path_strv_resolve(char **l, const char *prefix) {
239 /* Goes through every item in the string list and canonicalize
240 * the path. This works in place and won't rollback any
241 * changes on failure. */
245 _cleanup_free_ char *orig = NULL;
247 if (!path_is_absolute(*s)) {
254 t = strappend(prefix, orig);
263 u = canonicalize_file_name(t);
265 if (errno == ENOENT) {
274 if (errno == ENOMEM || errno == 0)
283 x = path_startswith(u, prefix);
285 /* restore the slash if it was lost */
286 if (!startswith(x, "/"))
297 /* canonicalized path goes outside of
298 * prefix, keep the original path instead */
317 char **path_strv_resolve_uniq(char **l, const char *prefix) {
322 if (!path_strv_resolve(l, prefix))
328 char *path_kill_slashes(char *path) {
332 /* Removes redundant inner and trailing slashes. Modifies the
333 * passed string in-place.
335 * ///foo///bar/ becomes /foo/bar
338 for (f = path, t = path; *f; f++) {
353 /* Special rule, if we are talking of the root directory, a
354 trailing slash is good */
356 if (t == path && slash)
363 char* path_startswith(const char *path, const char *prefix) {
367 if ((path[0] == '/') != (prefix[0] == '/'))
373 path += strspn(path, "/");
374 prefix += strspn(prefix, "/");
382 a = strcspn(path, "/");
383 b = strcspn(prefix, "/");
388 if (memcmp(path, prefix, a) != 0)
396 int path_compare(const char *a, const char *b) {
402 /* A relative path and an abolute path must not compare as equal.
403 * Which one is sorted before the other does not really matter.
404 * Here a relative path is ordered before an absolute path. */
405 d = (a[0] == '/') - (b[0] == '/');
415 if (*a == 0 && *b == 0)
418 /* Order prefixes first: "/foo" before "/foo/bar" */
427 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
428 d = memcmp(a, b, MIN(j, k));
430 return (d > 0) - (d < 0); /* sign of d */
432 /* Sort "/foo/a" before "/foo/aaa" */
433 d = (j > k) - (j < k); /* sign of (j - k) */
442 bool path_equal(const char *a, const char *b) {
443 return path_compare(a, b) == 0;
446 bool path_equal_or_files_same(const char *a, const char *b) {
447 return path_equal(a, b) || files_same(a, b) > 0;
450 /// UNNEEDED by elogind
452 char* path_join(const char *root, const char *path, const char *rest) {
456 return strjoin(root, endswith(root, "/") ? "" : "/",
457 path[0] == '/' ? path+1 : path,
458 rest ? (endswith(path, "/") ? "" : "/") : NULL,
459 rest && rest[0] == '/' ? rest+1 : rest,
463 rest ? (endswith(path, "/") ? "" : "/") : NULL,
464 rest && rest[0] == '/' ? rest+1 : rest,
468 /// UNNEEDED by elogind
471 int find_binary(const char *name, char **ret) {
478 if (access(name, X_OK) < 0)
482 r = path_make_absolute_cwd(name, ret);
491 * Plain getenv, not secure_getenv, because we want
492 * to actually allow the user to pick the binary.
498 last_error = -ENOENT;
501 _cleanup_free_ char *j = NULL, *element = NULL;
503 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
509 if (!path_is_absolute(element))
512 j = strjoin(element, "/", name, NULL);
516 if (access(j, X_OK) >= 0) {
520 *ret = path_kill_slashes(j);
533 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
534 bool changed = false;
535 const char* const* i;
542 STRV_FOREACH(i, paths) {
546 if (stat(*i, &stats) < 0)
549 u = timespec_load(&stats.st_mtim);
555 log_debug("timestamp of '%s' changed", *i);
557 /* update timestamp */
568 static int binary_is_good(const char *binary) {
569 _cleanup_free_ char *p = NULL, *d = NULL;
572 r = find_binary(binary, &p);
578 /* An fsck that is linked to /bin/true is a non-existent
581 r = readlink_malloc(p, &d);
582 if (r == -EINVAL) /* not a symlink */
587 return !path_equal(d, "true") &&
588 !path_equal(d, "/bin/true") &&
589 !path_equal(d, "/usr/bin/true") &&
590 !path_equal(d, "/dev/null");
593 int fsck_exists(const char *fstype) {
598 if (streq(fstype, "auto"))
601 checker = strjoina("fsck.", fstype);
602 return binary_is_good(checker);
605 int mkfs_exists(const char *fstype) {
610 if (streq(fstype, "auto"))
613 mkfs = strjoina("mkfs.", fstype);
614 return binary_is_good(mkfs);
617 char *prefix_root(const char *root, const char *path) {
621 /* If root is passed, prefixes path with it. Otherwise returns
626 /* First, drop duplicate prefixing slashes from the path */
627 while (path[0] == '/' && path[1] == '/')
630 if (isempty(root) || path_equal(root, "/"))
633 l = strlen(root) + 1 + strlen(path) + 1;
641 while (p > n && p[-1] == '/')
651 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
656 * This function is intended to be used in command line
657 * parsers, to handle paths that are passed in. It makes the
658 * path absolute, and reduces it to NULL if omitted or
659 * root (the latter optionally).
661 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
662 * SUCCESS! Hence, do not pass in uninitialized pointers.
670 r = path_make_absolute_cwd(path, &p);
672 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
674 path_kill_slashes(p);
675 if (suppress_root && path_equal(p, "/"))
684 char* dirname_malloc(const char *path) {
685 char *d, *dir, *dir2;
705 bool filename_is_valid(const char *p) {
717 e = strchrnul(p, '/');
721 if (e - p > FILENAME_MAX)
727 bool path_is_safe(const char *p) {
732 if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
735 if (strlen(p)+1 > PATH_MAX)
738 /* The following two checks are not really dangerous, but hey, they still are confusing */
739 if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
748 char *file_in_same_dir(const char *path, const char *filename) {
755 /* This removes the last component of path and appends
756 * filename, unless the latter is absolute anyway or the
759 if (path_is_absolute(filename))
760 return strdup(filename);
762 e = strrchr(path, '/');
764 return strdup(filename);
766 k = strlen(filename);
767 ret = new(char, (e + 1 - path) + k + 1);
771 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
775 bool hidden_file_allow_backup(const char *filename) {
779 filename[0] == '.' ||
780 streq(filename, "lost+found") ||
781 streq(filename, "aquota.user") ||
782 streq(filename, "aquota.group") ||
783 endswith(filename, ".rpmnew") ||
784 endswith(filename, ".rpmsave") ||
785 endswith(filename, ".rpmorig") ||
786 endswith(filename, ".dpkg-old") ||
787 endswith(filename, ".dpkg-new") ||
788 endswith(filename, ".dpkg-tmp") ||
789 endswith(filename, ".dpkg-dist") ||
790 endswith(filename, ".dpkg-bak") ||
791 endswith(filename, ".dpkg-backup") ||
792 endswith(filename, ".dpkg-remove") ||
793 endswith(filename, ".swp");
796 bool hidden_file(const char *filename) {
799 if (endswith(filename, "~"))
802 return hidden_file_allow_backup(filename);
805 /// UNNEEDED by elogind
807 bool is_device_path(const char *path) {
809 /* Returns true on paths that refer to a device, either in
810 * sysfs or in /dev */
813 path_startswith(path, "/dev/") ||
814 path_startswith(path, "/sys/");