1
0
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:
Zbigniew Jędrzejewski-Szmek 2016-12-01 09:29:14 -05:00 committed by GitHub
commit a32ffa161c
37 changed files with 643 additions and 308 deletions

View File

@ -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).

View File

@ -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

View File

@ -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>

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View 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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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)

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);