diff --git a/src/basic/chase.c b/src/basic/chase.c index 7d87f998f1..318454ca88 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -162,10 +162,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int /* If we get AT_FDCWD or dir_fd points to "/", then we always resolve symlinks relative to * the host's root. Hence, CHASE_AT_RESOLVE_IN_ROOT is meaningless. */ - if (dir_fd >= 0) - r = dir_fd_is_root(dir_fd); - else - r = true; + r = dir_fd_is_root_or_cwd(dir_fd); if (r < 0) return r; if (r > 0) diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 91f3d7fe9d..655ad29284 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include #include @@ -101,6 +102,9 @@ int read_nr_open(void); int fd_get_diskseq(int fd, uint64_t *ret); int dir_fd_is_root(int dir_fd); +static inline int dir_fd_is_root_or_cwd(int dir_fd) { + return dir_fd == AT_FDCWD ? true : dir_fd_is_root(dir_fd); +} /* The maximum length a buffer for a /proc/self/fd/ path needs */ #define PROC_FD_PATH_MAX \ diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 3fa1585aa3..76c9d1c051 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -43,6 +43,7 @@ bool id128_is_valid(const char *s) { int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) { char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ + sd_id128_t id; ssize_t l; int r; @@ -98,8 +99,18 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) { return -EUCLEAN; } - r = sd_id128_from_string(buffer, ret); - return r == -EINVAL ? -EUCLEAN : r; + r = sd_id128_from_string(buffer, &id); + if (r == -EINVAL) + return -EUCLEAN; + if (r < 0) + return r; + + if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) + return -ENOMEDIUM; + + if (ret) + *ret = id; + return 0; } int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) { @@ -123,6 +134,9 @@ int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) { assert(fd >= 0); assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID)); + if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) + return -ENOMEDIUM; + if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) { assert_se(sd_id128_to_string(id, buffer)); sz = SD_ID128_STRING_MAX; diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h index fde58a5228..7bcbd8e558 100644 --- a/src/libsystemd/sd-id128/id128-util.h +++ b/src/libsystemd/sd-id128/id128-util.h @@ -17,6 +17,7 @@ typedef enum Id128Flag { ID128_FORMAT_ANY = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID, ID128_SYNC_ON_WRITE = 1 << 2, /* Sync the file after write. Used only when writing an ID. */ + ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */ } Id128Flag; int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret); @@ -31,6 +32,9 @@ static inline int id128_write(const char *path, Id128Flag f, sd_id128_t id) { return id128_write_at(AT_FDCWD, path, f, id); } +int id128_get_machine(const char *root, sd_id128_t *ret); +int id128_get_machine_at(int rfd, sd_id128_t *ret); + void id128_hash_func(const sd_id128_t *p, struct siphash *state); int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 5ce36cf2fc..6a82a7f7b8 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -7,6 +7,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "chase.h" #include "fd-util.h" #include "hexdecoct.h" #include "hmac.h" @@ -15,6 +16,7 @@ #include "macro.h" #include "missing_syscall.h" #include "missing_threads.h" +#include "path-util.h" #include "random-util.h" #include "stat-util.h" #include "user-util.h" @@ -126,12 +128,9 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { int r; if (sd_id128_is_null(saved_machine_id)) { - r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN, &saved_machine_id); + r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, &saved_machine_id); if (r < 0) return r; - - if (sd_id128_is_null(saved_machine_id)) - return -ENOMEDIUM; } if (ret) @@ -139,19 +138,48 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { return 0; } +int id128_get_machine_at(int rfd, sd_id128_t *ret) { + _cleanup_close_ int fd = -EBADF; + int r; + + assert(rfd >= 0 || rfd == AT_FDCWD); + + r = dir_fd_is_root_or_cwd(rfd); + if (r < 0) + return r; + if (r > 0) + return sd_id128_get_machine(ret); + + fd = chase_and_openat(rfd, "/etc/machine-id", CHASE_AT_RESOLVE_IN_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); + if (fd < 0) + return fd; + + return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret); +} + +int id128_get_machine(const char *root, sd_id128_t *ret) { + _cleanup_close_ int fd = -EBADF; + + if (empty_or_root(root)) + return sd_id128_get_machine(ret); + + fd = chase_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); + if (fd < 0) + return fd; + + return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret); +} + _public_ int sd_id128_get_boot(sd_id128_t *ret) { static thread_local sd_id128_t saved_boot_id = {}; int r; if (sd_id128_is_null(saved_boot_id)) { - r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID, &saved_boot_id); + r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id); if (r == -ENOENT && proc_mounted() == 0) return -ENOSYS; if (r < 0) return r; - - if (sd_id128_is_null(saved_boot_id)) - return -ENOMEDIUM; } if (ret) diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c index 5620bf9baf..223164ea1b 100644 --- a/src/machine-id-setup/machine-id-setup-main.c +++ b/src/machine-id-setup/machine-id-setup-main.c @@ -158,14 +158,11 @@ static int run(int argc, char *argv[]) { } if (arg_commit) { - const char *etc_machine_id; - r = machine_id_commit(arg_root); if (r < 0) return r; - etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id); + r = id128_get_machine(arg_root, &id); if (r < 0) return log_error_errno(r, "Failed to read machine ID back: %m"); } else { diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 9fbacb2713..8abb016791 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2828,8 +2828,6 @@ static int mount_tunnel_open(void) { } static int setup_machine_id(const char *directory) { - const char *etc_machine_id; - sd_id128_t id; int r; /* If the UUID in the container is already set, then that's what counts, and we use. If it isn't set, and the @@ -2839,9 +2837,7 @@ static int setup_machine_id(const char *directory) { * in the container and our idea of the container UUID will always be in sync (at least if PID 1 in the * container behaves nicely). */ - etc_machine_id = prefix_roota(directory, "/etc/machine-id"); - - r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id); + r = id128_get_machine(directory, &arg_uuid); if (r < 0) { if (!ERRNO_IS_MACHINE_ID_UNSET(r)) /* If the file is missing, empty, or uninitialized, we don't mind */ return log_error_errno(r, "Failed to read machine ID from container image: %m"); @@ -2851,12 +2847,6 @@ static int setup_machine_id(const char *directory) { if (r < 0) return log_error_errno(r, "Failed to acquire randomized machine UUID: %m"); } - } else { - if (sd_id128_is_null(id)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Machine ID in container image is zero, refusing."); - - arg_uuid = id; } return 0; diff --git a/src/partition/repart.c b/src/partition/repart.c index 3d21caa306..e4ae8a2c89 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -4909,24 +4909,14 @@ static int context_read_seed(Context *context, const char *root) { return 0; if (!arg_randomize) { - _cleanup_close_ int fd = -EBADF; - - fd = chase_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, NULL); - if (fd == -ENOENT) - log_info("No machine ID set, using randomized partition UUIDs."); - else if (fd < 0) - return log_error_errno(fd, "Failed to determine machine ID of image: %m"); - else { - r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &context->seed); - if (r < 0) { - if (!ERRNO_IS_MACHINE_ID_UNSET(r)) - return log_error_errno(r, "Failed to parse machine ID of image: %m"); - - log_info("No machine ID set, using randomized partition UUIDs."); - } - + r = id128_get_machine(root, &context->seed); + if (r >= 0) return 0; - } + + if (!ERRNO_IS_MACHINE_ID_UNSET(r)) + return log_error_errno(r, "Failed to parse machine ID of image: %m"); + + log_info("No machine ID set, using randomized partition UUIDs."); } r = sd_id128_randomize(&context->seed); diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 6aff9fbb30..eed0a5629e 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1173,23 +1173,9 @@ int image_read_metadata(Image *i) { path = mfree(path); - r = chase("/etc/machine-id", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL); - if (r < 0 && r != -ENOENT) - log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name); - else if (r >= 0) { - _cleanup_close_ int fd = -EBADF; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - log_debug_errno(errno, "Failed to open %s: %m", path); - else { - r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id); - if (r < 0) - log_debug_errno(r, "Image %s contains invalid machine ID.", i->name); - } - } - - path = mfree(path); + r = id128_get_machine(i->path, &machine_id); + if (r < 0) + log_debug_errno(r, "Failed to read machine ID in image %s, ignoring: %m", i->name); r = chase("/etc/machine-info", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL); if (r < 0 && r != -ENOENT) diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 60f80387f8..0d45249d63 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -444,10 +444,7 @@ int find_esp_and_warn_at( assert(rfd >= 0 || rfd == AT_FDCWD); - if (rfd >= 0) - r = dir_fd_is_root(rfd); - else - r = true; + r = dir_fd_is_root_or_cwd(rfd); if (r < 0) return log_error_errno(r, "Failed to check if directory file descriptor is root: %m"); if (r == 0) diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index 4b4309037b..f27c3d768b 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -8,6 +8,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "chase.h" #include "fd-util.h" #include "id128-util.h" #include "io-util.h" @@ -27,22 +28,16 @@ #include "virt.h" static int generate_machine_id(const char *root, sd_id128_t *ret) { - const char *dbus_machine_id; _cleanup_close_ int fd = -EBADF; int r; assert(ret); /* First, try reading the D-Bus machine id, unless it is a symlink */ - dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id"); - fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) { - if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0) { - log_info("Initializing machine ID from D-Bus machine ID."); - return 0; - } - - fd = safe_close(fd); + fd = chase_and_open("/var/lib/dbus/machine-id", root, CHASE_PREFIX_ROOT | CHASE_NOFOLLOW, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); + if (fd >= 0 && id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret) >= 0) { + log_info("Initializing machine ID from D-Bus machine ID."); + return 0; } if (isempty(root) && running_in_chroot() <= 0) { diff --git a/src/shared/specifier.c b/src/shared/specifier.c index 65e555f716..a4a4017f06 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -187,19 +187,9 @@ int specifier_machine_id(char specifier, const void *data, const char *root, con assert(ret); - if (root) { - _cleanup_close_ int fd = -EBADF; - - fd = chase_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); - if (fd < 0) - /* Translate error for missing os-release file to EUNATCH. */ - return fd == -ENOENT ? -EUNATCH : fd; - - r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id); - } else - r = sd_id128_get_machine(&id); - if (r < 0) - return r; + r = id128_get_machine(root, &id); + if (r < 0) /* Translate error for missing /etc/machine-id file to EUNATCH. */ + return r == -ENOENT ? -EUNATCH : r; return specifier_id128(specifier, &id, root, userdata, ret); } diff --git a/src/test/test-id128.c b/src/test/test-id128.c index 75c3582734..80f762bc89 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -287,6 +287,36 @@ TEST(id128_at) { i = SD_ID128_NULL; assert_se(id128_read(p, ID128_FORMAT_PLAIN, &i) >= 0); assert_se(sd_id128_equal(id, i)); + + /* id128_get_machine_at() */ + i = SD_ID128_NULL; + assert_se(id128_get_machine_at(tfd, &i) >= 0); + assert_se(sd_id128_equal(id, i)); + + /* id128_get_machine() */ + i = SD_ID128_NULL; + assert_se(id128_get_machine(t, &i) >= 0); + assert_se(sd_id128_equal(id, i)); +} + +TEST(ID128_REFUSE_NULL) { + _cleanup_(rm_rf_physical_and_freep) char *t = NULL; + _cleanup_close_ int tfd = -EBADF; + sd_id128_t id; + + tfd = mkdtemp_open(NULL, O_PATH, &t); + assert_se(tfd >= 0); + + assert_se(id128_write_at(tfd, "zero-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, (sd_id128_t) {}) == -ENOMEDIUM); + assert_se(unlinkat(tfd, "zero-id", 0) >= 0); + assert_se(id128_write_at(tfd, "zero-id", ID128_FORMAT_PLAIN, (sd_id128_t) {}) >= 0); + + assert_se(sd_id128_randomize(&id) == 0); + assert_se(!sd_id128_equal(id, SD_ID128_NULL)); + assert_se(id128_read_at(tfd, "zero-id", ID128_FORMAT_PLAIN, &id) >= 0); + assert_se(sd_id128_equal(id, SD_ID128_NULL)); + + assert_se(id128_read_at(tfd, "zero-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, &id) == -ENOMEDIUM); } DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index 62986e8dc2..6d4093ec05 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -136,7 +136,7 @@ TEST(specifiers) { xsprintf(spec, "%%%c", s->specifier); r = specifier_printf(spec, SIZE_MAX, specifier_table, NULL, NULL, &resolved); - if (s->specifier == 'm' && IN_SET(r, -ENOENT, -ENOMEDIUM)) /* machine-id might be missing in build chroots */ + if (s->specifier == 'm' && IN_SET(r, -EUNATCH, -ENOMEDIUM)) /* machine-id might be missing in build chroots */ continue; assert_se(r >= 0);