summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/btrfs-util.c2
-rw-r--r--src/basic/copy.c129
-rw-r--r--src/basic/copy.h22
-rw-r--r--src/basic/hostname-util.c2
-rw-r--r--src/basic/missing_syscall.h2
-rw-r--r--src/core/hostname-setup.c6
-rw-r--r--src/core/socket.c4
-rw-r--r--src/coredump/coredump.c2
-rw-r--r--src/firstboot/firstboot.c2
-rw-r--r--src/import/pull-raw.c4
-rw-r--r--src/import/pull-tar.c2
-rw-r--r--src/machine/image-dbus.c2
-rw-r--r--src/machine/machine-dbus.c49
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machine.c91
-rw-r--r--src/machine/machine.h2
-rw-r--r--src/machine/machinectl.c59
-rw-r--r--src/machine/machined-dbus.c21
-rw-r--r--src/machine/operation.c4
-rw-r--r--src/machine/org.freedesktop.machine1.conf8
-rw-r--r--src/network/networkd-address.c39
-rw-r--r--src/network/networkd-address.h5
-rw-r--r--src/network/networkd-network.c49
-rw-r--r--src/network/networkd-network.h11
-rw-r--r--src/network/networkd-route.c39
-rw-r--r--src/network/networkd-route.h5
-rw-r--r--src/nspawn/nspawn.c6
-rw-r--r--src/resolve/resolve-tool.c2
-rw-r--r--src/resolve/resolved-dns-cache.c127
-rw-r--r--src/resolve/resolved-dns-dnssec.c8
-rw-r--r--src/resolve/resolved-dns-query.c12
-rw-r--r--src/resolve/resolved-dns-server.c41
-rw-r--r--src/resolve/resolved-dns-server.h2
-rw-r--r--src/resolve/resolved-dns-stub.c47
-rw-r--r--src/resolve/resolved-dns-transaction.c39
-rw-r--r--src/resolve/resolved-dns-transaction.h2
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c25
-rw-r--r--src/resolve/resolved-manager.c78
-rw-r--r--src/shared/machine-image.c4
-rw-r--r--src/shared/pager.c2
-rw-r--r--src/systemctl/systemctl.c4
-rw-r--r--src/sysusers/sysusers.c2
-rw-r--r--src/test/test-copy.c25
-rw-r--r--src/tmpfiles/tmpfiles.c2
-rw-r--r--src/udev/udev-builtin-net_id.c58
45 files changed, 808 insertions, 240 deletions
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index 5f9e21dcba..5505499312 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -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;
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e9a7efd232..6273ac9b47 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -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;
diff --git a/src/basic/copy.h b/src/basic/copy.h
index b5d08ebafe..4f3e11423e 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -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);
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index e44a357287..a94037b303 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -55,7 +55,7 @@ char* gethostname_malloc(void) {
assert_se(uname(&u) >= 0);
if (isempty(u.nodename) || streq(u.nodename, "(none)"))
- return strdup(u.sysname);
+ return strdup(FALLBACK_HOSTNAME);
return strdup(u.nodename);
}
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index e6fd67cb9d..9fc4156564 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -194,6 +194,8 @@ static inline pid_t raw_getpid(void) {
# define __NR_renameat2 316
# elif defined __arm__
# define __NR_renameat2 382
+# elif defined __aarch64__
+# define __NR_renameat2 276
# elif defined _MIPS_SIM
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define __NR_renameat2 4351
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 68be52856b..845e31e1c5 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -31,10 +31,10 @@
#include "util.h"
int hostname_setup(void) {
- int r;
_cleanup_free_ char *b = NULL;
- const char *hn;
bool enoent = false;
+ const char *hn;
+ int r;
r = read_hostname_config("/etc/hostname", &b);
if (r < 0) {
@@ -56,7 +56,7 @@ int hostname_setup(void) {
if (enoent)
log_info("No hostname configured.");
- hn = "localhost";
+ hn = FALLBACK_HOSTNAME;
}
r = sethostname_idempotent(hn);
diff --git a/src/core/socket.c b/src/core/socket.c
index a7b9ada65c..84b7a1a82d 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -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) {
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 838396b0c2..0fa830c33e 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -354,7 +354,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;
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index fd7051f21e..bc16290c72 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -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);
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index ef7fb6ac42..60a769e944 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -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");
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 375ee778e2..91833d6174 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -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)
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index d5051007fc..2f69e2c7b7 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -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);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index af745b6567..36568b65ef 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -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),
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index c513783480..2aa7b4ce06 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -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);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 067a7e2866..d3433d9b96 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -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",
diff --git a/src/machine/machine.h b/src/machine/machine.h
index e5d75361a9..6bdb204ed6 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -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);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index fe4f1b7726..28384286fb 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -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);
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index fd9e5b56fc..c9b92d2765 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -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),
diff --git a/src/machine/operation.c b/src/machine/operation.c
index c966d0d21c..f7d5310f44 100644
--- a/src/machine/operation.c
+++ b/src/machine/operation.c
@@ -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)
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index 82ebfba50c..daa365a9dd 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -66,6 +66,10 @@
<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"/>
<allow send_destination="org.freedesktop.machine1"
@@ -150,6 +154,10 @@
<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"/>
<allow send_destination="org.freedesktop.machine1"
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index ffd2e18a45..9d0311d76f 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -53,15 +53,21 @@ int address_new(Address **ret) {
return 0;
}
-int address_new_static(Network *network, unsigned section, Address **ret) {
+int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
+ _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
_cleanup_address_free_ Address *address = NULL;
int r;
assert(network);
assert(ret);
+ assert(!!filename == (section_line > 0));
- if (section) {
- address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ address = hashmap_get(network->addresses_by_section, n);
if (address) {
*ret = address;
address = NULL;
@@ -77,9 +83,9 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
if (r < 0)
return r;
- if (section) {
- address->section = section;
- hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address);
+ if (filename) {
+ address->section = n;
+ hashmap_put(network->addresses_by_section, n, address);
}
address->network = network;
@@ -88,6 +94,7 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
*ret = address;
address = NULL;
+ n = NULL;
return 0;
}
@@ -101,8 +108,10 @@ void address_free(Address *address) {
assert(address->network->n_static_addresses > 0);
address->network->n_static_addresses--;
- if (address->section)
- hashmap_remove(address->network->addresses_by_section, UINT_TO_PTR(address->section));
+ if (address->section) {
+ hashmap_remove(address->network->addresses_by_section, address->section);
+ network_config_section_free(address->section);
+ }
}
if (address->link) {
@@ -676,7 +685,7 @@ int config_parse_broadcast(
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -723,10 +732,10 @@ int config_parse_address(const char *unit,
if (streq(section, "Network")) {
/* we are not in an Address section, so treat
* this as the special '0' section */
- section_line = 0;
- }
+ r = address_new_static(network, NULL, 0, &n);
+ } else
+ r = address_new_static(network, filename, section_line, &n);
- r = address_new_static(network, section_line, &n);
if (r < 0)
return r;
@@ -805,7 +814,7 @@ int config_parse_label(
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -844,7 +853,7 @@ int config_parse_lifetime(const char *unit,
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -891,7 +900,7 @@ int config_parse_address_flags(const char *unit,
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index bc3b4fc7f3..71a07ea7a3 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -33,10 +33,11 @@ typedef struct Address Address;
typedef struct Network Network;
typedef struct Link Link;
+typedef struct NetworkConfigSection NetworkConfigSection;
struct Address {
Network *network;
- unsigned section;
+ NetworkConfigSection *section;
Link *link;
@@ -62,7 +63,7 @@ struct Address {
LIST_FIELDS(Address, addresses);
};
-int address_new_static(Network *network, unsigned section, Address **ret);
+int address_new_static(Network *network, const char *filename, unsigned section, Address **ret);
int address_new(Address **ret);
void address_free(Address *address);
int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 92062ca00c..ab372568de 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -36,6 +36,49 @@
#include "string-util.h"
#include "util.h"
+static void network_config_hash_func(const void *p, struct siphash *state) {
+ const NetworkConfigSection *c = p;
+
+ siphash24_compress(c->filename, strlen(c->filename), state);
+ siphash24_compress(&c->line, sizeof(c->line), state);
+}
+
+static int network_config_compare_func(const void *a, const void *b) {
+ const NetworkConfigSection *x = a, *y = b;
+ int r;
+
+ r = strcmp(x->filename, y->filename);
+ if (r != 0)
+ return r;
+
+ return y->line - x->line;
+}
+
+const struct hash_ops network_config_hash_ops = {
+ .hash = network_config_hash_func,
+ .compare = network_config_compare_func,
+};
+
+int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
+ NetworkConfigSection *cs;
+
+ cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
+ if (!cs)
+ return -ENOMEM;
+
+ strcpy(cs->filename, filename);
+ cs->line = line;
+
+ *s = cs;
+ cs = NULL;
+
+ return 0;
+}
+
+void network_config_section_free(NetworkConfigSection *cs) {
+ free(cs);
+}
+
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
@@ -76,11 +119,11 @@ static int network_load_one(Manager *manager, const char *filename) {
if (!network->stacked_netdevs)
return log_oom();
- network->addresses_by_section = hashmap_new(NULL);
+ network->addresses_by_section = hashmap_new(&network_config_hash_ops);
if (!network->addresses_by_section)
return log_oom();
- network->routes_by_section = hashmap_new(NULL);
+ network->routes_by_section = hashmap_new(&network_config_hash_ops);
if (!network->routes_by_section)
return log_oom();
@@ -385,7 +428,7 @@ int network_apply(Network *network, Link *link) {
if (network->ipv4ll_route) {
Route *route;
- r = route_new_static(network, 0, &route);
+ r = route_new_static(network, "Network", 0, &route);
if (r < 0)
return r;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index f06828a899..4ce066a764 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -82,6 +82,17 @@ typedef struct DUID {
uint8_t raw_data[MAX_DUID_LEN];
} DUID;
+typedef struct NetworkConfigSection {
+ unsigned line;
+ char filename[];
+} NetworkConfigSection;
+
+int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
+void network_config_section_free(NetworkConfigSection *network);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
+#define _cleanup_network_config_section_free_ _cleanup_(network_config_section_freep)
+
typedef struct Manager Manager;
struct Network {
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index bde26a42d4..a8ba293ae8 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -77,15 +77,21 @@ int route_new(Route **ret) {
return 0;
}
-int route_new_static(Network *network, unsigned section, Route **ret) {
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
+ _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
_cleanup_route_free_ Route *route = NULL;
int r;
assert(network);
assert(ret);
+ assert(!!filename == (section_line > 0));
- if (section) {
- route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section));
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ route = hashmap_get(network->routes_by_section, n);
if (route) {
*ret = route;
route = NULL;
@@ -103,10 +109,11 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
route->protocol = RTPROT_STATIC;
- if (section) {
- route->section = section;
+ if (filename) {
+ route->section = n;
+ n = NULL;
- r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route);
+ r = hashmap_put(network->routes_by_section, n, route);
if (r < 0)
return r;
}
@@ -132,9 +139,11 @@ void route_free(Route *route) {
route->network->n_static_routes--;
if (route->section)
- hashmap_remove(route->network->routes_by_section, UINT_TO_PTR(route->section));
+ hashmap_remove(route->network->routes_by_section, route->section);
}
+ network_config_section_free(route->section);
+
if (route->link) {
set_remove(route->link->routes, route);
set_remove(route->link->routes_foreign, route);
@@ -673,10 +682,10 @@ int config_parse_gateway(const char *unit,
if (streq(section, "Network")) {
/* we are not in an Route section, so treat
* this as the special '0' section */
- section_line = 0;
- }
+ r = route_new_static(network, NULL, 0, &n);
+ } else
+ r = route_new_static(network, filename, section_line, &n);
- r = route_new_static(network, section_line, &n);
if (r < 0)
return r;
@@ -715,7 +724,7 @@ int config_parse_preferred_src(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -757,7 +766,7 @@ int config_parse_destination(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -835,7 +844,7 @@ int config_parse_route_priority(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -872,7 +881,7 @@ int config_parse_route_scope(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -913,7 +922,7 @@ int config_parse_route_table(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index 02f0b27675..4ebfa0f0bd 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -20,12 +20,13 @@
***/
typedef struct Route Route;
+typedef struct NetworkConfigSection NetworkConfigSection;
#include "networkd-network.h"
struct Route {
Network *network;
- unsigned section;
+ NetworkConfigSection *section;
Link *link;
@@ -52,7 +53,7 @@ struct Route {
LIST_FIELDS(Route, routes);
};
-int route_new_static(Network *network, unsigned section, Route **ret);
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index b172b44933..efd3b014a3 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -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);
}
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 3cec36817a..32537ce6e8 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -38,7 +38,7 @@
#include "strv.h"
#include "terminal-util.h"
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
+#define DNS_CALL_TIMEOUT_USEC (90*USEC_PER_SEC)
static int arg_family = AF_UNSPEC;
static int arg_ifindex = 0;
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index c43a7865dc..46a25c2bee 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -34,6 +34,10 @@
/* We never keep any item longer than 2h in our cache */
#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
+/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
+ * now) */
+#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
+
typedef enum DnsCacheItemType DnsCacheItemType;
typedef struct DnsCacheItem DnsCacheItem;
@@ -41,12 +45,14 @@ enum DnsCacheItemType {
DNS_CACHE_POSITIVE,
DNS_CACHE_NODATA,
DNS_CACHE_NXDOMAIN,
+ DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */
};
struct DnsCacheItem {
DnsCacheItemType type;
DnsResourceKey *key;
DnsResourceRecord *rr;
+ int rcode;
usec_t until;
bool authenticated:1;
@@ -60,6 +66,27 @@ struct DnsCacheItem {
LIST_FIELDS(DnsCacheItem, by_key);
};
+static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
+ assert(item);
+
+ switch (item->type) {
+
+ case DNS_CACHE_POSITIVE:
+ return "POSITIVE";
+
+ case DNS_CACHE_NODATA:
+ return "NODATA";
+
+ case DNS_CACHE_NXDOMAIN:
+ return "NXDOMAIN";
+
+ case DNS_CACHE_RCODE:
+ return dns_rcode_to_string(item->rcode);
+ }
+
+ return NULL;
+}
+
static void dns_cache_item_free(DnsCacheItem *i) {
if (!i)
return;
@@ -484,7 +511,6 @@ static int dns_cache_put_negative(
assert(c);
assert(key);
- assert(soa);
assert(owner_address);
/* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
@@ -495,13 +521,17 @@ static int dns_cache_put_negative(
if (dns_type_is_pseudo(key->type))
return 0;
- if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
- log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
- }
+ if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
+ if (!soa)
+ return 0;
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
+ /* For negative replies, check if we have a TTL of a SOA */
+ if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
+ log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
+ dns_resource_key_to_string(key, key_str, sizeof key_str));
+ return 0;
+ }
+ } else if (rcode != DNS_RCODE_SERVFAIL)
return 0;
r = dns_cache_init(c);
@@ -514,12 +544,17 @@ static int dns_cache_put_negative(
if (!i)
return -ENOMEM;
- i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
- i->until = calculate_until(soa, nsec_ttl, timestamp, true);
+ i->type =
+ rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
+ rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE;
+ i->until =
+ i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
+ calculate_until(soa, nsec_ttl, timestamp, true);
i->authenticated = authenticated;
i->owner_family = owner_family;
i->owner_address = *owner_address;
i->prioq_idx = PRIOQ_IDX_NULL;
+ i->rcode = rcode;
if (i->type == DNS_CACHE_NXDOMAIN) {
/* NXDOMAIN entries should apply equally to all types, so we use ANY as
@@ -543,7 +578,7 @@ static int dns_cache_put_negative(
return r;
log_debug("Added %s cache entry for %s "USEC_FMT"s",
- i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN",
+ dns_cache_item_type_to_string(i),
dns_resource_key_to_string(i->key, key_str, sizeof key_str),
(i->until - timestamp) / USEC_PER_SEC);
@@ -615,6 +650,7 @@ int dns_cache_put(
const union in_addr_union *owner_address) {
DnsResourceRecord *soa = NULL, *rr;
+ bool weird_rcode = false;
DnsAnswerFlags flags;
unsigned cache_keys;
int r, ifindex;
@@ -624,18 +660,28 @@ int dns_cache_put(
dns_cache_remove_previous(c, key, answer);
- /* We only care for positive replies and NXDOMAINs, on all
- * other replies we will simply flush the respective entries,
- * and that's it */
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
- return 0;
+ /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
+ * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
+ * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
+ * short time.) */
- if (dns_answer_size(answer) <= 0) {
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
+ if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
- log_debug("Not caching negative entry without a SOA record: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
+ if (dns_answer_size(answer) <= 0) {
+ char key_str[DNS_RESOURCE_KEY_STRING_MAX];
+
+ log_debug("Not caching negative entry without a SOA record: %s",
+ dns_resource_key_to_string(key, key_str, sizeof key_str));
+ return 0;
+ }
+
+ } else {
+ /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
+ * beneficial. */
+ if (rcode != DNS_RCODE_SERVFAIL)
+ return 0;
+
+ weird_rcode = true;
}
cache_keys = dns_answer_size(answer);
@@ -690,19 +736,20 @@ int dns_cache_put(
if (r > 0)
return 0;
- /* See https://tools.ietf.org/html/rfc2308, which say that a
- * matching SOA record in the packet is used to enable
- * negative caching. */
+ /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
+ * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
+ * regardless of a SOA. */
r = dns_answer_find_soa(answer, key, &soa, &flags);
if (r < 0)
goto fail;
- if (r == 0)
- return 0;
-
- /* Refuse using the SOA data if it is unsigned, but the key is
- * signed */
- if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ if (r == 0 && !weird_rcode)
return 0;
+ if (r > 0) {
+ /* Refuse using the SOA data if it is unsigned, but the key is
+ * signed */
+ if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ return 0;
+ }
r = dns_cache_put_negative(
c,
@@ -799,6 +846,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
DnsCacheItem *j, *first, *nsec = NULL;
bool have_authenticated = false, have_non_authenticated = false;
usec_t current;
+ int found_rcode = -1;
assert(c);
assert(key);
@@ -817,6 +865,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = false;
+
return 0;
}
@@ -831,6 +881,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = false;
+
return 0;
}
@@ -842,6 +894,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
n++;
} else if (j->type == DNS_CACHE_NXDOMAIN)
nxdomain = true;
+ else if (j->type == DNS_CACHE_RCODE)
+ found_rcode = j->rcode;
if (j->authenticated)
have_authenticated = true;
@@ -849,6 +903,19 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
have_non_authenticated = true;
}
+ if (found_rcode >= 0) {
+ log_debug("RCODE %s cache hit for %s",
+ dns_rcode_to_string(found_rcode),
+ dns_resource_key_to_string(key, key_str, sizeof(key_str)));
+
+ *ret = NULL;
+ *rcode = found_rcode;
+ *authenticated = false;
+
+ c->n_hit++;
+ return 1;
+ }
+
if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
/* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
* the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
@@ -1042,7 +1109,7 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
fputs(" -- ", f);
- fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
+ fputs(dns_cache_item_type_to_string(j), f);
fputc('\n', f);
}
}
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 51327105d0..eddab58a81 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -1710,7 +1710,8 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
}
static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
- const char *common_suffix, *wc;
+ _cleanup_free_ char *wc = NULL;
+ const char *common_suffix;
int r;
assert(rr);
@@ -1734,7 +1735,10 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
if (r <= 0)
return r;
- wc = strjoina("*.", common_suffix);
+ r = dns_name_concat("*", common_suffix, &wc);
+ if (r < 0)
+ return r;
+
return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
}
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index c58845c3b6..2b091e6c45 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -28,7 +28,7 @@
#include "string-util.h"
/* How long to wait for the query in total */
-#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
+#define QUERY_TIMEOUT_USEC (60 * USEC_PER_SEC)
#define CNAME_MAX 8
#define QUERIES_MAX 2048
@@ -811,6 +811,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = 0;
q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_authenticated = false;
q->answer_errno = c->error_code;
}
@@ -847,15 +848,18 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
continue;
default:
- /* Any kind of failure? Store the data away,
- * if there's nothing stored yet. */
-
+ /* Any kind of failure? Store the data away, if there's nothing stored yet. */
if (state == DNS_TRANSACTION_SUCCESS)
continue;
+ /* If there's already an authenticated negative reply stored, then prefer that over any unauthenticated one */
+ if (q->answer_authenticated && !t->answer_authenticated)
+ continue;
+
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = t->answer_rcode;
q->answer_dnssec_result = t->answer_dnssec_result;
+ q->answer_authenticated = t->answer_authenticated;
q->answer_errno = t->answer_errno;
state = t->state;
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 072cbfca1a..5498f7b9cb 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -28,7 +28,7 @@
#include "string-util.h"
/* After how much time to repeat classic DNS requests */
-#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
+#define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC)
#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
/* The amount of time to wait before retrying with a full feature set */
@@ -399,12 +399,24 @@ static bool dns_server_grace_period_expired(DnsServer *s) {
}
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
+ DnsServerFeatureLevel best;
+
assert(s);
- if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
- dns_server_grace_period_expired(s)) {
+ /* Determine the best feature level we care about. If DNSSEC mode is off there's no point in using anything
+ * better than EDNS0, hence don't even try. */
+ best = dns_server_get_dnssec_mode(s) == DNSSEC_NO ?
+ DNS_SERVER_FEATURE_LEVEL_EDNS0 :
+ DNS_SERVER_FEATURE_LEVEL_BEST;
+
+ /* Clamp the feature level the highest level we care about. The DNSSEC mode might have changed since the last
+ * time, hence let's downgrade if we are still at a higher level. */
+ if (s->possible_feature_level > best)
+ s->possible_feature_level = best;
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
+ if (s->possible_feature_level < best && dns_server_grace_period_expired(s)) {
+
+ s->possible_feature_level = best;
dns_server_reset_counters(s);
@@ -415,6 +427,8 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
dns_server_feature_level_to_string(s->possible_feature_level),
dns_server_string(s));
+ dns_server_flush_cache(s);
+
} else if (s->possible_feature_level <= s->verified_feature_level)
s->possible_feature_level = s->verified_feature_level;
else {
@@ -792,6 +806,25 @@ DnssecMode dns_server_get_dnssec_mode(DnsServer *s) {
return manager_get_dnssec_mode(s->manager);
}
+void dns_server_flush_cache(DnsServer *s) {
+ DnsServer *current;
+ DnsScope *scope;
+
+ assert(s);
+
+ /* Flush the cache of the scope this server belongs to */
+
+ current = s->link ? s->link->current_dns_server : s->manager->current_dns_server;
+ if (current != s)
+ return;
+
+ scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope;
+ if (!scope)
+ return;
+
+ dns_cache_flush(&scope->cache);
+}
+
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 406282d864..bc95d53c6a 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -149,3 +149,5 @@ DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;
+
+void dns_server_flush_cache(DnsServer *s);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 7d43825960..7afbfedfb0 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -94,9 +94,18 @@ static int dns_stub_finish_reply_packet(
assert(p);
- /* If the client didn't do EDNS, clamp the rcode to 4 bit */
- if (!add_opt && rcode > 0xF)
- rcode = DNS_RCODE_SERVFAIL;
+ if (!add_opt) {
+ /* If the client can't to EDNS0, don't do DO either */
+ edns0_do = false;
+
+ /* If the client didn't do EDNS, clamp the rcode to 4 bit */
+ if (rcode > 0xF)
+ rcode = DNS_RCODE_SERVFAIL;
+ }
+
+ /* Don't set the AD bit unless DO is on, too */
+ if (!edns0_do)
+ ad = false;
DNS_PACKET_HEADER(p)->id = id;
@@ -162,7 +171,7 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
return 0;
}
-static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) {
+static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
int r;
@@ -173,7 +182,7 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco
if (r < 0)
return log_debug_errno(r, "Failed to make failure packet: %m");
- r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), false);
+ r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), authenticated);
if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m");
@@ -198,7 +207,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
r = dns_query_process_cname(q);
if (r == -ELOOP) {
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
break;
}
if (r < 0) {
@@ -214,7 +223,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
q->answer_rcode,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
- DNS_PACKET_DO(q->request_dns_packet) && dns_query_fully_authenticated(q));
+ dns_query_fully_authenticated(q));
if (r < 0) {
log_debug_errno(r, "Failed to finish reply packet: %m");
break;
@@ -224,11 +233,11 @@ static void dns_stub_query_complete(DnsQuery *q) {
break;
case DNS_TRANSACTION_RCODE_FAILURE:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
break;
case DNS_TRANSACTION_NOT_FOUND:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
break;
case DNS_TRANSACTION_TIMEOUT:
@@ -244,7 +253,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
break;
case DNS_TRANSACTION_NULL:
@@ -291,52 +300,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
in_addr_is_localhost(p->family, &p->destination) <= 0) {
log_error("Got packet on unexpected IP range, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
goto fail;
}
if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
log_debug("Got EDNS OPT field with unsupported version number.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
goto fail;
}
if (dns_type_is_obsolete(p->question->keys[0]->type)) {
log_debug("Got message with obsolete key type, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
log_debug("Got request for zone transfer, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
if (!DNS_PACKET_RD(p)) {
/* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
log_debug("Got request with recursion disabled, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
goto fail;
}
if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
log_debug("Got request with DNSSEC CD bit set, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
if (r < 0) {
log_error_errno(r, "Failed to generate query object: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
@@ -356,7 +365,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
r = dns_query_go(q);
if (r < 0) {
log_error_errno(r, "Failed to start query: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 9687409663..ecd7068683 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -31,6 +31,7 @@
#include "string-table.h"
#define TRANSACTIONS_MAX 4096
+#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
static void dns_transaction_reset_answer(DnsTransaction *t) {
assert(t);
@@ -832,7 +833,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* should hence not attempt to access the query or transaction
* after calling this function. */
- log_debug("Processing incoming packet on transaction %" PRIu16".", t->id);
+ log_debug("Processing incoming packet on transaction %" PRIu16". (rcode=%s)", t->id, dns_rcode_to_string(DNS_PACKET_RCODE(p)));
switch (t->scope->protocol) {
@@ -910,9 +911,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* Request failed, immediately try again with reduced features */
- if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) {
- /* This was already at the lowest possible feature level? If so, we can't downgrade
- * this transaction anymore, hence let's process the response, and accept the rcode. */
+ if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP) {
+ /* This was already at UDP feature level? If so, it doesn't make sense to downgrade
+ * this transaction anymore, hence let's process the response, and accept the
+ * rcode. Note that we don't retry on TCP, since that's a suitable way to mitigate
+ * packet loss, but is not going to give us better rcodes should we actually have
+ * managed to get them already at UDP level. */
+
log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
break;
}
@@ -1135,7 +1140,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
return r;
if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP)
- return -EAGAIN;
+ return -EAGAIN; /* Sorry, can't do UDP, try TCP! */
if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
return -EOPNOTSUPP;
@@ -1212,9 +1217,17 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
assert(t);
assert(t->scope);
+
switch (t->scope->protocol) {
case DNS_PROTOCOL_DNS:
+
+ /* When we do TCP, grant a much longer timeout, as in this case there's no need for us to quickly
+ * resend, as the kernel does that anyway for us, and we really don't want to interrupt it in that
+ * needlessly. */
+ if (t->stream)
+ return TRANSACTION_TCP_TIMEOUT_USEC;
+
assert(t->server);
return t->server->resend_timeout;
@@ -1579,7 +1592,7 @@ int dns_transaction_go(DnsTransaction *t) {
r = dns_transaction_emit_udp(t);
if (r == -EMSGSIZE)
log_debug("Sending query via TCP since it is too large.");
- if (r == -EAGAIN)
+ else if (r == -EAGAIN)
log_debug("Sending query via TCP since server doesn't support UDP.");
if (r == -EMSGSIZE || r == -EAGAIN)
r = dns_transaction_open_tcp(t);
@@ -1996,8 +2009,18 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
r = dns_resource_key_match_rr(t->key, rr, NULL);
if (r < 0)
return r;
- if (r == 0)
- continue;
+ if (r == 0) {
+ /* Hmm, so this SOA RR doesn't match our original question. In this case, maybe this is
+ * a negative reply, and we need the a SOA RR's TTL in order to cache a negative entry?
+ * If so, we need to validate it, too. */
+
+ r = dns_answer_match_key(t->answer, t->key, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) /* positive reply, we won't need the SOA and hence don't need to validate
+ * it. */
+ continue;
+ }
r = dnssec_has_rrsig(t->answer, rr->key);
if (r < 0)
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index c4fb51958d..a8d97738ef 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -178,7 +178,7 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
/* Maximum attempts to send DNS requests, across all DNS servers */
-#define DNS_TRANSACTION_ATTEMPTS_MAX 16
+#define DNS_TRANSACTION_ATTEMPTS_MAX 24
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index d8529f8317..7e08cba4e1 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -547,10 +547,33 @@ int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *ke
}
int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
+ int r;
+
assert(d);
assert(name);
- return set_contains(d->negative_by_name, name);
+ for (;;) {
+ /* If the domain is listed as-is in the NTA database, then that counts */
+ if (set_contains(d->negative_by_name, name))
+ return true;
+
+ /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC
+ * 7646, section 1.1 */
+ if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)))
+ return false;
+
+ if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name)))
+ return false;
+
+ /* And now, let's look at the parent, and check that too */
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ }
+
+ return false;
}
static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 791c125613..c4e4409fe3 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -332,19 +332,18 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
assert(llmnr_hostname);
assert(mdns_hostname);
- /* Extract and normalize the first label of the locally
- * configured hostname, and check it's not "localhost". */
+ /* Extract and normalize the first label of the locally configured hostname, and check it's not "localhost". */
- h = gethostname_malloc();
- if (!h)
- return log_oom();
+ r = gethostname_strict(&h);
+ if (r < 0)
+ return log_debug_errno(r, "Can't determine system hostname: %m");
p = h;
r = dns_label_unescape(&p, label, sizeof(label));
if (r < 0)
return log_error_errno(r, "Failed to unescape host name: %m");
if (r == 0) {
- log_error("Couldn't find a single label in hosntame.");
+ log_error("Couldn't find a single label in hostname.");
return -EINVAL;
}
@@ -381,6 +380,57 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
return 0;
}
+static const char *fallback_hostname(void) {
+
+ /* Determine the fall back hostname. For exposing this system to the outside world, we cannot have it to be
+ * "localhost" even if that's the compiled in hostname. In this case, let's revert to "linux" instead. */
+
+ if (is_localhost(FALLBACK_HOSTNAME))
+ return "linux";
+
+ return FALLBACK_HOSTNAME;
+}
+
+static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
+ _cleanup_free_ char *n = NULL, *m = NULL;
+ char label[DNS_LABEL_MAX], *h;
+ const char *p;
+ int r;
+
+ assert(full_hostname);
+ assert(llmnr_hostname);
+ assert(mdns_hostname);
+
+ p = fallback_hostname();
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape fallback host name: %m");
+
+ assert(r > 0); /* The fallback hostname must have at least one label */
+
+ r = dns_label_escape_new(label, r, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to escape fallback hostname: %m");
+
+ r = dns_name_concat(n, "local", &m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to concatenate mDNS hostname: %m");
+
+ h = strdup(fallback_hostname());
+ if (!h)
+ return log_oom();
+
+ *llmnr_hostname = n;
+ n = NULL;
+
+ *mdns_hostname = m;
+ m = NULL;
+
+ *full_hostname = h;
+
+ return 0;
+}
+
static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
_cleanup_free_ char *full_hostname = NULL, *llmnr_hostname = NULL, *mdns_hostname = NULL;
Manager *m = userdata;
@@ -432,19 +482,11 @@ static int manager_watch_hostname(Manager *m) {
r = determine_hostname(&m->full_hostname, &m->llmnr_hostname, &m->mdns_hostname);
if (r < 0) {
- log_info("Defaulting to hostname 'linux'.");
-
- m->full_hostname = strdup("linux");
- if (!m->full_hostname)
- return log_oom();
-
- m->llmnr_hostname = strdup("linux");
- if (!m->llmnr_hostname)
- return log_oom();
+ log_info("Defaulting to hostname '%s'.", fallback_hostname());
- m->mdns_hostname = strdup("linux.local");
- if (!m->mdns_hostname)
- return log_oom();
+ r = make_fallback_hostnames(&m->full_hostname, &m->llmnr_hostname, &m->mdns_hostname);
+ if (r < 0)
+ return r;
} else
log_info("Using system hostname '%s'.", m->full_hostname);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 7bc5c0a128..d96ff44e66 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -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:
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 09672a4abf..af667a83f4 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -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);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 2336ae34f4..60f8bc3df5 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -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);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 17b966eb52..4a0a49f2bb 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -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;
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index e65516f080..ed6725611d 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -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
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index c4f4d46ca1..08a138b1b2 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -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;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 5be158f527..b5a88c667c 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -34,7 +34,8 @@
*
* Type of names:
* b<number> — BCMA bus core number
- * c<bus_id> — CCW bus group name, without leading zeros [s390]
+ * c<bus_id> — bus id of a grouped CCW or CCW device,
+ * with all leading zeros stripped [s390]
* o<index>[n<phys_port_name>|d<dev_port>]
* — on-board device index number
* s<slot>[f<function>][n<phys_port_name>|d<dev_port>]
@@ -87,6 +88,11 @@
* /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
* ID_NET_NAME_MAC=enxd626b3450fb5
* ID_NET_NAME_PATH=enp0s29u1u2
+ *
+ * s390 grouped CCW interface:
+ * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0
+ * ID_NET_NAME_MAC=enx026d3c00000a
+ * ID_NET_NAME_PATH=encf5f0
*/
#include <errno.h>
@@ -115,7 +121,7 @@ enum netname_type{
NET_USB,
NET_BCMA,
NET_VIRTIO,
- NET_CCWGROUP,
+ NET_CCW,
};
struct netnames {
@@ -132,9 +138,21 @@ struct netnames {
char usb_ports[IFNAMSIZ];
char bcma_core[IFNAMSIZ];
- char ccw_group[IFNAMSIZ];
+ char ccw_busid[IFNAMSIZ];
};
+/* skip intermediate virtio devices */
+static struct udev_device *skip_virtio(struct udev_device *dev) {
+ struct udev_device *parent = dev;
+
+ /* there can only ever be one virtio bus per parent device, so we can
+ safely ignore any virtio buses. see
+ <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
+ while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
+ parent = udev_device_get_parent(parent);
+ return parent;
+}
+
/* retrieve on-board index number and label from firmware */
static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
unsigned dev_port = 0;
@@ -308,12 +326,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
assert(names);
parent = udev_device_get_parent(dev);
-
- /* there can only ever be one virtio bus per parent device, so we can
- safely ignore any virtio buses. see
- <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
- while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
- parent = udev_device_get_parent(parent);
+ /* skip virtio subsystem if present */
+ parent = skip_virtio(parent);
if (!parent)
return -ENOENT;
@@ -414,21 +428,26 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
struct udev_device *cdev;
const char *bus_id;
size_t bus_id_len;
+ size_t bus_id_start;
int rc;
+ char *subsys;
assert(dev);
assert(names);
/* Retrieve the associated CCW device */
cdev = udev_device_get_parent(dev);
+ /* skip virtio subsystem if present */
+ cdev = skip_virtio(cdev);
if (!cdev)
return -ENOENT;
- /* Network devices are always grouped CCW devices */
- if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev)))
+ /* Network devices are either single or grouped CCW devices */
+ subsys = udev_device_get_subsystem(cdev);
+ if (!STRPTR_IN_SET(subsys, "ccwgroup", "ccw"))
return -ENOENT;
- /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely
+ /* Retrieve bus-ID of the CCW device. The bus-ID uniquely
* identifies the network device on the Linux on System z channel
* subsystem. Note that the bus-ID contains lowercase characters.
*/
@@ -447,14 +466,15 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
/* Strip leading zeros from the bus id for aesthetic purposes. This
* keeps the ccw names stable, yet much shorter in general case of
* bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is
- * not prepended when it is zero.
+ * not prepended when it is zero. Preserve the last 0 for 0.0.0000.
*/
- bus_id += strspn(bus_id, ".0");
+ bus_id_start = strspn(bus_id, ".0");
+ bus_id += bus_id_start < bus_id_len ? bus_id_start : bus_id_len - 1;
/* Store the CCW bus-ID for use as network device name */
- rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id);
- if (rc >= 0 && rc < (int)sizeof(names->ccw_group))
- names->type = NET_CCWGROUP;
+ rc = snprintf(names->ccw_busid, sizeof(names->ccw_busid), "c%s", bus_id);
+ if (rc >= 0 && rc < (int)sizeof(names->ccw_busid))
+ names->type = NET_CCW;
return 0;
}
@@ -564,10 +584,10 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
/* get path names for Linux on System z network devices */
err = names_ccw(dev, &names);
- if (err >= 0 && names.type == NET_CCWGROUP) {
+ if (err >= 0 && names.type == NET_CCW) {
char str[IFNAMSIZ];
- if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str))
+ if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_busid) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
goto out;
}