path-util.c revision e26d6ce517a49c246141ed20528614823c2f5799
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek This file is part of systemd.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek Copyright 2010-2012 Lennart Poettering
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek (at your option) any later version.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekbool path_is_absolute(const char *p) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek return p[0] == '/';
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekbool is_path(const char *p) {
61376f96a98291b5421bfd79a15ca4db50c2a3feZbigniew Jędrzejewski-Szmekint path_get_parent(const char *path, char **_r) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek const char *e, *a = NULL, *b = NULL, *p;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek for (e = path; *e; e++) {
61376f96a98291b5421bfd79a15ca4db50c2a3feZbigniew Jędrzejewski-Szmekchar **path_split_and_make_absolute(const char *p) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!path_strv_make_absolute_cwd(l)) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar *path_make_absolute(const char *p, const char *prefix) {
8cb4ab0058e51f1fba93683d145ef95f97c2fa86Lennart Poettering /* Makes every item in the list an absolute path by prepending
61376f96a98291b5421bfd79a15ca4db50c2a3feZbigniew Jędrzejewski-Szmek * the prefix, if specified and necessary */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek return strjoin(prefix, "/", p, NULL);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar *path_make_absolute_cwd(const char *p) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek _cleanup_free_ char *cwd = NULL;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Similar to path_make_absolute(), but prefixes with the
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * current working directory. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek return strjoin(cwd, "/", p, NULL);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekint path_make_relative(const char *from_dir, const char *to_path, char **_r) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Strips the common part, and adds ".." elements as necessary. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!path_is_absolute(from_dir))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Skip the common part. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek from_dir += strspn(from_dir, "/");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek to_path += strspn(to_path, "/");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* from_dir equals to_path. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* from_dir is a parent directory of to_path. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (memcmp(from_dir, to_path, a) != 0)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* If we're here, then "from_dir" has one or more elements that need to
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * be replaced with "..". */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Count the number of necessary ".." elements. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek from_dir += strspn(from_dir, "/");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek from_dir += strcspn(from_dir, "/");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = malloc(n_parents * 3 + strlen(to_path) + 1);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek for (p = r; n_parents > 0; n_parents--, p += 3)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar **path_strv_make_absolute_cwd(char **l) {
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering /* Goes through every item in the string list and makes it
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * absolute. This works in place and won't rollback any
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * changes on failure. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar **path_strv_resolve(char **l, const char *prefix) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Goes through every item in the string list and canonicalize
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * the path. This works in place and won't rollback any
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * changes on failure. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek _cleanup_free_ char *orig = NULL;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (errno == ENOMEM || errno == 0)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* restore the slash if it was lost */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* canonicalized path goes outside of
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * prefix, keep the original path instead */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar **path_strv_resolve_uniq(char **l, const char *prefix) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!path_strv_resolve(l, prefix))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar *path_kill_slashes(char *path) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Removes redundant inner and trailing slashes. Modifies the
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * passed string in-place.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * ///foo///bar/ becomes /foo/bar
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek for (f = path, t = path; *f; f++) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (*f == '/') {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Special rule, if we are talking of the root directory, a
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek trailing slash is good */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekchar* path_startswith(const char *path, const char *prefix) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if ((path[0] == '/') != (prefix[0] == '/'))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (memcmp(path, prefix, a) != 0)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekint path_compare(const char *a, const char *b) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* A relative path and an abolute path must not compare as equal.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * Which one is sorted before the other does not really matter.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * Here a relative path is ordered before an absolute path. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek d = (a[0] == '/') - (b[0] == '/');
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (*a == 0 && *b == 0)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Order prefixes first: "/foo" before "/foo/bar" */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek return (d > 0) - (d < 0); /* sign of d */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering /* Sort "/foo/a" before "/foo/aaa" */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering d = (j > k) - (j < k); /* sign of (j - k) */
2d62c530d2b4c2730abff715b7342f1402114513Lennart Poetteringbool path_equal(const char *a, const char *b) {
2d62c530d2b4c2730abff715b7342f1402114513Lennart Poettering return path_compare(a, b) == 0;
2d62c530d2b4c2730abff715b7342f1402114513Lennart Poetteringbool path_equal_or_files_same(const char *a, const char *b) {
2d62c530d2b4c2730abff715b7342f1402114513Lennart Poettering return path_equal(a, b) || files_same(a, b) > 0;
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poetteringchar* path_join(const char *root, const char *path, const char *rest) {
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering return strjoin(root, endswith(root, "/") ? "" : "/",
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering rest ? (endswith(path, "/") ? "" : "/") : NULL,
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering rest ? (endswith(path, "/") ? "" : "/") : NULL,
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poetteringstatic int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering if ((flags & AT_EMPTY_PATH) && isempty(filename))
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering xsprintf(path, "/proc/self/fdinfo/%i", fd);
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
94036de887ad5b0dc805abe38b5c1c58b57d9465Mantas Mikulėnas xsprintf(path, "/proc/self/fdinfo/%i", subfd);
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering if (!p) /* The mnt_id field is a relatively new addition */
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poetteringint fd_is_mount_point(int fd, const char *filename, int flags) {
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering bool nosupp = false, check_st_dev = true;
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering /* First we will try the name_to_handle_at() syscall, which
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * tells us the mount id and an opaque file "handle". It is
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * not supported everywhere though (kernel compile-time
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * option, not all file systems are hooked up). If it works
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * the mount id is usually good enough to tell us whether
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * something is a mount point.
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * If that didn't work we will try to read the mount id from
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * /proc/self/fdinfo/<fd>. This is almost as good as
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * name_to_handle_at(), however, does not return the the
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * opaque file handle. The opaque file handle is pretty useful
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * to detect the root directory, which we should always
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * consider a mount point. Hence we use this only as
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * fallback. Exporting the mnt_id in fdinfo is a pretty recent
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * kernel addition.
6a79c58603ea816a1b4fa1520397b4e138bc1ca0Lennart Poettering * As last fallback we do traditional fstat() based st_dev
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer * comparisons. This is how things were traditionally done,
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * but unionfs breaks breaks this since it exposes file
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer * systems with a variety of st_dev reported. Also, btrfs
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer * subvolumes have different st_dev, even though they aren't
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer * real mounts of their own. */
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer /* This kernel does not support name_to_handle_at()
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer * fall back to simpler logic. */
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer /* This kernel or file system does not support
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt * name_to_handle_at(), hence let's see if the
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * upper fs supports it (in which case it is a
602a41c22ac2df33b4b5e5083719c1cfaf58acf9Lennart Poettering * mount point), otherwise fallback to the
3c56cab44150ad47323970cfadfb0257c6305a74Ben Wolsieffer * traditional stat() logic */
if (nosupp)
goto fallback_fdinfo;
return -errno;
if (nosupp)
if (r == -EOPNOTSUPP)
goto fallback_fstat;
check_st_dev = false;
return -errno;
return -errno;
assert(t);
if (fd < 0)
return -errno;
return -errno;
/* We use /usr/lib/os-release as flag file if something is an OS */
return -errno;
if (filename) {
return -ENOMEM;
*filename = p;
const char *path;
size_t l;
if (!path)
return -ENOMEM;
if (filename) {
p = NULL;
return -ENOENT;
bool changed = false;
usec_t u;
if (*timestamp >= u)
if (update) {
*timestamp = u;
changed = true;
return changed;
const char *checker;
r = readlink_malloc(p, &d);
return -ENOENT;
size_t l;
path++;
n = new(char, l);
return NULL;