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 char **path_strv_make_absolute_cwd(char **l) {
138 /* Goes through every item in the string list and makes it
139 * absolute. This works in place and won't rollback any
140 * changes on failure. */
145 t = path_make_absolute_cwd(*s);
156 char **path_strv_canonicalize_absolute(char **l, const char *prefix) {
164 /* Goes through every item in the string list and canonicalize
165 * the path. This works in place and won't rollback any
166 * changes on failure. */
170 _cleanup_free_ char *orig = NULL;
172 if (!path_is_absolute(*s)) {
179 t = strappend(prefix, orig);
188 u = canonicalize_file_name(t);
190 if (errno == ENOENT) {
199 if (errno == ENOMEM || errno == 0)
208 x = path_startswith(u, prefix);
210 /* restore the slash if it was lost */
211 if (!startswith(x, "/"))
222 /* canonicalized path goes outside of
223 * prefix, keep the original path instead */
241 char **path_strv_canonicalize_absolute_uniq(char **l, const char *prefix) {
246 if (!path_strv_canonicalize_absolute(l, prefix))
252 char *path_kill_slashes(char *path) {
256 /* Removes redundant inner and trailing slashes. Modifies the
257 * passed string in-place.
259 * ///foo///bar/ becomes /foo/bar
262 for (f = path, t = path; *f; f++) {
277 /* Special rule, if we are talking of the root directory, a
278 trailing slash is good */
280 if (t == path && slash)
287 char* path_startswith(const char *path, const char *prefix) {
291 if ((path[0] == '/') != (prefix[0] == '/'))
297 path += strspn(path, "/");
298 prefix += strspn(prefix, "/");
306 a = strcspn(path, "/");
307 b = strcspn(prefix, "/");
312 if (memcmp(path, prefix, a) != 0)
320 bool path_equal(const char *a, const char *b) {
324 if ((a[0] == '/') != (b[0] == '/'))
333 if (*a == 0 && *b == 0)
336 if (*a == 0 || *b == 0)
345 if (memcmp(a, b, j) != 0)
353 int path_is_mount_point(const char *t, bool allow_symlink) {
355 union file_handle_union h = {
356 .handle.handle_bytes = MAX_HANDLE_SZ
359 int mount_id, mount_id_parent;
364 /* We are not actually interested in the file handles, but
365 * name_to_handle_at() also passes us the mount ID, hence use
366 * it but throw the handle away */
368 if (path_equal(t, "/"))
371 r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
373 if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
374 /* This kernel or file system does not support
375 * name_to_handle_at(), hence fallback to the
376 * traditional stat() logic */
385 r = path_get_parent(t, &parent);
389 h.handle.handle_bytes = MAX_HANDLE_SZ;
390 r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0);
393 /* The parent can't do name_to_handle_at() but the
394 * directory we are interested in can? If so, it must
395 * be a mount point */
396 if (errno == EOPNOTSUPP)
402 return mount_id != mount_id_parent;
417 r = path_get_parent(t, &parent);
421 r = lstat(parent, &b);
426 return a.st_dev != b.st_dev;
429 int path_is_read_only_fs(const char *path) {
434 if (statvfs(path, &st) < 0)
437 return !!(st.f_flag & ST_RDONLY);
440 int path_is_os_tree(const char *path) {
444 /* We use /etc/os-release as flag file if something is an OS */
446 p = strappenda(path, "/etc/os-release");
449 return r < 0 ? 0 : 1;
452 int find_binary(const char *name, char **filename) {
455 if (strchr(name, '/')) {
456 if (access(name, X_OK) < 0)
462 p = path_make_absolute_cwd(name);
476 * Plain getenv, not secure_getenv, because we want
477 * to actually allow the user to pick the binary.
479 path = getenv("PATH");
483 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
484 _cleanup_free_ char *p = NULL;
486 if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
489 if (access(p, X_OK) < 0)
493 *filename = path_kill_slashes(p);
504 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
505 bool changed = false;
506 const char* const* i;
513 STRV_FOREACH(i, paths) {
517 if (stat(*i, &stats) < 0)
520 u = timespec_load(&stats.st_mtim);
526 log_debug("timestamp of '%s' changed", *i);
528 /* update timestamp */
539 int fsck_exists(const char *fstype) {
542 checker = strappenda("fsck.", fstype);
543 return find_binary(checker, NULL);