]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-util.c
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 strjoin(cwd
, "/", p
, NULL
);
135 int path_make_relative(const char *from_dir
, const char *to_path
, char **_r
) {
143 /* Strips the common part, and adds ".." elements as necessary. */
145 if (!path_is_absolute(from_dir
))
148 if (!path_is_absolute(to_path
))
151 /* Skip the common part. */
156 from_dir
+= strspn(from_dir
, "/");
157 to_path
+= strspn(to_path
, "/");
161 /* from_dir equals to_path. */
164 /* from_dir is a parent directory of to_path. */
170 path_kill_slashes(r
);
179 a
= strcspn(from_dir
, "/");
180 b
= strcspn(to_path
, "/");
185 if (memcmp(from_dir
, to_path
, a
) != 0)
192 /* If we're here, then "from_dir" has one or more elements that need to
193 * be replaced with "..". */
195 /* Count the number of necessary ".." elements. */
196 for (n_parents
= 0;;) {
197 from_dir
+= strspn(from_dir
, "/");
202 from_dir
+= strcspn(from_dir
, "/");
206 r
= malloc(n_parents
* 3 + strlen(to_path
) + 1);
210 for (p
= r
; n_parents
> 0; n_parents
--, p
+= 3)
214 path_kill_slashes(r
);
220 char **path_strv_make_absolute_cwd(char **l
) {
223 /* Goes through every item in the string list and makes it
224 * absolute. This works in place and won't rollback any
225 * changes on failure. */
230 t
= path_make_absolute_cwd(*s
);
241 char **path_strv_resolve(char **l
, const char *prefix
) {
249 /* Goes through every item in the string list and canonicalize
250 * the path. This works in place and won't rollback any
251 * changes on failure. */
255 _cleanup_free_
char *orig
= NULL
;
257 if (!path_is_absolute(*s
)) {
264 t
= strappend(prefix
, orig
);
273 u
= canonicalize_file_name(t
);
275 if (errno
== ENOENT
) {
284 if (errno
== ENOMEM
|| errno
== 0)
293 x
= path_startswith(u
, prefix
);
295 /* restore the slash if it was lost */
296 if (!startswith(x
, "/"))
307 /* canonicalized path goes outside of
308 * prefix, keep the original path instead */
326 char **path_strv_resolve_uniq(char **l
, const char *prefix
) {
331 if (!path_strv_resolve(l
, prefix
))
337 char *path_kill_slashes(char *path
) {
341 /* Removes redundant inner and trailing slashes. Modifies the
342 * passed string in-place.
344 * ///foo///bar/ becomes /foo/bar
347 for (f
= path
, t
= path
; *f
; f
++) {
362 /* Special rule, if we are talking of the root directory, a
363 trailing slash is good */
365 if (t
== path
&& slash
)
372 char* path_startswith(const char *path
, const char *prefix
) {
376 if ((path
[0] == '/') != (prefix
[0] == '/'))
382 path
+= strspn(path
, "/");
383 prefix
+= strspn(prefix
, "/");
391 a
= strcspn(path
, "/");
392 b
= strcspn(prefix
, "/");
397 if (memcmp(path
, prefix
, a
) != 0)
405 bool path_equal(const char *a
, const char *b
) {
409 if ((a
[0] == '/') != (b
[0] == '/'))
418 if (*a
== 0 && *b
== 0)
421 if (*a
== 0 || *b
== 0)
430 if (memcmp(a
, b
, j
) != 0)
438 char* path_join(const char *root
, const char *path
, const char *rest
) {
442 return strjoin(root
, endswith(root
, "/") ? "" : "/",
443 path
[0] == '/' ? path
+1 : path
,
444 rest
? (endswith(path
, "/") ? "" : "/") : NULL
,
445 rest
&& rest
[0] == '/' ? rest
+1 : rest
,
449 rest
? (endswith(path
, "/") ? "" : "/") : NULL
,
450 rest
&& rest
[0] == '/' ? rest
+1 : rest
,
454 int path_is_mount_point(const char *t
, bool allow_symlink
) {
456 union file_handle_union h
= {
457 .handle
.handle_bytes
= MAX_HANDLE_SZ
460 int mount_id
, mount_id_parent
;
461 _cleanup_free_
char *parent
= NULL
;
465 /* We are not actually interested in the file handles, but
466 * name_to_handle_at() also passes us the mount ID, hence use
467 * it but throw the handle away */
469 if (path_equal(t
, "/"))
472 r
= name_to_handle_at(AT_FDCWD
, t
, &h
.handle
, &mount_id
, allow_symlink
? AT_SYMLINK_FOLLOW
: 0);
474 if (IN_SET(errno
, ENOSYS
, EOPNOTSUPP
))
475 /* This kernel or file system does not support
476 * name_to_handle_at(), hence fallback to the
477 * traditional stat() logic */
486 r
= path_get_parent(t
, &parent
);
490 h
.handle
.handle_bytes
= MAX_HANDLE_SZ
;
491 r
= name_to_handle_at(AT_FDCWD
, parent
, &h
.handle
, &mount_id_parent
, 0);
493 /* The parent can't do name_to_handle_at() but the
494 * directory we are interested in can? If so, it must
495 * be a mount point */
496 if (errno
== EOPNOTSUPP
)
502 return mount_id
!= mount_id_parent
;
517 r
= path_get_parent(t
, &parent
);
521 r
= lstat(parent
, &b
);
525 return a
.st_dev
!= b
.st_dev
;
528 int path_is_read_only_fs(const char *path
) {
533 if (statvfs(path
, &st
) < 0)
536 if (st
.f_flag
& ST_RDONLY
)
539 /* On NFS, statvfs() might not reflect whether we can actually
540 * write to the remote share. Let's try again with
541 * access(W_OK) which is more reliable, at least sometimes. */
542 if (access(path
, W_OK
) < 0 && errno
== EROFS
)
548 int path_is_os_tree(const char *path
) {
552 /* We use /usr/lib/os-release as flag file if something is an OS */
553 p
= strappenda(path
, "/usr/lib/os-release");
559 /* Also check for the old location in /etc, just in case. */
560 p
= strappenda(path
, "/etc/os-release");
566 int find_binary(const char *name
, bool local
, char **filename
) {
570 if (local
&& access(name
, X_OK
) < 0)
576 p
= path_make_absolute_cwd(name
);
586 const char *word
, *state
;
590 * Plain getenv, not secure_getenv, because we want
591 * to actually allow the user to pick the binary.
593 path
= getenv("PATH");
597 FOREACH_WORD_SEPARATOR(word
, l
, path
, ":", state
) {
598 _cleanup_free_
char *p
= NULL
;
600 if (asprintf(&p
, "%.*s/%s", (int) l
, word
, name
) < 0)
603 if (access(p
, X_OK
) < 0)
607 *filename
= path_kill_slashes(p
);
618 bool paths_check_timestamp(const char* const* paths
, usec_t
*timestamp
, bool update
) {
619 bool changed
= false;
620 const char* const* i
;
627 STRV_FOREACH(i
, paths
) {
631 if (stat(*i
, &stats
) < 0)
634 u
= timespec_load(&stats
.st_mtim
);
640 log_debug("timestamp of '%s' changed", *i
);
642 /* update timestamp */
653 int fsck_exists(const char *fstype
) {
654 _cleanup_free_
char *p
= NULL
, *d
= NULL
;
658 checker
= strappenda("fsck.", fstype
);
660 r
= find_binary(checker
, true, &p
);
664 /* An fsck that is linked to /bin/true is a non-existent
667 r
= readlink_malloc(p
, &d
);
669 (path_equal(d
, "/bin/true") ||
670 path_equal(d
, "/usr/bin/true") ||
671 path_equal(d
, "/dev/null")))