mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
Merge pull request #4694 from poettering/chase-everywhere
tree-wide: stop using canonicalize_file_name(), use chase_symlinks() …
This commit is contained in:
commit
a32ffa161c
@ -429,3 +429,8 @@
|
||||
and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there
|
||||
aren't, we are happy to use GNU or Linux APIs, and expect non-GNU
|
||||
implementations of libc to catch up with glibc.
|
||||
|
||||
- Whenever installing a signal handler, make sure to set SA_RESTART for it, so
|
||||
that interrupted system calls are automatically restarted, and we minimize
|
||||
hassles with handling EINTR (in particular as EINTR handling is pretty broken
|
||||
on Linux).
|
||||
|
@ -740,21 +740,19 @@
|
||||
<term><option>--bind=</option></term>
|
||||
<term><option>--bind-ro=</option></term>
|
||||
|
||||
<listitem><para>Bind mount a file or directory from the host
|
||||
into the container. Takes one of: a path argument — in which
|
||||
case the specified path will be mounted from the host to the
|
||||
same path in the container —, or a colon-separated pair of
|
||||
paths — in which case the first specified path is the source
|
||||
in the host, and the second path is the destination in the
|
||||
container —, or a colon-separated triple of source path,
|
||||
destination path and mount options. Mount options are
|
||||
comma-separated and currently, only "rbind" and "norbind"
|
||||
are allowed. Defaults to "rbind". Backslash escapes are interpreted, so
|
||||
<literal>\:</literal> may be used to embed colons in either path.
|
||||
This option may be specified multiple times for
|
||||
creating multiple independent bind mount points. The
|
||||
<option>--bind-ro=</option> option creates read-only bind
|
||||
mounts.</para></listitem>
|
||||
<listitem><para>Bind mount a file or directory from the host into the container. Takes one of: a path
|
||||
argument — in which case the specified path will be mounted from the host to the same path in the container, or
|
||||
a colon-separated pair of paths — in which case the first specified path is the source in the host, and the
|
||||
second path is the destination in the container, or a colon-separated triple of source path, destination path
|
||||
and mount options. The source path may optionally be prefixed with a <literal>+</literal> character. If so, the
|
||||
source path is taken relative to the image's root directory. This permits setting up bind mounts within the
|
||||
container image. The source path may be specified as empty string, in which case a temporary directory below
|
||||
the host's <filename>/var/tmp</filename> directory is used. It is automatically removed when the container is
|
||||
shut down. Mount options are comma-separated and currently, only <option>rbind</option> and
|
||||
<option>norbind</option> are allowed, controlling whether to create a recursive or a regular bind
|
||||
mount. Defaults to "rbind". Backslash escapes are interpreted, so <literal>\:</literal> may be used to embed
|
||||
colons in either path. This option may be specified multiple times for creating multiple independent bind
|
||||
mount points. The <option>--bind-ro=</option> option creates read-only bind mounts.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -808,6 +806,14 @@
|
||||
point for the overlay file system in the container. At least
|
||||
two paths have to be specified.</para>
|
||||
|
||||
<para>The source paths may optionally be prefixed with <literal>+</literal> character. If so they are taken
|
||||
relative to the image's root directory. The uppermost source path may also be specified as empty string, in
|
||||
which case a temporary directory below the host's <filename>/var/tmp</filename> is used. The directory is
|
||||
removed automatically when the container is shut down. This behaviour is useful in order to make read-only
|
||||
container directories writable while the container is running. For example, use the
|
||||
<literal>--overlay=+/var::/var</literal> option in order to automatically overlay a writable temporary
|
||||
directory on a read-only <filename>/var</filename> directory.</para>
|
||||
|
||||
<para>For details about overlay file systems, see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt">overlayfs.txt</ulink>. Note
|
||||
that the semantics of overlay file systems are substantially
|
||||
|
@ -334,6 +334,17 @@
|
||||
is privileged (see above).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Overlay=</varname></term>
|
||||
<term><varname>OverlayReadOnly=</varname></term>
|
||||
|
||||
<listitem><para>Adds an overlay mount point. Takes a colon-separated list of paths. This option may be used
|
||||
multiple times to configure multiple overlay mounts. This option is equivalent to the command line switches
|
||||
<option>--overlay=</option> and <option>--overlay-ro=</option>, see
|
||||
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details
|
||||
about the specific options supported. This setting is privileged (see above).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PrivateUsersChown=</varname></term>
|
||||
|
||||
|
@ -339,7 +339,7 @@ static void sigchld_hdl(int sig) {
|
||||
|
||||
static int install_chld_handler(void) {
|
||||
static const struct sigaction act = {
|
||||
.sa_flags = SA_NOCLDSTOP,
|
||||
.sa_flags = SA_NOCLDSTOP|SA_RESTART,
|
||||
.sa_handler = sigchld_hdl,
|
||||
};
|
||||
|
||||
|
@ -224,25 +224,25 @@ int readlink_and_make_absolute(const char *p, char **r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int readlink_and_canonicalize(const char *p, char **r) {
|
||||
int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
|
||||
char *t, *s;
|
||||
int j;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(r);
|
||||
assert(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, 0, &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;
|
||||
free(t);
|
||||
*r = s;
|
||||
} else
|
||||
*r = t;
|
||||
|
||||
path_kill_slashes(*r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -598,10 +598,11 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int chase_symlinks(const char *path, const char *_root, char **ret) {
|
||||
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
|
||||
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
|
||||
bool exists = true;
|
||||
char *todo;
|
||||
int r;
|
||||
|
||||
@ -610,26 +611,39 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
|
||||
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
|
||||
* symlinks relative to a root directory, instead of the root of the host.
|
||||
*
|
||||
* Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly
|
||||
* this means the path parameter passed in is not prefixed by it.
|
||||
* Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
|
||||
* relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
|
||||
* assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
|
||||
* prefixed accordingly.
|
||||
*
|
||||
* Algorithmically this operates on two path buffers: "done" are the components of the path we already
|
||||
* processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
|
||||
* process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
|
||||
* each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
|
||||
* slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
|
||||
* at a minimum. */
|
||||
* at a minimum.
|
||||
*
|
||||
* Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
|
||||
* as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
|
||||
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
|
||||
* specified path.
|
||||
*
|
||||
* Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the
|
||||
* passed root. */
|
||||
|
||||
if (original_root) {
|
||||
r = path_make_absolute_cwd(original_root, &root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (flags & CHASE_PREFIX_ROOT)
|
||||
path = prefix_roota(root, path);
|
||||
}
|
||||
|
||||
r = path_make_absolute_cwd(path, &buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (_root) {
|
||||
r = path_make_absolute_cwd(_root, &root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
@ -665,18 +679,20 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
int fd_parent = -1;
|
||||
|
||||
/* If we already are at the top, then going up will not change anything. This is in-line with
|
||||
* how the kernel handles this. */
|
||||
if (isempty(done) || path_equal(done, "/"))
|
||||
return -EINVAL;
|
||||
continue;
|
||||
|
||||
parent = dirname_malloc(done);
|
||||
if (!parent)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Don't allow this to leave the root dir */
|
||||
/* Don't allow this to leave the root dir. */
|
||||
if (root &&
|
||||
path_startswith(done, root) &&
|
||||
!path_startswith(parent, root))
|
||||
return -EINVAL;
|
||||
continue;
|
||||
|
||||
free_and_replace(done, parent);
|
||||
|
||||
@ -692,8 +708,25 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
|
||||
|
||||
/* Otherwise let's see what this is. */
|
||||
child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
if (child < 0)
|
||||
if (child < 0) {
|
||||
|
||||
if (errno == ENOENT &&
|
||||
(flags & CHASE_NONEXISTENT) &&
|
||||
(isempty(todo) || path_is_safe(todo))) {
|
||||
|
||||
/* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
|
||||
* what we got so far. But don't allow this if the remaining path contains "../ or "./"
|
||||
* or something else weird. */
|
||||
|
||||
if (!strextend(&done, first, todo, NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (fstat(child, &st) < 0)
|
||||
return -errno;
|
||||
@ -778,5 +811,5 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
|
||||
*ret = done;
|
||||
done = NULL;
|
||||
|
||||
return 0;
|
||||
return exists;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret);
|
||||
int readlink_malloc(const char *p, char **r);
|
||||
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);
|
||||
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||
@ -78,4 +78,9 @@ union inotify_event_buffer {
|
||||
|
||||
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
|
||||
|
||||
int chase_symlinks(const char *path, const char *_root, char **ret);
|
||||
enum {
|
||||
CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
|
||||
CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
|
||||
};
|
||||
|
||||
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "mount-util.h"
|
||||
#include "parse-util.h"
|
||||
@ -205,9 +206,10 @@ fallback_fstat:
|
||||
}
|
||||
|
||||
/* 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_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
||||
@ -219,9 +221,9 @@ int path_is_mount_point(const char *t, int flags) {
|
||||
* /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, 0, &canonical);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = canonical;
|
||||
}
|
||||
@ -473,7 +475,7 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
|
||||
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)
|
||||
|
@ -30,7 +30,7 @@
|
||||
#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);
|
||||
|
||||
int repeat_unmount(const char *path, int flags);
|
||||
|
||||
|
@ -220,10 +220,11 @@ int path_strv_make_absolute_cwd(char **l) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int r;
|
||||
|
||||
if (strv_isempty(l))
|
||||
return l;
|
||||
@ -233,17 +234,17 @@ char **path_strv_resolve(char **l, const char *prefix) {
|
||||
* changes on failure. */
|
||||
|
||||
STRV_FOREACH(s, l) {
|
||||
char *t, *u;
|
||||
_cleanup_free_ char *orig = NULL;
|
||||
char *t, *u;
|
||||
|
||||
if (!path_is_absolute(*s)) {
|
||||
free(*s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
if (root) {
|
||||
orig = *s;
|
||||
t = strappend(prefix, orig);
|
||||
t = prefix_root(root, orig);
|
||||
if (!t) {
|
||||
enomem = true;
|
||||
continue;
|
||||
@ -251,28 +252,26 @@ char **path_strv_resolve(char **l, const char *prefix) {
|
||||
} else
|
||||
t = *s;
|
||||
|
||||
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, 0, &u);
|
||||
if (r == -ENOENT) {
|
||||
if (root) {
|
||||
u = orig;
|
||||
orig = NULL;
|
||||
free(t);
|
||||
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) {
|
||||
char *x;
|
||||
|
||||
free(t);
|
||||
x = path_startswith(u, prefix);
|
||||
x = path_startswith(u, root);
|
||||
if (x) {
|
||||
/* restore the slash if it was lost */
|
||||
if (!startswith(x, "/"))
|
||||
@ -304,12 +303,12 @@ char **path_strv_resolve(char **l, const char *prefix) {
|
||||
return l;
|
||||
}
|
||||
|
||||
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 (!path_strv_resolve(l, prefix))
|
||||
if (!path_strv_resolve(l, root))
|
||||
return NULL;
|
||||
|
||||
return strv_uniq(l);
|
||||
|
@ -66,8 +66,8 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
|
||||
})
|
||||
|
||||
int path_strv_make_absolute_cwd(char **l);
|
||||
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);
|
||||
|
||||
int find_binary(const char *name, char **filename);
|
||||
|
||||
|
@ -783,7 +783,7 @@ static int automount_start(Unit *u) {
|
||||
assert(a);
|
||||
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
|
||||
|
||||
if (path_is_mount_point(a->where, 0) > 0) {
|
||||
if (path_is_mount_point(a->where, NULL, 0) > 0) {
|
||||
log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
@ -359,7 +359,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
|
||||
fail:
|
||||
log_unit_warning_errno(u, r, "Failed to set up device unit: %m");
|
||||
|
||||
if (delete && u)
|
||||
if (delete)
|
||||
unit_free(u);
|
||||
|
||||
return r;
|
||||
|
@ -199,7 +199,7 @@ int machine_id_commit(const char *root) {
|
||||
|
||||
etc_machine_id = prefix_roota(root, "/etc/machine-id");
|
||||
|
||||
r = path_is_mount_point(etc_machine_id, 0);
|
||||
r = path_is_mount_point(etc_machine_id, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
|
||||
if (r == 0) {
|
||||
|
@ -159,7 +159,7 @@ static int mount_one(const MountPoint *p, bool relabel) {
|
||||
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;
|
||||
|
@ -1509,7 +1509,7 @@ static int mount_setup_unit(
|
||||
fail:
|
||||
log_warning_errno(r, "Failed to set up mount unit: %m");
|
||||
|
||||
if (delete && u)
|
||||
if (delete)
|
||||
unit_free(u);
|
||||
|
||||
return r;
|
||||
|
@ -596,7 +596,7 @@ static int apply_mount(
|
||||
case READONLY:
|
||||
case READWRITE:
|
||||
|
||||
r = path_is_mount_point(bind_mount_path(m), 0);
|
||||
r = path_is_mount_point(bind_mount_path(m), NULL, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", bind_mount_path(m));
|
||||
if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
|
||||
@ -665,7 +665,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned
|
||||
_cleanup_free_ char *chased = NULL;
|
||||
int k;
|
||||
|
||||
k = chase_symlinks(bind_mount_path(f), root_directory, &chased);
|
||||
k = chase_symlinks(bind_mount_path(f), root_directory, 0, &chased);
|
||||
if (k < 0) {
|
||||
/* Get only real errors */
|
||||
if (r >= 0 && (k != -ENOENT || !f->ignore))
|
||||
@ -860,7 +860,7 @@ int setup_namespace(
|
||||
|
||||
if (root_directory) {
|
||||
/* Turn directory into bind mount, if it isn't one yet */
|
||||
r = path_is_mount_point(root_directory, AT_SYMLINK_FOLLOW);
|
||||
r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0) {
|
||||
|
@ -420,7 +420,7 @@ static int swap_setup_unit(
|
||||
fail:
|
||||
log_unit_warning_errno(u, r, "Failed to load swap unit: %m");
|
||||
|
||||
if (delete && u)
|
||||
if (delete)
|
||||
unit_free(u);
|
||||
|
||||
return r;
|
||||
|
@ -516,7 +516,8 @@ void unit_free(Unit *u) {
|
||||
Iterator i;
|
||||
char *t;
|
||||
|
||||
assert(u);
|
||||
if (!u)
|
||||
return;
|
||||
|
||||
if (u->transient_file)
|
||||
fclose(u->transient_file);
|
||||
|
@ -87,14 +87,15 @@ static enum {
|
||||
|
||||
static int equivalent(const char *a, const char *b) {
|
||||
_cleanup_free_ char *x = NULL, *y = NULL;
|
||||
int r;
|
||||
|
||||
x = canonicalize_file_name(a);
|
||||
if (!x)
|
||||
return -errno;
|
||||
r = chase_symlinks(a, NULL, 0, &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
y = canonicalize_file_name(b);
|
||||
if (!y)
|
||||
return -errno;
|
||||
r = chase_symlinks(b, NULL, 0, &y);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return path_equal(x, y);
|
||||
}
|
||||
@ -360,7 +361,7 @@ static int should_skip_prefix(const char* p) {
|
||||
int r;
|
||||
_cleanup_free_ char *target = NULL;
|
||||
|
||||
r = chase_symlinks(p, NULL, &target);
|
||||
r = chase_symlinks(p, NULL, 0, &target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -252,7 +252,7 @@ static bool path_is_busy(const char *where) {
|
||||
int r;
|
||||
|
||||
/* already a mountpoint; generators run during reload */
|
||||
r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
|
||||
r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW);
|
||||
if (r > 0)
|
||||
return false;
|
||||
|
||||
|
@ -938,21 +938,21 @@ static int add_matches(sd_journal *j, char **args) {
|
||||
have_term = false;
|
||||
|
||||
} else if (path_is_absolute(*i)) {
|
||||
_cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL;
|
||||
const char *path;
|
||||
_cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL;
|
||||
struct stat st;
|
||||
|
||||
p = canonicalize_file_name(*i);
|
||||
path = p ?: *i;
|
||||
r = chase_symlinks(*i, NULL, 0, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Couldn't canonicalize path: %m");
|
||||
|
||||
if (lstat(path, &st) < 0)
|
||||
if (lstat(p, &st) < 0)
|
||||
return log_error_errno(errno, "Couldn't stat file: %m");
|
||||
|
||||
if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
|
||||
if (executable_is_script(path, &interpreter) > 0) {
|
||||
if (executable_is_script(p, &interpreter) > 0) {
|
||||
_cleanup_free_ char *comm;
|
||||
|
||||
comm = strndup(basename(path), 15);
|
||||
comm = strndup(basename(p), 15);
|
||||
if (!comm)
|
||||
return log_oom();
|
||||
|
||||
@ -968,7 +968,7 @@ static int add_matches(sd_journal *j, char **args) {
|
||||
return log_oom();
|
||||
}
|
||||
} else {
|
||||
t = strappend("_EXE=", path);
|
||||
t = strappend("_EXE=", p);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
}
|
||||
@ -979,7 +979,7 @@ static int add_matches(sd_journal *j, char **args) {
|
||||
r = sd_journal_add_match(j, t2, 0);
|
||||
|
||||
} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
|
||||
r = add_matches_for_device(j, path);
|
||||
r = add_matches_for_device(j, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
|
@ -164,7 +164,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
r = readlink_and_canonicalize(_syspath, &syspath);
|
||||
r = readlink_and_canonicalize(_syspath, NULL, &syspath);
|
||||
if (r == -ENOENT)
|
||||
/* the device does not exist (any more?) */
|
||||
return -ENODEV;
|
||||
|
@ -338,7 +338,7 @@ static int user_mkdir_runtime_path(User *u) {
|
||||
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);
|
||||
|
@ -33,6 +33,8 @@ Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings,
|
||||
Files.Bind, config_parse_bind, 0, 0
|
||||
Files.BindReadOnly, config_parse_bind, 1, 0
|
||||
Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
|
||||
Files.Overlay, config_parse_overlay, 0, 0
|
||||
Files.OverlayReadOnly, config_parse_overlay, 1, 0
|
||||
Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown)
|
||||
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
|
||||
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
|
||||
|
@ -47,7 +47,7 @@ CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) {
|
||||
assert(t >= 0);
|
||||
assert(t < _CUSTOM_MOUNT_TYPE_MAX);
|
||||
|
||||
c = realloc(*l, (*n + 1) * sizeof(CustomMount));
|
||||
c = realloc_multiply(*l, (*n + 1), sizeof(CustomMount));
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
@ -75,13 +75,18 @@ void custom_mount_free_all(CustomMount *l, unsigned n) {
|
||||
free(m->work_dir);
|
||||
}
|
||||
|
||||
if (m->rm_rf_tmpdir) {
|
||||
(void) rm_rf(m->rm_rf_tmpdir, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
free(m->rm_rf_tmpdir);
|
||||
}
|
||||
|
||||
strv_free(m->lower);
|
||||
}
|
||||
|
||||
free(l);
|
||||
}
|
||||
|
||||
int custom_mount_compare(const void *a, const void *b) {
|
||||
static int custom_mount_compare(const void *a, const void *b) {
|
||||
const CustomMount *x = a, *y = b;
|
||||
int r;
|
||||
|
||||
@ -97,6 +102,109 @@ int custom_mount_compare(const void *a, const void *b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool source_path_is_valid(const char *p) {
|
||||
assert(p);
|
||||
|
||||
if (*p == '+')
|
||||
p++;
|
||||
|
||||
return path_is_absolute(p);
|
||||
}
|
||||
|
||||
static char *resolve_source_path(const char *dest, const char *source) {
|
||||
|
||||
if (!source)
|
||||
return NULL;
|
||||
|
||||
if (source[0] == '+')
|
||||
return prefix_root(dest, source + 1);
|
||||
|
||||
return strdup(source);
|
||||
}
|
||||
|
||||
int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
/* Prepare all custom mounts. This will make source we know all temporary directories. This is called in the
|
||||
* parent process, so that we know the temporary directories to remove on exit before we fork off the
|
||||
* children. */
|
||||
|
||||
assert(l || n == 0);
|
||||
|
||||
/* Order the custom mounts, and make sure we have a working directory */
|
||||
qsort_safe(l, n, sizeof(CustomMount), custom_mount_compare);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
CustomMount *m = l + i;
|
||||
|
||||
if (m->source) {
|
||||
char *s;
|
||||
|
||||
s = resolve_source_path(dest, m->source);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
free(m->source);
|
||||
m->source = s;
|
||||
} else {
|
||||
/* No source specified? In that case, use a throw-away temporary directory in /var/tmp */
|
||||
|
||||
m->rm_rf_tmpdir = strdup("/var/tmp/nspawn-temp-XXXXXX");
|
||||
if (!m->rm_rf_tmpdir)
|
||||
return log_oom();
|
||||
|
||||
if (!mkdtemp(m->rm_rf_tmpdir)) {
|
||||
m->rm_rf_tmpdir = mfree(m->rm_rf_tmpdir);
|
||||
return log_error_errno(errno, "Failed to acquire temporary directory: %m");
|
||||
}
|
||||
|
||||
m->source = strjoin(m->rm_rf_tmpdir, "/src");
|
||||
if (!m->source)
|
||||
return log_oom();
|
||||
|
||||
if (mkdir(m->source, 0755) < 0)
|
||||
return log_error_errno(errno, "Failed to create %s: %m", m->source);
|
||||
}
|
||||
|
||||
if (m->type == CUSTOM_MOUNT_OVERLAY) {
|
||||
char **j;
|
||||
|
||||
STRV_FOREACH(j, m->lower) {
|
||||
char *s;
|
||||
|
||||
s = resolve_source_path(dest, *j);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
free(*j);
|
||||
*j = s;
|
||||
}
|
||||
|
||||
if (m->work_dir) {
|
||||
char *s;
|
||||
|
||||
s = resolve_source_path(dest, m->work_dir);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
free(m->work_dir);
|
||||
m->work_dir = s;
|
||||
} else {
|
||||
assert(m->source);
|
||||
|
||||
r = tempfn_random(m->source, NULL, &m->work_dir);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire working directory: %m");
|
||||
}
|
||||
|
||||
(void) mkdir_label(m->work_dir, 0700);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
|
||||
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
|
||||
const char *p = s;
|
||||
@ -111,20 +219,20 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (r == 1) {
|
||||
destination = strdup(source);
|
||||
destination = strdup(source[0] == '+' ? source+1 : source);
|
||||
if (!destination)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (r == 2 && !isempty(p)) {
|
||||
opts = strdup(p);
|
||||
if (!opts)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(source))
|
||||
if (isempty(source))
|
||||
source = NULL;
|
||||
else if (!source_path_is_valid(source))
|
||||
return -EINVAL;
|
||||
|
||||
if (!path_is_absolute(destination))
|
||||
@ -132,7 +240,7 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only
|
||||
|
||||
m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND);
|
||||
if (!m)
|
||||
return log_oom();
|
||||
return -ENOMEM;
|
||||
|
||||
m->source = source;
|
||||
m->destination = destination;
|
||||
@ -180,6 +288,71 @@ int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
|
||||
_cleanup_free_ char *upper = NULL, *destination = NULL;
|
||||
_cleanup_strv_free_ char **lower = NULL;
|
||||
CustomMount *m;
|
||||
int k;
|
||||
|
||||
k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k < 2)
|
||||
return -EADDRNOTAVAIL;
|
||||
if (k == 2) {
|
||||
/* If two parameters are specified, the first one is the lower, the second one the upper directory. And
|
||||
* we'll also define the destination mount point the same as the upper. */
|
||||
|
||||
if (!source_path_is_valid(lower[0]) ||
|
||||
!source_path_is_valid(lower[1]))
|
||||
return -EINVAL;
|
||||
|
||||
upper = lower[1];
|
||||
lower[1] = NULL;
|
||||
|
||||
destination = strdup(upper[0] == '+' ? upper+1 : upper); /* take the destination without "+" prefix */
|
||||
if (!destination)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
char **i;
|
||||
|
||||
/* If more than two parameters are specified, the last one is the destination, the second to last one
|
||||
* the "upper", and all before that the "lower" directories. */
|
||||
|
||||
destination = lower[k - 1];
|
||||
upper = lower[k - 2];
|
||||
lower[k - 2] = NULL;
|
||||
|
||||
STRV_FOREACH(i, lower)
|
||||
if (!source_path_is_valid(*i))
|
||||
return -EINVAL;
|
||||
|
||||
/* If the upper directory is unspecified, then let's create it automatically as a throw-away directory
|
||||
* in /var/tmp */
|
||||
if (isempty(upper))
|
||||
upper = NULL;
|
||||
else if (!source_path_is_valid(upper))
|
||||
return -EINVAL;
|
||||
|
||||
if (!path_is_absolute(destination))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
m->destination = destination;
|
||||
m->source = upper;
|
||||
m->lower = lower;
|
||||
m->read_only = read_only;
|
||||
|
||||
upper = destination = NULL;
|
||||
lower = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmpfs_patch_options(
|
||||
const char *options,
|
||||
bool userns,
|
||||
@ -414,11 +587,11 @@ int mount_all(const char *dest,
|
||||
if (!ro && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_RO))
|
||||
continue;
|
||||
|
||||
where = prefix_root(dest, mount_table[k].where);
|
||||
if (!where)
|
||||
return log_oom();
|
||||
r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where);
|
||||
|
||||
r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
|
||||
r = path_is_mount_point(where, NULL, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
|
||||
|
||||
@ -464,12 +637,14 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl
|
||||
const char *p = options;
|
||||
unsigned long flags = *mount_flags;
|
||||
char *opts = NULL;
|
||||
int r;
|
||||
|
||||
assert(options);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
int r = extract_first_word(&p, &word, ",", 0);
|
||||
|
||||
r = extract_first_word(&p, &word, ",", 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract mount option: %m");
|
||||
if (r == 0)
|
||||
@ -493,12 +668,13 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl
|
||||
}
|
||||
|
||||
static int mount_bind(const char *dest, CustomMount *m) {
|
||||
struct stat source_st, dest_st;
|
||||
const char *where;
|
||||
|
||||
_cleanup_free_ char *mount_opts = NULL, *where = NULL;
|
||||
unsigned long mount_flags = MS_BIND | MS_REC;
|
||||
_cleanup_free_ char *mount_opts = NULL;
|
||||
struct stat source_st, dest_st;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
assert(m);
|
||||
|
||||
if (m->options) {
|
||||
@ -510,9 +686,14 @@ static int mount_bind(const char *dest, CustomMount *m) {
|
||||
if (stat(m->source, &source_st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", m->source);
|
||||
|
||||
where = prefix_roota(dest, m->destination);
|
||||
r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination);
|
||||
if (r > 0) { /* Path exists already? */
|
||||
|
||||
if (stat(where, &dest_st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", where);
|
||||
|
||||
if (stat(where, &dest_st) >= 0) {
|
||||
if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
|
||||
log_error("Cannot bind mount directory %s on file %s.", m->source, where);
|
||||
return -EINVAL;
|
||||
@ -523,7 +704,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
} else if (errno == ENOENT) {
|
||||
} else { /* Path doesn't exist yet? */
|
||||
r = mkdir_parents_label(where, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make parents of %s: %m", where);
|
||||
@ -539,8 +720,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create mount point %s: %m", where);
|
||||
|
||||
} else
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", where);
|
||||
}
|
||||
|
||||
r = mount_verbose(LOG_ERR, m->source, where, NULL, mount_flags, mount_opts);
|
||||
if (r < 0)
|
||||
@ -561,18 +741,21 @@ static int mount_tmpfs(
|
||||
bool userns, uid_t uid_shift, uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
const char *where, *options;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
const char *options;
|
||||
_cleanup_free_ char *buf = NULL, *where = NULL;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
assert(m);
|
||||
|
||||
where = prefix_roota(dest, m->destination);
|
||||
|
||||
r = mkdir_p_label(where, 0755);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
|
||||
r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination);
|
||||
if (r == 0) { /* Doesn't exist yet? */
|
||||
r = mkdir_p_label(where, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
|
||||
}
|
||||
|
||||
r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
|
||||
if (r < 0)
|
||||
@ -582,7 +765,7 @@ static int mount_tmpfs(
|
||||
return mount_verbose(LOG_ERR, "tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options);
|
||||
}
|
||||
|
||||
static char *joined_and_escaped_lower_dirs(char * const *lower) {
|
||||
static char *joined_and_escaped_lower_dirs(char **lower) {
|
||||
_cleanup_strv_free_ char **sv = NULL;
|
||||
|
||||
sv = strv_copy(lower);
|
||||
@ -598,18 +781,22 @@ static char *joined_and_escaped_lower_dirs(char * const *lower) {
|
||||
}
|
||||
|
||||
static int mount_overlay(const char *dest, CustomMount *m) {
|
||||
_cleanup_free_ char *lower = NULL;
|
||||
const char *where, *options;
|
||||
|
||||
_cleanup_free_ char *lower = NULL, *where = NULL, *escaped_source = NULL;
|
||||
const char *options;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
assert(m);
|
||||
|
||||
where = prefix_roota(dest, m->destination);
|
||||
|
||||
r = mkdir_label(where, 0755);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
|
||||
r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination);
|
||||
if (r == 0) { /* Doesn't exist yet? */
|
||||
r = mkdir_label(where, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
|
||||
}
|
||||
|
||||
(void) mkdir_p_label(m->source, 0755);
|
||||
|
||||
@ -617,23 +804,15 @@ static int mount_overlay(const char *dest, CustomMount *m) {
|
||||
if (!lower)
|
||||
return log_oom();
|
||||
|
||||
if (m->read_only) {
|
||||
_cleanup_free_ char *escaped_source = NULL;
|
||||
|
||||
escaped_source = shell_escape(m->source, ",:");
|
||||
if (!escaped_source)
|
||||
return log_oom();
|
||||
escaped_source = shell_escape(m->source, ",:");
|
||||
if (!escaped_source)
|
||||
return log_oom();
|
||||
|
||||
if (m->read_only)
|
||||
options = strjoina("lowerdir=", escaped_source, ":", lower);
|
||||
} else {
|
||||
_cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
|
||||
else {
|
||||
_cleanup_free_ char *escaped_work_dir = NULL;
|
||||
|
||||
assert(m->work_dir);
|
||||
(void) mkdir_label(m->work_dir, 0700);
|
||||
|
||||
escaped_source = shell_escape(m->source, ",:");
|
||||
if (!escaped_source)
|
||||
return log_oom();
|
||||
escaped_work_dir = shell_escape(m->work_dir, ",:");
|
||||
if (!escaped_work_dir)
|
||||
return log_oom();
|
||||
@ -726,14 +905,19 @@ static int get_controllers(Set *subsystems) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy,
|
||||
CGroupUnified unified_requested, bool read_only) {
|
||||
static int mount_legacy_cgroup_hierarchy(
|
||||
const char *dest,
|
||||
const char *controller,
|
||||
const char *hierarchy,
|
||||
CGroupUnified unified_requested,
|
||||
bool read_only) {
|
||||
|
||||
const char *to, *fstype, *opts;
|
||||
int r;
|
||||
|
||||
to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
|
||||
|
||||
r = path_is_mount_point(to, 0);
|
||||
r = path_is_mount_point(to, dest, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
|
||||
if (r > 0)
|
||||
@ -773,8 +957,13 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle
|
||||
|
||||
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
|
||||
static int mount_legacy_cgns_supported(
|
||||
CGroupUnified unified_requested, bool userns, uid_t uid_shift,
|
||||
uid_t uid_range, const char *selinux_apifs_context) {
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
const char *cgroup_root = "/sys/fs/cgroup", *c;
|
||||
int r;
|
||||
@ -782,7 +971,7 @@ static int mount_legacy_cgns_supported(
|
||||
(void) mkdir_p(cgroup_root, 0755);
|
||||
|
||||
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
|
||||
r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW);
|
||||
r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
|
||||
if (r == 0) {
|
||||
@ -871,8 +1060,12 @@ skip_controllers:
|
||||
/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
|
||||
static int mount_legacy_cgns_unsupported(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
const char *cgroup_root;
|
||||
int r;
|
||||
@ -882,7 +1075,7 @@ static int mount_legacy_cgns_unsupported(
|
||||
(void) mkdir_p(cgroup_root, 0755);
|
||||
|
||||
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
|
||||
r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW);
|
||||
r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
|
||||
if (r == 0) {
|
||||
@ -975,7 +1168,7 @@ static int mount_unified_cgroups(const char *dest) {
|
||||
|
||||
(void) mkdir_p(p, 0755);
|
||||
|
||||
r = path_is_mount_point(p, AT_SYMLINK_FOLLOW);
|
||||
r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
|
||||
if (r > 0) {
|
||||
@ -995,14 +1188,16 @@ static int mount_unified_cgroups(const char *dest) {
|
||||
int mount_cgroups(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns, uid_t uid_shift, uid_t uid_range,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context,
|
||||
bool use_cgns) {
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_ALL)
|
||||
return mount_unified_cgroups(dest);
|
||||
else if (use_cgns)
|
||||
return mount_legacy_cgns_supported(unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
|
||||
return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
}
|
||||
|
@ -56,15 +56,16 @@ typedef struct CustomMount {
|
||||
char *options;
|
||||
char *work_dir;
|
||||
char **lower;
|
||||
char *rm_rf_tmpdir;
|
||||
} CustomMount;
|
||||
|
||||
CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t);
|
||||
|
||||
void custom_mount_free_all(CustomMount *l, unsigned n);
|
||||
int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n);
|
||||
|
||||
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
|
||||
int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s);
|
||||
|
||||
int custom_mount_compare(const void *a, const void *b);
|
||||
int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
|
||||
|
||||
int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
|
||||
int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
|
||||
|
@ -135,6 +135,11 @@ int register_machine(
|
||||
continue;
|
||||
|
||||
r = is_device_node(cm->source);
|
||||
if (r == -ENOENT) {
|
||||
/* The bind source might only appear as the image is put together, hence don't complain */
|
||||
log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source);
|
||||
continue;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stat %s: %m", cm->source);
|
||||
|
||||
|
@ -293,6 +293,32 @@ int config_parse_tmpfs(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_overlay(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Settings *settings = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
|
||||
if (r < 0)
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_veth_extra(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
@ -111,6 +111,7 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li
|
||||
int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_overlay(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_network_zone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_boot(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
@ -280,14 +280,9 @@ static void help(void) {
|
||||
, program_invocation_short_name);
|
||||
}
|
||||
|
||||
static int custom_mounts_prepare(void) {
|
||||
static int custom_mount_check_all(void) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
/* Ensure the mounts are applied prefix first. */
|
||||
qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare);
|
||||
|
||||
/* Allocate working directories for the overlay file systems that need it */
|
||||
for (i = 0; i < arg_n_custom_mounts; i++) {
|
||||
CustomMount *m = &arg_custom_mounts[i];
|
||||
|
||||
@ -301,19 +296,6 @@ static int custom_mounts_prepare(void) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (m->type != CUSTOM_MOUNT_OVERLAY)
|
||||
continue;
|
||||
|
||||
if (m->work_dir)
|
||||
continue;
|
||||
|
||||
if (m->read_only)
|
||||
continue;
|
||||
|
||||
r = tempfn_random(m->source, NULL, &m->work_dir);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -789,69 +771,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_OVERLAY:
|
||||
case ARG_OVERLAY_RO: {
|
||||
_cleanup_free_ char *upper = NULL, *destination = NULL;
|
||||
_cleanup_strv_free_ char **lower = NULL;
|
||||
CustomMount *m;
|
||||
unsigned n = 0;
|
||||
char **i;
|
||||
|
||||
r = strv_split_extract(&lower, optarg, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
else if (r < 0) {
|
||||
log_error("Invalid overlay specification: %s", optarg);
|
||||
return r;
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, lower) {
|
||||
if (!path_is_absolute(*i)) {
|
||||
log_error("Overlay path %s is not absolute.", *i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n < 2) {
|
||||
log_error("--overlay= needs at least two colon-separated directories specified.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (n == 2) {
|
||||
/* If two parameters are specified,
|
||||
* the first one is the lower, the
|
||||
* second one the upper directory. And
|
||||
* we'll also define the destination
|
||||
* mount point the same as the upper. */
|
||||
upper = lower[1];
|
||||
lower[1] = NULL;
|
||||
|
||||
destination = strdup(upper);
|
||||
if (!destination)
|
||||
return log_oom();
|
||||
|
||||
} else {
|
||||
upper = lower[n - 2];
|
||||
destination = lower[n - 1];
|
||||
lower[n - 2] = NULL;
|
||||
}
|
||||
|
||||
m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY);
|
||||
if (!m)
|
||||
return log_oom();
|
||||
|
||||
m->destination = destination;
|
||||
m->source = upper;
|
||||
m->lower = lower;
|
||||
m->read_only = c == ARG_OVERLAY_RO;
|
||||
|
||||
upper = destination = NULL;
|
||||
lower = NULL;
|
||||
case ARG_OVERLAY_RO:
|
||||
r = overlay_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_OVERLAY_RO);
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
return log_error_errno(r, "--overlay(-ro)= needs at least two colon-separated directories specified.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --overlay(-ro)= argument %s: %m", optarg);
|
||||
|
||||
arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'E': {
|
||||
char **n;
|
||||
@ -1133,6 +1061,16 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_ephemeral && arg_template && !arg_directory) {
|
||||
/* User asked for ephemeral execution but specified --template= instead of --directory=. Semantically
|
||||
* such an invocation makes some sense, see https://github.com/systemd/systemd/issues/3667. Let's
|
||||
* accept this here, and silently make "--ephemeral --template=" equivalent to "--ephemeral
|
||||
* --directory=". */
|
||||
|
||||
arg_directory = arg_template;
|
||||
arg_template = NULL;
|
||||
}
|
||||
|
||||
if (arg_template && !(arg_directory || arg_machine)) {
|
||||
log_error("--template= needs --directory= or --machine=.");
|
||||
return -EINVAL;
|
||||
@ -1191,6 +1129,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
else
|
||||
arg_use_cgns = r;
|
||||
|
||||
r = custom_mount_check_all();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1666,7 +1608,7 @@ static int setup_journal(const char *directory) {
|
||||
p = strjoina("/var/log/journal/", id);
|
||||
q = prefix_roota(directory, p);
|
||||
|
||||
if (path_is_mount_point(p, 0) > 0) {
|
||||
if (path_is_mount_point(p, NULL, 0) > 0) {
|
||||
if (try)
|
||||
return 0;
|
||||
|
||||
@ -1674,7 +1616,7 @@ static int setup_journal(const char *directory) {
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (path_is_mount_point(q, 0) > 0) {
|
||||
if (path_is_mount_point(q, NULL, 0) > 0) {
|
||||
if (try)
|
||||
return 0;
|
||||
|
||||
@ -2656,6 +2598,25 @@ static int determine_names(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chase_symlinks_and_update(char **p, unsigned flags) {
|
||||
char *chased;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (!*p)
|
||||
return 0;
|
||||
|
||||
r = chase_symlinks(*p, NULL, flags, &chased);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve path %s: %m", *p);
|
||||
|
||||
free(*p);
|
||||
*p = chased;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int determine_uid_shift(const char *directory) {
|
||||
int r;
|
||||
|
||||
@ -3657,7 +3618,7 @@ static int run(int master,
|
||||
|
||||
static const struct sigaction sa = {
|
||||
.sa_handler = nop_signal_handler,
|
||||
.sa_flags = SA_NOCLDSTOP,
|
||||
.sa_flags = SA_NOCLDSTOP|SA_RESTART,
|
||||
};
|
||||
|
||||
_cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
|
||||
@ -4126,13 +4087,17 @@ int main(int argc, char *argv[]) {
|
||||
if (arg_ephemeral) {
|
||||
_cleanup_free_ char *np = NULL;
|
||||
|
||||
r = chase_symlinks_and_update(&arg_directory, 0);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
/* If the specified path is a mount point we
|
||||
* generate the new snapshot immediately
|
||||
* inside it under a random name. However if
|
||||
* the specified is not a mount point we
|
||||
* create the new snapshot in the parent
|
||||
* directory, just next to it. */
|
||||
r = path_is_mount_point(arg_directory, 0);
|
||||
r = path_is_mount_point(arg_directory, NULL, 0);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory);
|
||||
goto finish;
|
||||
@ -4170,6 +4135,10 @@ int main(int argc, char *argv[]) {
|
||||
remove_directory = true;
|
||||
|
||||
} else {
|
||||
r = chase_symlinks_and_update(&arg_directory, arg_template ? CHASE_NONEXISTENT : 0);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
|
||||
if (r == -EBUSY) {
|
||||
log_error_errno(r, "Directory tree %s is currently busy.", arg_directory);
|
||||
@ -4181,6 +4150,10 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (arg_template) {
|
||||
r = chase_symlinks_and_update(&arg_template, 0);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = btrfs_subvol_snapshot(arg_template, arg_directory,
|
||||
(arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
|
||||
BTRFS_SNAPSHOT_FALLBACK_COPY |
|
||||
@ -4222,6 +4195,10 @@ int main(int argc, char *argv[]) {
|
||||
assert(arg_image);
|
||||
assert(!arg_template);
|
||||
|
||||
r = chase_symlinks_and_update(&arg_image, 0);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
if (arg_ephemeral) {
|
||||
_cleanup_free_ char *np = NULL;
|
||||
|
||||
@ -4293,7 +4270,7 @@ int main(int argc, char *argv[]) {
|
||||
remove_image = false;
|
||||
}
|
||||
|
||||
r = custom_mounts_prepare();
|
||||
r = custom_mount_prepare_all(arg_directory, arg_custom_mounts, arg_n_custom_mounts);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
@ -399,7 +399,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
|
||||
|
||||
return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
|
||||
return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0;
|
||||
}
|
||||
|
||||
static int condition_test_path_is_read_write(Condition *c) {
|
||||
|
@ -225,7 +225,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) {
|
||||
if (path_is_mount_point("/var/lib/machines", NULL, AT_SYMLINK_FOLLOW) > 0) {
|
||||
log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
|
||||
return 0;
|
||||
}
|
||||
|
@ -2487,7 +2487,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
r = chase_symlinks(path, arg_root, &lpath);
|
||||
r = chase_symlinks(path, arg_root, 0, &lpath);
|
||||
if (r == -ENOENT)
|
||||
continue;
|
||||
if (r == -ENOMEM)
|
||||
|
@ -144,7 +144,7 @@ static void test_copy_tree(void) {
|
||||
assert_se((f = strjoin(original_dir, *p)));
|
||||
assert_se((l = strjoin(copy_dir, *link)));
|
||||
|
||||
assert_se(readlink_and_canonicalize(l, &target) == 0);
|
||||
assert_se(readlink_and_canonicalize(l, NULL, &target) == 0);
|
||||
assert_se(path_equal(f, target));
|
||||
}
|
||||
|
||||
|
@ -60,66 +60,131 @@ static void test_chase_symlinks(void) {
|
||||
p = strjoina(temp, "/start");
|
||||
assert_se(symlink("top/dot/dotdota", p) >= 0);
|
||||
|
||||
r = chase_symlinks(p, NULL, &result);
|
||||
assert_se(r >= 0);
|
||||
/* Paths that use symlinks underneath the "root" */
|
||||
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/usr"));
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, &result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
q = strjoina(temp, "/usr");
|
||||
|
||||
r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
|
||||
assert_se(r == 0);
|
||||
assert_se(path_equal(result, q));
|
||||
|
||||
assert_se(mkdir(q, 0700) >= 0);
|
||||
|
||||
r = chase_symlinks(p, temp, &result);
|
||||
assert_se(r >= 0);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, q));
|
||||
|
||||
p = strjoina(temp, "/slash");
|
||||
assert_se(symlink("/", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, NULL, &result);
|
||||
assert_se(r >= 0);
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/"));
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, &result);
|
||||
assert_se(r >= 0);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, temp));
|
||||
|
||||
/* Paths that would "escape" outside of the "root" */
|
||||
|
||||
p = strjoina(temp, "/6dots");
|
||||
assert_se(symlink("../../..", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, temp));
|
||||
|
||||
p = strjoina(temp, "/6dotsusr");
|
||||
assert_se(symlink("../../../usr", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, q));
|
||||
|
||||
p = strjoina(temp, "/top/8dotsusr");
|
||||
assert_se(symlink("../../../../usr", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, q));
|
||||
|
||||
/* Paths that contain repeated slashes */
|
||||
|
||||
p = strjoina(temp, "/slashslash");
|
||||
assert_se(symlink("///usr///", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, NULL, &result);
|
||||
assert_se(r >= 0);
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/usr"));
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, &result);
|
||||
assert_se(r >= 0);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, q));
|
||||
|
||||
/* Paths using . */
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks("/etc/./.././", NULL, &result);
|
||||
assert_se(r >= 0);
|
||||
r = chase_symlinks("/etc/./.././", NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/"));
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks("/etc/./.././", "/etc", &result);
|
||||
assert_se(r == -EINVAL);
|
||||
r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, "/etc"));
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks("/etc/machine-id/foo", NULL, &result);
|
||||
r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
|
||||
assert_se(r == -ENOTDIR);
|
||||
|
||||
/* Path that loops back to self */
|
||||
|
||||
result = mfree(result);
|
||||
p = strjoina(temp, "/recursive-symlink");
|
||||
assert_se(symlink("recursive-symlink", p) >= 0);
|
||||
r = chase_symlinks(p, NULL, &result);
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r == -ELOOP);
|
||||
|
||||
/* Path which doesn't exist */
|
||||
|
||||
p = strjoina(temp, "/idontexist");
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
|
||||
assert_se(r == 0);
|
||||
assert_se(path_equal(result, p));
|
||||
result = mfree(result);
|
||||
|
||||
p = strjoina(temp, "/idontexist/meneither");
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
|
||||
assert_se(r == 0);
|
||||
assert_se(path_equal(result, p));
|
||||
result = mfree(result);
|
||||
|
||||
/* Path which doesn't exist, but contains weird stuff */
|
||||
|
||||
p = strjoina(temp, "/idontexist/..");
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
|
||||
}
|
||||
|
||||
|
@ -337,17 +337,17 @@ static void test_path_is_mount_point(void) {
|
||||
_cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
|
||||
_cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
|
||||
|
||||
assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/", 0) > 0);
|
||||
assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/", NULL, 0) > 0);
|
||||
|
||||
assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/proc", 0) > 0);
|
||||
assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
|
||||
|
||||
assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1", 0) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
|
||||
|
||||
assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/sys", 0) > 0);
|
||||
assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
|
||||
|
||||
/* we'll create a hierarchy of different kinds of dir/file/link
|
||||
* layouts:
|
||||
@ -381,10 +381,10 @@ static void test_path_is_mount_point(void) {
|
||||
assert_se(link1);
|
||||
assert_se(symlink("file2", link2) == 0);
|
||||
|
||||
assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(file1, 0) == 0);
|
||||
assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(link1, 0) == 0);
|
||||
assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(file1, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(link1, NULL, 0) == 0);
|
||||
|
||||
/* directory mountpoints */
|
||||
dir1 = path_join(NULL, tmp_dir, "dir1");
|
||||
@ -400,10 +400,10 @@ static void test_path_is_mount_point(void) {
|
||||
assert_se(dir2);
|
||||
assert_se(mkdir(dir2, 0755) == 0);
|
||||
|
||||
assert_se(path_is_mount_point(dir1, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, 0) == 0);
|
||||
assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
|
||||
|
||||
/* file in subdirectory mountpoints */
|
||||
dir1file = path_join(NULL, dir1, "file");
|
||||
@ -412,10 +412,10 @@ static void test_path_is_mount_point(void) {
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
|
||||
assert_se(path_is_mount_point(dir1file, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1file, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, 0) == 0);
|
||||
assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
|
||||
|
||||
/* these tests will only work as root */
|
||||
if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
|
||||
@ -423,10 +423,10 @@ static void test_path_is_mount_point(void) {
|
||||
|
||||
/* files */
|
||||
/* capture results in vars, to avoid dangling mounts on failure */
|
||||
rf = path_is_mount_point(file2, 0);
|
||||
rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW);
|
||||
rlf = path_is_mount_point(link2, 0);
|
||||
rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW);
|
||||
rf = path_is_mount_point(file2, NULL, 0);
|
||||
rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
|
||||
rlf = path_is_mount_point(link2, NULL, 0);
|
||||
rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
assert_se(umount(file2) == 0);
|
||||
|
||||
@ -444,13 +444,13 @@ static void test_path_is_mount_point(void) {
|
||||
|
||||
assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
|
||||
|
||||
rf = path_is_mount_point(dir1, 0);
|
||||
rt = path_is_mount_point(dir1, AT_SYMLINK_FOLLOW);
|
||||
rlf = path_is_mount_point(dirlink1, 0);
|
||||
rlt = path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW);
|
||||
rf = path_is_mount_point(dir1, NULL, 0);
|
||||
rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
|
||||
rlf = path_is_mount_point(dirlink1, NULL, 0);
|
||||
rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
|
||||
/* its parent is a mount point, but not /file itself */
|
||||
rl1f = path_is_mount_point(dirlink1file, 0);
|
||||
rl1t = path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW);
|
||||
rl1f = path_is_mount_point(dirlink1file, NULL, 0);
|
||||
rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
assert_se(umount(dir1) == 0);
|
||||
|
||||
|
@ -143,7 +143,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
|
||||
|
||||
/* set signal handlers */
|
||||
act.sa_handler = sig_handler;
|
||||
act.sa_flags = SA_RESTART;
|
||||
act.sa_flags = SA_RESTART|SA_RESTART;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
sigaction(SIGTERM, &act, NULL);
|
||||
sigemptyset(&mask);
|
||||
|
Loading…
Reference in New Issue
Block a user