Let's use chase_symlinks() everywhere, and stop using GNU
canonicalize_file_name() everywhere. For most cases this should not change
behaviour, however increase exposure of our function to get better tested. Most
importantly in a few cases (most notably nspawn) it can take the correct root
directory into account when chasing symlinks.
-int readlink_and_canonicalize(const char *p, char **r) {
+int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
- j = readlink_and_make_absolute(p, &t);
- if (j < 0)
- return j;
+ r = readlink_and_make_absolute(p, &t);
+ if (r < 0)
+ return r;
- s = canonicalize_file_name(t);
- if (s) {
+ r = chase_symlinks(t, root, &s);
+ if (r < 0)
+ /* If we can't follow up, then let's return the original string, slightly cleaned up. */
+ *ret = path_kill_slashes(t);
+ else {
+ *ret = s;
- *r = s;
- } else
- *r = t;
-
- path_kill_slashes(*r);
#if 0 /// UNNEEDED by elogind
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
#if 0 /// UNNEEDED by elogind
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
-int readlink_and_canonicalize(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, const char *root, char **r);
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
#endif // 0
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
#endif // 0
fallback_fdinfo:
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
fallback_fdinfo:
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES))
goto fallback_fstat;
if (r < 0)
return r;
goto fallback_fstat;
if (r < 0)
return r;
}
/* flags can be AT_SYMLINK_FOLLOW or 0 */
}
/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, int flags) {
- _cleanup_close_ int fd = -1;
+int path_is_mount_point(const char *t, const char *root, int flags) {
_cleanup_free_ char *canonical = NULL, *parent = NULL;
_cleanup_free_ char *canonical = NULL, *parent = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
* /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
* look at needs to be /usr, not /. */
if (flags & AT_SYMLINK_FOLLOW) {
* /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
* look at needs to be /usr, not /. */
if (flags & AT_SYMLINK_FOLLOW) {
- canonical = canonicalize_file_name(t);
- if (!canonical)
- return -errno;
+ r = chase_symlinks(t, root, &canonical);
+ if (r < 0)
+ return r;
return r;
/* Deal with mount points that are obstructed by a later mount */
return r;
/* Deal with mount points that are obstructed by a later mount */
- r = path_is_mount_point(x, 0);
+ r = path_is_mount_point(x, NULL, 0);
if (r == -ENOENT || r == 0)
continue;
if (r < 0)
if (r == -ENOENT || r == 0)
continue;
if (r < 0)
"glusterfs\0"
"pvfs2\0" /* OrangeFS */
"ocfs2\0"
"glusterfs\0"
"pvfs2\0" /* OrangeFS */
"ocfs2\0"
#include "missing.h"
int fd_is_mount_point(int fd, const char *filename, int flags);
#include "missing.h"
int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, int flags);
+int path_is_mount_point(const char *path, const char *root, int flags);
#if 0 /// UNNEEDED by elogind
int repeat_unmount(const char *path, int flags);
#if 0 /// UNNEEDED by elogind
int repeat_unmount(const char *path, int flags);
-char **path_strv_resolve(char **l, const char *prefix) {
+char **path_strv_resolve(char **l, const char *root) {
char **s;
unsigned k = 0;
bool enomem = false;
char **s;
unsigned k = 0;
bool enomem = false;
if (strv_isempty(l))
return l;
if (strv_isempty(l))
return l;
* changes on failure. */
STRV_FOREACH(s, l) {
* changes on failure. */
STRV_FOREACH(s, l) {
_cleanup_free_ char *orig = NULL;
_cleanup_free_ char *orig = NULL;
if (!path_is_absolute(*s)) {
free(*s);
continue;
}
if (!path_is_absolute(*s)) {
free(*s);
continue;
}
- t = strappend(prefix, orig);
+ t = prefix_root(root, orig);
if (!t) {
enomem = true;
continue;
if (!t) {
enomem = true;
continue;
- errno = 0;
- u = canonicalize_file_name(t);
- if (!u) {
- if (errno == ENOENT) {
- if (prefix) {
- u = orig;
- orig = NULL;
- free(t);
- } else
- u = t;
- } else {
+ r = chase_symlinks(t, root, &u);
+ if (r == -ENOENT) {
+ if (root) {
+ u = orig;
+ orig = NULL;
- if (errno == ENOMEM || errno == 0)
- enomem = true;
+ } else
+ u = t;
+ } else if (r < 0) {
+ free(t);
- continue;
- }
- } else if (prefix) {
+ if (r == -ENOMEM)
+ enomem = true;
+
+ continue;
+ } else if (root) {
- x = path_startswith(u, prefix);
+ x = path_startswith(u, root);
if (x) {
/* restore the slash if it was lost */
if (!startswith(x, "/"))
if (x) {
/* restore the slash if it was lost */
if (!startswith(x, "/"))
-char **path_strv_resolve_uniq(char **l, const char *prefix) {
+char **path_strv_resolve_uniq(char **l, const char *root) {
if (strv_isempty(l))
return l;
if (strv_isempty(l))
return l;
- if (!path_strv_resolve(l, prefix))
+ if (!path_strv_resolve(l, root))
return NULL;
return strv_uniq(l);
return NULL;
return strv_uniq(l);
assert(path);
assert(prefix);
assert(path);
assert(prefix);
+ /* Returns a pointer to the start of the first component after the parts matched by
+ * the prefix, iff
+ * - both paths are absolute or both paths are relative,
+ * and
+ * - each component in prefix in turn matches a component in path at the same position.
+ * An empty string will be returned when the prefix and path are equivalent.
+ *
+ * Returns NULL otherwise.
+ */
+
if ((path[0] == '/') != (prefix[0] == '/'))
return NULL;
if ((path[0] == '/') != (prefix[0] == '/'))
return NULL;
#if 0 /// UNNEEDED by elogind
int path_strv_make_absolute_cwd(char **l);
#endif // 0
#if 0 /// UNNEEDED by elogind
int path_strv_make_absolute_cwd(char **l);
#endif // 0
-char** path_strv_resolve(char **l, const char *prefix);
-char** path_strv_resolve_uniq(char **l, const char *prefix);
+char** path_strv_resolve(char **l, const char *root);
+char** path_strv_resolve_uniq(char **l, const char *root);
#if 0 /// UNNEEDED by elogind
int find_binary(const char *name, char **filename);
#if 0 /// UNNEEDED by elogind
int find_binary(const char *name, char **filename);
if (relabel)
(void) label_fix(p->where, true, true);
if (relabel)
(void) label_fix(p->where, true, true);
- r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
if (r < 0 && r != -ENOENT) {
log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
return (p->mode & MNT_FATAL) ? r : 0;
if (r < 0 && r != -ENOENT) {
log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
return (p->mode & MNT_FATAL) ? r : 0;
if (r < 0)
return log_error_errno(r, "Failed to create /run/user: %m");
if (r < 0)
return log_error_errno(r, "Failed to create /run/user: %m");
- if (path_is_mount_point(u->runtime_path, 0) <= 0) {
+ if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) {
_cleanup_free_ char *t = NULL;
(void) mkdir_label(u->runtime_path, 0700);
_cleanup_free_ char *t = NULL;
(void) mkdir_label(u->runtime_path, 0700);