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 char *path_get_file_name(const char *p) {
60 int path_get_parent(const char *path, char **_r) {
61 const char *e, *a = NULL, *b = NULL, *p;
71 for (e = path; *e; e++) {
73 if (!slash && *e == '/') {
77 } else if (slash && *e != '/')
92 r = strndup(path, p-path);
101 char **path_split_and_make_absolute(const char *p) {
105 l = strv_split(p, ":");
109 if (!path_strv_make_absolute_cwd(l)) {
117 char *path_make_absolute(const char *p, const char *prefix) {
120 /* Makes every item in the list an absolute path by prepending
121 * the prefix, if specified and necessary */
123 if (path_is_absolute(p) || !prefix)
126 return strjoin(prefix, "/", p, NULL);
129 char *path_make_absolute_cwd(const char *p) {
130 _cleanup_free_ char *cwd = NULL;
134 /* Similar to path_make_absolute(), but prefixes with the
135 * current working directory. */
137 if (path_is_absolute(p))
140 cwd = get_current_dir_name();
144 return path_make_absolute(p, cwd);
147 char **path_strv_make_absolute_cwd(char **l) {
150 /* Goes through every item in the string list and makes it
151 * absolute. This works in place and won't rollback any
152 * changes on failure. */
157 t = path_make_absolute_cwd(*s);
168 char **path_strv_canonicalize(char **l) {
176 /* Goes through every item in the string list and canonicalize
177 * the path. This works in place and won't rollback any
178 * changes on failure. */
183 t = path_make_absolute_cwd(*s);
193 u = canonicalize_file_name(t);
199 if (errno == ENOMEM || !errno)
218 char **path_strv_canonicalize_uniq(char **l) {
222 if (!path_strv_canonicalize(l))
228 char *path_kill_slashes(char *path) {
232 /* Removes redundant inner and trailing slashes. Modifies the
233 * passed string in-place.
235 * ///foo///bar/ becomes /foo/bar
238 for (f = path, t = path; *f; f++) {
253 /* Special rule, if we are talking of the root directory, a
254 trailing slash is good */
256 if (t == path && slash)
263 char* path_startswith(const char *path, const char *prefix) {
267 if ((path[0] == '/') != (prefix[0] == '/'))
273 path += strspn(path, "/");
274 prefix += strspn(prefix, "/");
282 a = strcspn(path, "/");
283 b = strcspn(prefix, "/");
288 if (memcmp(path, prefix, a) != 0)
296 bool path_equal(const char *a, const char *b) {
300 if ((a[0] == '/') != (b[0] == '/'))
309 if (*a == 0 && *b == 0)
312 if (*a == 0 || *b == 0)
321 if (memcmp(a, b, j) != 0)
329 int path_is_mount_point(const char *t, bool allow_symlink) {
332 struct file_handle *h;
333 int mount_id, mount_id_parent;
336 /* We are not actually interested in the file handles, but
337 * name_to_handle_at() also passes us the mount ID, hence use
338 * it but throw the handle away */
340 if (path_equal(t, "/"))
343 h = alloca(MAX_HANDLE_SZ);
344 h->handle_bytes = MAX_HANDLE_SZ;
346 r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
348 if (errno == ENOSYS || errno == ENOTSUP)
349 /* This kernel or file system does not support
350 * name_to_handle_at(), hence fallback to the
351 * traditional stat() logic */
360 r = path_get_parent(t, &parent);
364 h->handle_bytes = MAX_HANDLE_SZ;
365 r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
369 /* The parent can't do name_to_handle_at() but the
370 * directory we are interested in can? If so, it must
371 * be a mount point */
372 if (errno == ENOTSUP)
378 return mount_id != mount_id_parent;
393 r = path_get_parent(t, &parent);
397 r = lstat(parent, &b);
403 return a.st_dev != b.st_dev;
406 int path_is_read_only_fs(const char *path) {
411 if (statvfs(path, &st) < 0)
414 return !!(st.f_flag & ST_RDONLY);
417 int path_is_os_tree(const char *path) {
421 /* We use /etc/os-release as flag file if something is an OS */
423 p = strappenda(path, "/etc/os-release");
426 return r < 0 ? 0 : 1;
429 int find_binary(const char *name, char **filename) {
433 if (strchr(name, '/')) {
436 if (path_is_absolute(name))
439 p = path_make_absolute_cwd(name);
451 * Plain getenv, not secure_getenv, because we want
452 * to actually allow the user to pick the binary.
454 path = getenv("PATH");
458 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
461 if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
464 if (access(p, X_OK) < 0) {
469 path_kill_slashes(p);
479 bool paths_check_timestamp(char **paths, usec_t *timestamp, bool update) {
480 bool changed = false;
488 STRV_FOREACH(i, paths) {
492 if (stat(*i, &stats) < 0)
495 u = timespec_load(&stats.st_mtim);
501 log_debug("timestamp of '%s' changed\n", *i);
503 /* update timestamp */