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/>.
31 #include <sys/statvfs.h>
37 #include "path-util.h"
40 bool path_is_absolute(const char *p) {
44 bool is_path(const char *p) {
45 return !!strchr(p, '/');
48 int path_get_parent(const char *path, char **_r) {
49 const char *e, *a = NULL, *b = NULL, *p;
59 for (e = path; *e; e++) {
61 if (!slash && *e == '/') {
65 } else if (slash && *e != '/')
80 r = strndup(path, p-path);
89 char **path_split_and_make_absolute(const char *p) {
93 l = strv_split(p, ":");
97 if (!path_strv_make_absolute_cwd(l)) {
105 char *path_make_absolute(const char *p, const char *prefix) {
108 /* Makes every item in the list an absolute path by prepending
109 * the prefix, if specified and necessary */
111 if (path_is_absolute(p) || !prefix)
114 return strjoin(prefix, "/", p, NULL);
117 char *path_make_absolute_cwd(const char *p) {
118 _cleanup_free_ char *cwd = NULL;
122 /* Similar to path_make_absolute(), but prefixes with the
123 * current working directory. */
125 if (path_is_absolute(p))
128 cwd = get_current_dir_name();
132 return path_make_absolute(p, cwd);
135 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
144 /* Strips the common part, and adds ".." elements as necessary. */
146 if (!path_is_absolute(from_dir))
149 if (!path_is_absolute(to_path))
152 /* Skip the common part. */
157 from_dir += strspn(from_dir, "/");
158 to_path += strspn(to_path, "/");
162 /* from_dir equals to_path. */
165 /* from_dir is a parent directory of to_path. */
178 a = strcspn(from_dir, "/");
179 b = strcspn(to_path, "/");
184 if (memcmp(from_dir, to_path, a) != 0)
191 /* If we're here, then "from_dir" has one or more elements that need to
192 * be replaced with "..". */
194 /* Count the number of necessary ".." elements. */
195 for (n_parents = 0;;) {
196 from_dir += strspn(from_dir, "/");
201 from_dir += strcspn(from_dir, "/");
205 to_path_len = strlen(to_path);
207 r = malloc(n_parents * 3 + to_path_len);
211 for (p = r; n_parents > 0; n_parents--, p += 3)
215 memcpy(p, to_path, to_path_len);
217 /* "to_path" is a parent directory of "from_dir". Let's remove
218 * the redundant slash from the end of the result. */
225 char **path_strv_make_absolute_cwd(char **l) {
228 /* Goes through every item in the string list and makes it
229 * absolute. This works in place and won't rollback any
230 * changes on failure. */
235 t = path_make_absolute_cwd(*s);
246 char **path_strv_canonicalize_absolute(char **l, const char *prefix) {
254 /* Goes through every item in the string list and canonicalize
255 * the path. This works in place and won't rollback any
256 * changes on failure. */
260 _cleanup_free_ char *orig = NULL;
262 if (!path_is_absolute(*s)) {
269 t = strappend(prefix, orig);
278 u = canonicalize_file_name(t);
280 if (errno == ENOENT) {
289 if (errno == ENOMEM || errno == 0)
298 x = path_startswith(u, prefix);
300 /* restore the slash if it was lost */
301 if (!startswith(x, "/"))
312 /* canonicalized path goes outside of
313 * prefix, keep the original path instead */
331 char **path_strv_canonicalize_absolute_uniq(char **l, const char *prefix) {
336 if (!path_strv_canonicalize_absolute(l, prefix))
342 char *path_kill_slashes(char *path) {
346 /* Removes redundant inner and trailing slashes. Modifies the
347 * passed string in-place.
349 * ///foo///bar/ becomes /foo/bar
352 for (f = path, t = path; *f; f++) {
367 /* Special rule, if we are talking of the root directory, a
368 trailing slash is good */
370 if (t == path && slash)
377 char* path_startswith(const char *path, const char *prefix) {
381 if ((path[0] == '/') != (prefix[0] == '/'))
387 path += strspn(path, "/");
388 prefix += strspn(prefix, "/");
396 a = strcspn(path, "/");
397 b = strcspn(prefix, "/");
402 if (memcmp(path, prefix, a) != 0)
410 bool path_equal(const char *a, const char *b) {
414 if ((a[0] == '/') != (b[0] == '/'))
423 if (*a == 0 && *b == 0)
426 if (*a == 0 || *b == 0)
435 if (memcmp(a, b, j) != 0)
443 int path_is_mount_point(const char *t, bool allow_symlink) {
445 union file_handle_union h = {
446 .handle.handle_bytes = MAX_HANDLE_SZ
449 int mount_id, mount_id_parent;
454 /* We are not actually interested in the file handles, but
455 * name_to_handle_at() also passes us the mount ID, hence use
456 * it but throw the handle away */
458 if (path_equal(t, "/"))
461 r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
463 if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
464 /* This kernel or file system does not support
465 * name_to_handle_at(), hence fallback to the
466 * traditional stat() logic */
475 r = path_get_parent(t, &parent);
479 h.handle.handle_bytes = MAX_HANDLE_SZ;
480 r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0);
483 /* The parent can't do name_to_handle_at() but the
484 * directory we are interested in can? If so, it must
485 * be a mount point */
486 if (errno == EOPNOTSUPP)
492 return mount_id != mount_id_parent;
507 r = path_get_parent(t, &parent);
511 r = lstat(parent, &b);
516 return a.st_dev != b.st_dev;
519 int path_is_read_only_fs(const char *path) {
524 if (statvfs(path, &st) < 0)
527 return !!(st.f_flag & ST_RDONLY);
530 int path_is_os_tree(const char *path) {
534 /* We use /etc/os-release as flag file if something is an OS */
536 p = strappenda(path, "/etc/os-release");
539 return r < 0 ? 0 : 1;
542 int find_binary(const char *name, char **filename) {
545 if (strchr(name, '/')) {
546 if (access(name, X_OK) < 0)
552 p = path_make_absolute_cwd(name);
566 * Plain getenv, not secure_getenv, because we want
567 * to actually allow the user to pick the binary.
569 path = getenv("PATH");
573 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
574 _cleanup_free_ char *p = NULL;
576 if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
579 if (access(p, X_OK) < 0)
583 *filename = path_kill_slashes(p);
594 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
595 bool changed = false;
596 const char* const* i;
603 STRV_FOREACH(i, paths) {
607 if (stat(*i, &stats) < 0)
610 u = timespec_load(&stats.st_mtim);
616 log_debug("timestamp of '%s' changed", *i);
618 /* update timestamp */
629 int fsck_exists(const char *fstype) {
632 checker = strappenda("fsck.", fstype);
633 return find_binary(checker, NULL);