diff options
Diffstat (limited to 'src')
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; } |