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/>.
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"
37 #include "extract-word.h"
42 #include "path-util.h"
43 #include "stat-util.h"
44 #include "string-util.h"
46 #include "time-util.h"
48 bool path_is_absolute(const char *p) {
52 bool is_path(const char *p) {
53 return !!strchr(p, '/');
56 #if 0 /// UNNEEDED by elogind
57 int path_split_and_make_absolute(const char *p, char ***ret) {
64 l = strv_split(p, ":");
68 r = path_strv_make_absolute_cwd(l);
78 char *path_make_absolute(const char *p, const char *prefix) {
81 /* Makes every item in the list an absolute path by prepending
82 * the prefix, if specified and necessary */
84 if (path_is_absolute(p) || !prefix)
87 return strjoin(prefix, "/", p, NULL);
91 int path_make_absolute_cwd(const char *p, char **ret) {
97 /* Similar to path_make_absolute(), but prefixes with the
98 * current working directory. */
100 if (path_is_absolute(p))
103 _cleanup_free_ char *cwd = NULL;
105 cwd = get_current_dir_name();
109 c = strjoin(cwd, "/", p, NULL);
118 #if 0 /// UNNEEDED by elogind
119 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
127 /* Strips the common part, and adds ".." elements as necessary. */
129 if (!path_is_absolute(from_dir))
132 if (!path_is_absolute(to_path))
135 /* Skip the common part. */
140 from_dir += strspn(from_dir, "/");
141 to_path += strspn(to_path, "/");
145 /* from_dir equals to_path. */
148 /* from_dir is a parent directory of to_path. */
154 path_kill_slashes(r);
163 a = strcspn(from_dir, "/");
164 b = strcspn(to_path, "/");
169 if (memcmp(from_dir, to_path, a) != 0)
176 /* If we're here, then "from_dir" has one or more elements that need to
177 * be replaced with "..". */
179 /* Count the number of necessary ".." elements. */
180 for (n_parents = 0;;) {
181 from_dir += strspn(from_dir, "/");
186 from_dir += strcspn(from_dir, "/");
190 r = malloc(n_parents * 3 + strlen(to_path) + 1);
194 for (p = r; n_parents > 0; n_parents--, p += 3)
198 path_kill_slashes(r);
204 int path_strv_make_absolute_cwd(char **l) {
208 /* Goes through every item in the string list and makes it
209 * absolute. This works in place and won't rollback any
210 * changes on failure. */
215 r = path_make_absolute_cwd(*s, &t);
227 char **path_strv_resolve(char **l, const char *prefix) {
235 /* Goes through every item in the string list and canonicalize
236 * the path. This works in place and won't rollback any
237 * changes on failure. */
241 _cleanup_free_ char *orig = NULL;
243 if (!path_is_absolute(*s)) {
250 t = strappend(prefix, orig);
259 u = canonicalize_file_name(t);
261 if (errno == ENOENT) {
270 if (errno == ENOMEM || errno == 0)
279 x = path_startswith(u, prefix);
281 /* restore the slash if it was lost */
282 if (!startswith(x, "/"))
293 /* canonicalized path goes outside of
294 * prefix, keep the original path instead */
313 char **path_strv_resolve_uniq(char **l, const char *prefix) {
318 if (!path_strv_resolve(l, prefix))
324 char *path_kill_slashes(char *path) {
328 /* Removes redundant inner and trailing slashes. Modifies the
329 * passed string in-place.
331 * ///foo///bar/ becomes /foo/bar
334 for (f = path, t = path; *f; f++) {
349 /* Special rule, if we are talking of the root directory, a
350 trailing slash is good */
352 if (t == path && slash)
359 char* path_startswith(const char *path, const char *prefix) {
363 if ((path[0] == '/') != (prefix[0] == '/'))
369 path += strspn(path, "/");
370 prefix += strspn(prefix, "/");
378 a = strcspn(path, "/");
379 b = strcspn(prefix, "/");
384 if (memcmp(path, prefix, a) != 0)
392 int path_compare(const char *a, const char *b) {
398 /* A relative path and an abolute path must not compare as equal.
399 * Which one is sorted before the other does not really matter.
400 * Here a relative path is ordered before an absolute path. */
401 d = (a[0] == '/') - (b[0] == '/');
411 if (*a == 0 && *b == 0)
414 /* Order prefixes first: "/foo" before "/foo/bar" */
423 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
424 d = memcmp(a, b, MIN(j, k));
426 return (d > 0) - (d < 0); /* sign of d */
428 /* Sort "/foo/a" before "/foo/aaa" */
429 d = (j > k) - (j < k); /* sign of (j - k) */
438 bool path_equal(const char *a, const char *b) {
439 return path_compare(a, b) == 0;
442 bool path_equal_or_files_same(const char *a, const char *b) {
443 return path_equal(a, b) || files_same(a, b) > 0;
446 #if 0 /// UNNEEDED by elogind
447 char* path_join(const char *root, const char *path, const char *rest) {
451 return strjoin(root, endswith(root, "/") ? "" : "/",
452 path[0] == '/' ? path+1 : path,
453 rest ? (endswith(path, "/") ? "" : "/") : NULL,
454 rest && rest[0] == '/' ? rest+1 : rest,
458 rest ? (endswith(path, "/") ? "" : "/") : NULL,
459 rest && rest[0] == '/' ? rest+1 : rest,
463 int find_binary(const char *name, char **ret) {
470 if (access(name, X_OK) < 0)
474 r = path_make_absolute_cwd(name, ret);
483 * Plain getenv, not secure_getenv, because we want
484 * to actually allow the user to pick the binary.
490 last_error = -ENOENT;
493 _cleanup_free_ char *j = NULL, *element = NULL;
495 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
501 if (!path_is_absolute(element))
504 j = strjoin(element, "/", name, NULL);
508 if (access(j, X_OK) >= 0) {
512 *ret = path_kill_slashes(j);
525 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
526 bool changed = false;
527 const char* const* i;
534 STRV_FOREACH(i, paths) {
538 if (stat(*i, &stats) < 0)
541 u = timespec_load(&stats.st_mtim);
547 log_debug("timestamp of '%s' changed", *i);
549 /* update timestamp */
560 static int binary_is_good(const char *binary) {
561 _cleanup_free_ char *p = NULL, *d = NULL;
564 r = find_binary(binary, &p);
570 /* An fsck that is linked to /bin/true is a non-existent
573 r = readlink_malloc(p, &d);
574 if (r == -EINVAL) /* not a symlink */
579 return !path_equal(d, "true") &&
580 !path_equal(d, "/bin/true") &&
581 !path_equal(d, "/usr/bin/true") &&
582 !path_equal(d, "/dev/null");
585 int fsck_exists(const char *fstype) {
590 if (streq(fstype, "auto"))
593 checker = strjoina("fsck.", fstype);
594 return binary_is_good(checker);
597 int mkfs_exists(const char *fstype) {
602 if (streq(fstype, "auto"))
605 mkfs = strjoina("mkfs.", fstype);
606 return binary_is_good(mkfs);
609 char *prefix_root(const char *root, const char *path) {
613 /* If root is passed, prefixes path with it. Otherwise returns
618 /* First, drop duplicate prefixing slashes from the path */
619 while (path[0] == '/' && path[1] == '/')
622 if (isempty(root) || path_equal(root, "/"))
625 l = strlen(root) + 1 + strlen(path) + 1;
633 while (p > n && p[-1] == '/')
643 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
648 * This function is intended to be used in command line
649 * parsers, to handle paths that are passed in. It makes the
650 * path absolute, and reduces it to NULL if omitted or
651 * root (the latter optionally).
653 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
654 * SUCCESS! Hence, do not pass in uninitialized pointers.
662 r = path_make_absolute_cwd(path, &p);
664 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
666 path_kill_slashes(p);
667 if (suppress_root && path_equal(p, "/"))
676 char* dirname_malloc(const char *path) {
677 char *d, *dir, *dir2;
697 bool filename_is_valid(const char *p) {
709 e = strchrnul(p, '/');
713 if (e - p > FILENAME_MAX)
719 bool path_is_safe(const char *p) {
724 if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
727 if (strlen(p)+1 > PATH_MAX)
730 /* The following two checks are not really dangerous, but hey, they still are confusing */
731 if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
740 char *file_in_same_dir(const char *path, const char *filename) {
747 /* This removes the last component of path and appends
748 * filename, unless the latter is absolute anyway or the
751 if (path_is_absolute(filename))
752 return strdup(filename);
754 e = strrchr(path, '/');
756 return strdup(filename);
758 k = strlen(filename);
759 ret = new(char, (e + 1 - path) + k + 1);
763 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
767 bool hidden_file_allow_backup(const char *filename) {
771 filename[0] == '.' ||
772 streq(filename, "lost+found") ||
773 streq(filename, "aquota.user") ||
774 streq(filename, "aquota.group") ||
775 endswith(filename, ".rpmnew") ||
776 endswith(filename, ".rpmsave") ||
777 endswith(filename, ".rpmorig") ||
778 endswith(filename, ".dpkg-old") ||
779 endswith(filename, ".dpkg-new") ||
780 endswith(filename, ".dpkg-tmp") ||
781 endswith(filename, ".dpkg-dist") ||
782 endswith(filename, ".dpkg-bak") ||
783 endswith(filename, ".dpkg-backup") ||
784 endswith(filename, ".dpkg-remove") ||
785 endswith(filename, ".swp");
788 bool hidden_file(const char *filename) {
791 if (endswith(filename, "~"))
794 return hidden_file_allow_backup(filename);
797 #if 0 /// UNNEEDED by elogind
798 bool is_device_path(const char *path) {
800 /* Returns true on paths that refer to a device, either in
801 * sysfs or in /dev */
804 path_startswith(path, "/dev/") ||
805 path_startswith(path, "/sys/");