Merge pull request #5333 from poettering/machined-copy-files-userns

machined userns fixes
This commit is contained in:
Lennart Poettering
2017-02-17 13:51:58 +01:00
committed by GitHub
26 changed files with 381 additions and 109 deletions
+18 -16
View File
@@ -518,19 +518,14 @@
<varlistentry>
<term><command>bind</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
<listitem><para>Bind mounts a directory from the host into the
specified container. The first directory argument is the
source directory on the host, the second directory argument
is the destination directory in the container. When the
latter is omitted, the destination path in the container is
the same as the source path on the host. When combined with
the <option>--read-only</option> switch, a ready-only bind
mount is created. When combined with the
<option>--mkdir</option> switch, the destination path is first
created before the mount is applied. Note that this option is
currently only supported for
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
containers.</para></listitem>
<listitem><para>Bind mounts a directory from the host into the specified container. The first directory
argument is the source directory on the host, the second directory argument is the destination directory in the
container. When the latter is omitted, the destination path in the container is the same as the source path on
the host. When combined with the <option>--read-only</option> switch, a ready-only bind mount is created. When
combined with the <option>--mkdir</option> switch, the destination path is first created before the mount is
applied. Note that this option is currently only supported for
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> containers,
and only if user namespacing (<option>--private-users</option>) is not used.</para></listitem>
</varlistentry>
<varlistentry>
@@ -540,9 +535,12 @@
system into a running container. Takes a container name,
followed by the source path on the host and the destination
path in the container. If the destination path is omitted, the
same as the source path is used.</para></listitem>
</varlistentry>
same as the source path is used.</para>
<para>If host and container share the same user and group namespace, file ownership by numeric user ID and
group ID is preserved for the copy, otherwise all files and directories in the copy will be owned by the root
user and group (UID/GID 0).</para></listitem>
</varlistentry>
<varlistentry>
<term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
@@ -551,7 +549,11 @@
into the host system. Takes a container name, followed by the
source path in the container the destination path on the host.
If the destination path is omitted, the same as the source path
is used.</para></listitem>
is used.</para>
<para>If host and container share the same user and group namespace, file ownership by numeric user ID and
group ID is preserved for the copy, otherwise all files and directories in the copy will be owned by the root
user and group (UID/GID 0).</para></listitem>
</varlistentry>
</variablelist></refsect2>
+6 -1
View File
@@ -474,7 +474,12 @@
<para>Note that the picked UID/GID range is not written to <filename>/etc/passwd</filename> or
<filename>/etc/group</filename>. In fact, the allocation of the range is not stored persistently anywhere,
except in the file ownership of the files and directories of the container.</para></listitem>
except in the file ownership of the files and directories of the container.</para>
<para>Note that when user namespacing is used file ownership on disk reflects this, and all of the container's
files and directories are owned by the container's effective user and group IDs. This means that copying files
from and to the container image requires correction of the numeric UID/GID values, according to the UID/GID
shift applied.</para></listitem>
</varlistentry>
<varlistentry>
+1 -1
View File
@@ -1737,7 +1737,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
} else if (r < 0)
return r;
r = copy_directory_fd(old_fd, new_path, true);
r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
if (r < 0)
goto fallback_fail;
+89 -40
View File
@@ -45,6 +45,7 @@
#include "strv.h"
#include "time-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "xattr-util.h"
#define COPY_BUFFER_SIZE (16*1024u)
@@ -68,7 +69,7 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
return -errno;
}
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
@@ -77,7 +78,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
assert(fdt >= 0);
/* Try btrfs reflinks first. */
if (try_reflink &&
if ((copy_flags & COPY_REFLINK) &&
max_bytes == (uint64_t) -1 &&
lseek(fdf, 0, SEEK_CUR) == 0 &&
lseek(fdt, 0, SEEK_CUR) == 0) {
@@ -176,7 +177,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
return 0; /* return 0 if we hit EOF earlier than the size limit */
}
static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
static int fd_copy_symlink(
int df,
const char *from,
const struct stat *st,
int dt,
const char *to,
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags) {
_cleanup_free_ char *target = NULL;
int r;
@@ -191,13 +201,25 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int
if (symlinkat(target, dt, to) < 0)
return -errno;
if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
if (fchownat(dt, to,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
gid_is_valid(override_gid) ? override_gid : st->st_gid,
AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
return 0;
}
static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
static int fd_copy_regular(
int df,
const char *from,
const struct stat *st,
int dt,
const char *to,
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1, fdt = -1;
struct timespec ts[2];
int r, q;
@@ -214,13 +236,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
if (fdt < 0)
return -errno;
r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
if (r < 0) {
unlinkat(dt, to, 0);
return r;
}
if (fchown(fdt, st->st_uid, st->st_gid) < 0)
if (fchown(fdt,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -229,7 +253,6 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
ts[0] = st->st_atim;
ts[1] = st->st_mtim;
(void) futimens(fdt, ts);
(void) copy_xattr(fdf, fdt);
q = close(fdt);
@@ -243,7 +266,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
return r;
}
static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
static int fd_copy_fifo(
int df,
const char *from,
const struct stat *st,
int dt,
const char *to,
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags) {
int r;
assert(from);
@@ -254,7 +285,10 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
if (fchownat(dt, to,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
gid_is_valid(override_gid) ? override_gid : st->st_gid,
AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -263,7 +297,15 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
return r;
}
static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
static int fd_copy_node(
int df,
const char *from,
const struct stat *st,
int dt,
const char *to,
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags) {
int r;
assert(from);
@@ -274,7 +316,10 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
if (fchownat(dt, to,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
gid_is_valid(override_gid) ? override_gid : st->st_gid,
AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -290,7 +335,9 @@ static int fd_copy_directory(
int dt,
const char *to,
dev_t original_device,
bool merge) {
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
@@ -316,7 +363,7 @@ static int fd_copy_directory(
r = mkdirat(dt, to, st->st_mode & 07777);
if (r >= 0)
created = true;
else if (errno == EEXIST && merge)
else if (errno == EEXIST && (copy_flags & COPY_MERGE))
created = false;
else
return -errno;
@@ -343,19 +390,19 @@ static int fd_copy_directory(
continue;
if (S_ISREG(buf.st_mode))
q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISDIR(buf.st_mode))
q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
else if (S_ISLNK(buf.st_mode))
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(buf.st_mode))
q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else
q = -EOPNOTSUPP;
if (q == -EEXIST && merge)
if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0;
if (q < 0)
@@ -368,7 +415,9 @@ static int fd_copy_directory(
st->st_mtim
};
if (fchown(fdt, st->st_uid, st->st_gid) < 0)
if (fchown(fdt,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -381,7 +430,7 @@ static int fd_copy_directory(
return r;
}
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -391,24 +440,24 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
return -errno;
if (S_ISREG(st.st_mode))
return fd_copy_regular(fdf, from, &st, fdt, to);
return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISDIR(st.st_mode))
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags);
else if (S_ISLNK(st.st_mode))
return fd_copy_symlink(fdf, from, &st, fdt, to);
return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode))
return fd_copy_fifo(fdf, from, &st, fdt, to);
return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
return fd_copy_node(fdf, from, &st, fdt, to);
return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else
return -EOPNOTSUPP;
}
int copy_tree(const char *from, const char *to, bool merge) {
return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
}
int copy_directory_fd(int dirfd, const char *to, bool merge) {
int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(dirfd >= 0);
@@ -420,10 +469,10 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
int copy_directory(const char *from, const char *to, bool merge) {
int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -435,10 +484,10 @@ int copy_directory(const char *from, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
int copy_file_fd(const char *from, int fdt, bool try_reflink) {
int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1;
int r;
@@ -449,7 +498,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
if (fdf < 0)
return -errno;
r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
(void) copy_times(fdf, fdt);
(void) copy_xattr(fdf, fdt);
@@ -457,7 +506,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
return r;
}
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
int fdt = -1, r;
assert(from);
@@ -472,7 +521,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
if (chattr_flags != 0)
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
r = copy_file_fd(from, fdt, true);
r = copy_file_fd(from, fdt, copy_flags);
if (r < 0) {
close(fdt);
unlink(to);
@@ -487,7 +536,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
return 0;
}
int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
_cleanup_free_ char *t = NULL;
int r;
@@ -498,11 +547,11 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
if (r < 0)
return r;
r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags, copy_flags);
if (r < 0)
return r;
if (replace) {
if (copy_flags & COPY_REPLACE) {
r = renameat(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0)
r = -errno;
+14 -8
View File
@@ -24,13 +24,19 @@
#include <stdint.h>
#include <sys/types.h>
int copy_file_fd(const char *from, int to, bool try_reflink);
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags);
int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags);
int copy_tree(const char *from, const char *to, bool merge);
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
int copy_directory_fd(int dirfd, const char *to, bool merge);
int copy_directory(const char *from, const char *to, bool merge);
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink);
typedef enum CopyFlags {
COPY_REFLINK = 0x1, /* try to reflink */
COPY_MERGE = 0x2, /* merge existing trees with our new one to copy */
COPY_REPLACE = 0x4, /* replace an existing file if there's one */
} CopyFlags;
int copy_file_fd(const char *from, int to, CopyFlags copy_flags);
int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags);
int copy_directory(const char *from, const char *to, CopyFlags copy_flags);
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags);
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
+2 -2
View File
@@ -1340,11 +1340,11 @@ static int usbffs_write_descs(int fd, Service *s) {
if (!s->usb_function_descriptors || !s->usb_function_strings)
return -EINVAL;
r = copy_file_fd(s->usb_function_descriptors, fd, false);
r = copy_file_fd(s->usb_function_descriptors, fd, 0);
if (r < 0)
return r;
return copy_file_fd(s->usb_function_strings, fd, false);
return copy_file_fd(s->usb_function_strings, fd, 0);
}
static int usbffs_select_ep(const struct dirent *d) {
+1 -1
View File
@@ -352,7 +352,7 @@ static int save_external_coredump(
if (fd < 0)
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
r = copy_bytes(input_fd, fd, max_size, false);
r = copy_bytes(input_fd, fd, max_size, 0);
if (r < 0) {
log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]);
goto fail;
+1 -1
View File
@@ -252,7 +252,7 @@ static int process_locale(void) {
if (arg_copy_locale && arg_root) {
mkdir_parents(etc_localeconf, 0755);
r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0);
r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, COPY_REFLINK);
if (r != -ENOENT) {
if (r < 0)
return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
+2 -2
View File
@@ -315,7 +315,7 @@ static int raw_pull_copy_auxiliary_file(
local = strjoina(i->image_root, "/", i->local, suffix);
r = copy_file_atomic(*path, local, 0644, i->force_local, 0);
r = copy_file_atomic(*path, local, 0644, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0));
if (r == -EEXIST)
log_warning_errno(r, "File %s already exists, not replacing.", local);
else if (r == -ENOENT)
@@ -378,7 +378,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true);
r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK);
if (r < 0) {
unlink(tp);
return log_error_errno(r, "Failed to make writable copy of image: %m");
+1 -1
View File
@@ -256,7 +256,7 @@ static int tar_pull_make_local_copy(TarPull *i) {
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0);
r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0));
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r == -ENOENT)
+1 -1
View File
@@ -374,7 +374,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err
if (fd < 0)
_exit(EXIT_FAILURE);
r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
if (r < 0)
_exit(EXIT_FAILURE);
+46 -3
View File
@@ -411,7 +411,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (fd < 0)
_exit(EXIT_FAILURE);
r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
if (r < 0)
_exit(EXIT_FAILURE);
@@ -841,6 +841,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
int read_only, make_directory;
pid_t child;
siginfo_t si;
uid_t uid;
int r;
assert(message);
@@ -875,6 +876,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
if (r == 0)
return 1; /* Will call us back */
r = machine_get_uid_shift(m, &uid);
if (r < 0)
return r;
if (uid != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
/* One day, when bind mounting /proc/self/fd/n works across
* namespace boundaries we should rework this logic to make
* use of it... */
@@ -1055,10 +1062,12 @@ finish:
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE;
_cleanup_close_ int hostfd = -1;
Machine *m = userdata;
bool copy_from;
pid_t child;
uid_t uid_shift;
char *t;
int r;
@@ -1097,6 +1106,10 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
if (r == 0)
return 1; /* Will call us back */
r = machine_get_uid_shift(m, &uid_shift);
if (r < 0)
return r;
copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
if (copy_from) {
@@ -1151,10 +1164,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
goto child_fail;
}
/* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
* 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
* the UID/GIDs as they are. */
if (copy_from)
r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
else
r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
hostfd = safe_close(hostfd);
containerfd = safe_close(containerfd);
@@ -1276,6 +1292,32 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
return sd_bus_reply_method_return(message, "h", fd);
}
int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Machine *m = userdata;
uid_t shift = 0;
int r;
assert(message);
assert(m);
/* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
* we kinda have to for this. */
if (m->class == MACHINE_HOST)
return sd_bus_reply_method_return(message, "u", UINT32_C(0));
if (m->class != MACHINE_CONTAINER)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
r = machine_get_uid_shift(m, &shift);
if (r == -ENXIO)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, "u", (uint32_t) shift);
}
const sd_bus_vtable machine_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1293,6 +1335,7 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
+1
View File
@@ -39,6 +39,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error);
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
+91
View File
@@ -38,6 +38,7 @@
#include "parse-util.h"
#include "process-util.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
#include "terminal-util.h"
#include "unit-name.h"
@@ -604,6 +605,96 @@ void machine_release_unit(Machine *m) {
m->unit = mfree(m->unit);
}
int machine_get_uid_shift(Machine *m, uid_t *ret) {
char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
uid_t uid_base, uid_shift, uid_range;
gid_t gid_base, gid_shift, gid_range;
_cleanup_fclose_ FILE *f = NULL;
int k;
assert(m);
assert(ret);
/* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
* mappings. In most cases setups should be simple like this, and administrators should only care about the
* basic offset a container has relative to the host. This is what this function exposes.
*
* If we encounter any more complex mappings we politely refuse this with ENXIO. */
if (m->class == MACHINE_HOST) {
*ret = 0;
return 0;
}
if (m->class != MACHINE_CONTAINER)
return -EOPNOTSUPP;
xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
f = fopen(p, "re");
if (!f) {
if (errno == ENOENT) {
/* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
*ret = 0;
return 0;
}
return -errno;
}
/* Read the first line. There's at least one. */
errno = 0;
k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
if (k != 3) {
if (ferror(f))
return -errno;
return -EBADMSG;
}
/* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
if (uid_base != 0)
return -ENXIO;
/* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
if (uid_range < (uid_t) 65534U)
return -ENXIO;
/* If there's more than one line, then we don't support this mapping. */
if (fgetc(f) != EOF)
return -ENXIO;
fclose(f);
xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
f = fopen(p, "re");
if (!f)
return -errno;
/* Read the first line. There's at least one. */
errno = 0;
k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
if (k != 3) {
if (ferror(f))
return -errno;
return -EBADMSG;
}
/* If there's more than one line, then we don't support this file. */
if (fgetc(f) != EOF)
return -ENXIO;
/* If the UID and GID mapping doesn't match, we don't support this mapping. */
if (uid_base != (uid_t) gid_base)
return -ENXIO;
if (uid_shift != (uid_t) gid_shift)
return -ENXIO;
if (uid_range != (uid_t) gid_range)
return -ENXIO;
*ret = uid_shift;
return 0;
}
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
[MACHINE_VM] = "vm",
+2
View File
@@ -108,3 +108,5 @@ KillWho kill_who_from_string(const char *s) _pure_;
int machine_openpt(Machine *m, int flags);
int machine_open_terminal(Machine *m, const char *path, int mode);
int machine_get_uid_shift(Machine *m, uid_t *ret);
+50 -9
View File
@@ -327,8 +327,10 @@ static int list_machines(int argc, char *argv[], void *userdata) {
(int) max_version_id, strdash_if_empty(machines[j].version_id));
r = print_addresses(bus, machines[j].name, 0, "", prefix, arg_addrs);
if (r == -EOPNOTSUPP)
printf("-\n");
if (r <= 0) /* error or no addresses defined? */
fputs("-\n", stdout);
else
fputc('\n', stdout);
}
if (arg_legend) {
@@ -520,6 +522,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *addresses = NULL;
bool truncate = false;
unsigned n = 0;
int r;
assert(bus);
@@ -567,7 +570,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
else
strcpy(buf_ifi, "");
if(!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
return log_oom();
} else
truncate = true;
@@ -581,6 +584,8 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
if (n_addr > 0)
n_addr -= 1;
n++;
}
if (r < 0)
return bus_log_parse_error(r);
@@ -589,8 +594,10 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
if (r < 0)
return bus_log_parse_error(r);
fprintf(stdout, "%s%s\n", addresses, truncate ? "..." : "");
return 0;
if (n > 0)
fprintf(stdout, "%s%s", addresses, truncate ? "..." : "");
return (int) n;
}
static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
@@ -611,6 +618,37 @@ static int print_os_release(sd_bus *bus, const char *method, const char *name, c
return 0;
}
static int print_uid_shift(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
uint32_t shift;
int r;
assert(bus);
assert(name);
r = sd_bus_call_method(bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetMachineUIDShift",
&error,
&reply,
"s", name);
if (r < 0)
return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "u", &shift);
if (r < 0)
return r;
if (shift == 0) /* Don't show trivial mappings */
return 0;
printf(" UID Shift: %" PRIu32 "\n", shift);
return 0;
}
typedef struct MachineStatusInfo {
char *name;
sd_id128_t id;
@@ -707,13 +745,16 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
fputc('\n', stdout);
}
print_addresses(bus, i->name, ifi,
"\t Address: ",
"\n\t ",
ALL_IP_ADDRESSES);
if (print_addresses(bus, i->name, ifi,
"\t Address: ",
"\n\t ",
ALL_IP_ADDRESSES) > 0)
fputc('\n', stdout);
print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
print_uid_shift(bus, i->name);
if (i->unit) {
printf("\t Unit: %s\n", i->unit);
show_unit_cgroup(bus, i->unit, i->leader);
+21
View File
@@ -729,6 +729,26 @@ static int method_open_machine_root_directory(sd_bus_message *message, void *use
return bus_machine_method_open_root_directory(message, machine, error);
}
static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
const char *name;
int r;
assert(message);
assert(m);
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
machine = hashmap_get(m->machines, name);
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
return bus_machine_method_get_uid_shift(message, machine, error);
}
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
@@ -1416,6 +1436,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetMachineUIDShift", "s", "u", method_get_machine_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
+3 -1
View File
@@ -61,8 +61,10 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
} else {
/* The default operation when done is to simply return an error on failure or an empty success
* message on success. */
if (r < 0)
if (r < 0) {
sd_bus_error_set_errno(&error, r);
goto fail;
}
r = sd_bus_reply_method_return(o->message, NULL);
if (r < 0)
@@ -64,6 +64,10 @@
send_interface="org.freedesktop.machine1.Manager"
send_member="GetMachineOSRelease"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
send_member="GetMachineUIDShift"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
send_member="OpenMachineLogin"/>
@@ -148,6 +152,10 @@
send_interface="org.freedesktop.machine1.Machine"
send_member="GetOSRelease"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Machine"
send_member="GetUIDShift"/>
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Machine"
send_member="OpenLogin"/>
+3 -3
View File
@@ -1364,7 +1364,7 @@ static int setup_resolv_conf(const char *dest) {
}
/* If that didn't work, let's copy the file */
r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0);
r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0, COPY_REFLINK);
if (r < 0) {
/* If the file already exists as symlink, let's suppress the warning, under the assumption that
* resolved or something similar runs inside and the symlink points there.
@@ -3700,7 +3700,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL);
r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, COPY_REFLINK);
if (r < 0) {
r = log_error_errno(r, "Failed to copy image file: %m");
goto finish;
@@ -3856,7 +3856,7 @@ finish:
/* Try to flush whatever is still queued in the pty */
if (master >= 0) {
(void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false);
(void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, 0);
master = safe_close(master);
}
+2 -2
View File
@@ -594,7 +594,7 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch
if (!rs)
return -ENOMEM;
return copy_file_atomic(path, rs, 0664, false, 0);
return copy_file_atomic(path, rs, 0664, 0, COPY_REFLINK);
}
int image_clone(Image *i, const char *new_name, bool read_only) {
@@ -656,7 +656,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_RAW:
new_path = strjoina("/var/lib/machines/", new_name, ".raw");
r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL);
r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, COPY_REFLINK);
break;
default:
+1 -1
View File
@@ -44,7 +44,7 @@ static pid_t pager_pid = 0;
noreturn static void pager_fallback(void) {
int r;
r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false);
r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, 0);
if (r < 0) {
log_error_errno(r, "Internal pager failed: %m");
_exit(EXIT_FAILURE);
+2 -2
View File
@@ -5300,7 +5300,7 @@ static int cat_file(const char *filename, bool newline) {
ansi_normal());
fflush(stdout);
return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false);
return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
}
static int cat(int argc, char *argv[], void *userdata) {
@@ -6582,7 +6582,7 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
if (r < 0)
return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
r = copy_file(original_path, t, 0, 0644, 0);
r = copy_file(original_path, t, 0, 0644, 0, COPY_REFLINK);
if (r == -ENOENT) {
r = touch(t);
+1 -1
View File
@@ -211,7 +211,7 @@ static int make_backup(const char *target, const char *x) {
if (r < 0)
return r;
r = copy_bytes(src, fileno(dst), (uint64_t) -1, true);
r = copy_bytes(src, fileno(dst), (uint64_t) -1, COPY_REFLINK);
if (r < 0)
goto fail;
+13 -12
View File
@@ -31,6 +31,7 @@
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
static void test_copy_file(void) {
@@ -52,7 +53,7 @@ static void test_copy_file(void) {
assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
assert_se(copy_file(fn, fn_copy, 0, 0644, 0) == 0);
assert_se(copy_file(fn, fn_copy, 0, 0644, 0, COPY_REFLINK) == 0);
assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
assert_se(streq(buf, "foo bar bar bar foo\n"));
@@ -77,8 +78,8 @@ static void test_copy_file_fd(void) {
assert_se(out_fd >= 0);
assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, true) < 0);
assert_se(copy_file_fd(in_fn, out_fd, true) >= 0);
assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0);
assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0);
assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1);
@@ -125,7 +126,7 @@ static void test_copy_tree(void) {
unixsockp = strjoina(original_dir, "unixsock");
assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
assert_se(copy_tree(original_dir, copy_dir, true) == 0);
assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0);
STRV_FOREACH(p, files) {
_cleanup_free_ char *buf = NULL, *f;
@@ -152,8 +153,8 @@ static void test_copy_tree(void) {
assert_se(stat(unixsockp, &st) >= 0);
assert_se(S_ISSOCK(st.st_mode));
assert_se(copy_tree(original_dir, copy_dir, false) < 0);
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0);
assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
(void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -172,7 +173,7 @@ static void test_copy_bytes(void) {
assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
r = copy_bytes(infd, pipefd[1], (uint64_t) -1, false);
r = copy_bytes(infd, pipefd[1], (uint64_t) -1, 0);
assert_se(r == 0);
r = read(pipefd[0], buf, sizeof(buf));
@@ -185,13 +186,13 @@ static void test_copy_bytes(void) {
assert_se(strneq(buf, buf2, r));
/* test copy_bytes with invalid descriptors */
r = copy_bytes(pipefd[0], pipefd[0], 1, false);
r = copy_bytes(pipefd[0], pipefd[0], 1, 0);
assert_se(r == -EBADF);
r = copy_bytes(pipefd[1], pipefd[1], 1, false);
r = copy_bytes(pipefd[1], pipefd[1], 1, 0);
assert_se(r == -EBADF);
r = copy_bytes(pipefd[1], infd, 1, false);
r = copy_bytes(pipefd[1], infd, 1, 0);
assert_se(r == -EBADF);
}
@@ -213,7 +214,7 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint
fd3 = mkostemp_safe(fn3);
assert_se(fd3 >= 0);
r = copy_bytes(fd, fd2, max_bytes, try_reflink);
r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0);
if (max_bytes == (uint64_t) -1)
assert_se(r == 0);
else
@@ -221,7 +222,7 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint
assert_se(lseek(fd2, 0, SEEK_SET) == 0);
r = copy_bytes(fd2, fd3, max_bytes, try_reflink);
r = copy_bytes(fd2, fd3, max_bytes, try_reflink ? COPY_REFLINK : 0);
if (max_bytes == (uint64_t) -1)
assert_se(r == 0);
else
+1 -1
View File
@@ -1170,7 +1170,7 @@ static int create_item(Item *i) {
return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
r = copy_tree(resolved, i->path, false);
r = copy_tree(resolved, i->path, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, COPY_REFLINK);
if (r == -EROFS && stat(i->path, &st) == 0)
r = -EEXIST;