mirror of
https://github.com/systemd/systemd.git
synced 2026-08-09 09:32:04 +00:00
machine-id-setup: follow symlink target on --commit
Resolve /etc/machine-id before checking the mount state and before
writing back, so symlinked targets are committed correctly.
Fixes: #43104
Follow-up for: efabf4e044
This commit is contained in:
committed by
Yu Watanabe
parent
9d217e051b
commit
aa776aefba
@@ -318,17 +318,37 @@ int machine_id_commit(const char *root) {
|
||||
* in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
|
||||
* original mount namespace, thus revealing the file that was just created. */
|
||||
|
||||
_cleanup_close_ int etc_fd = -EBADF;
|
||||
_cleanup_free_ char *etc = NULL;
|
||||
r = chase("/etc/", root, CHASE_PREFIX_ROOT|CHASE_MUST_BE_DIRECTORY, &etc, &etc_fd);
|
||||
_cleanup_close_ int etc_machine_id_fd = -EBADF;
|
||||
_cleanup_free_ char *etc_machine_id = NULL;
|
||||
r = chase("/etc/machine-id", root, CHASE_PREFIX_ROOT|CHASE_MUST_BE_REGULAR,
|
||||
&etc_machine_id, &etc_machine_id_fd);
|
||||
if (r == -ENOENT) {
|
||||
_cleanup_close_ int etc_fd = -EBADF;
|
||||
_cleanup_free_ char *etc = NULL, *target = NULL;
|
||||
|
||||
r = chase("/etc/", root, CHASE_PREFIX_ROOT|CHASE_MUST_BE_DIRECTORY, &etc, &etc_fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open %s: %m", "/etc/");
|
||||
|
||||
etc_machine_id = path_join(etc, "machine-id");
|
||||
if (!etc_machine_id)
|
||||
return log_oom();
|
||||
|
||||
r = readlinkat_malloc(etc_fd, "machine-id", &target);
|
||||
if (r == -ENOENT) {
|
||||
log_debug_errno(r, "%s does not exist. Nothing to do.", etc_machine_id);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read symlink target of %s: %m", etc_machine_id);
|
||||
|
||||
log_debug("%s is a dangling symlink to %s. Nothing to do.", etc_machine_id, target);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open %s: %m", "/etc/");
|
||||
return log_error_errno(r, "Failed to open %s: %m", "/etc/machine-id");
|
||||
|
||||
_cleanup_free_ char *etc_machine_id = path_join(etc, "machine-id");
|
||||
if (!etc_machine_id)
|
||||
return log_oom();
|
||||
|
||||
r = is_mount_point_at(etc_fd, "machine-id", /* flags= */ 0);
|
||||
r = is_mount_point_at(etc_machine_id_fd, /* path= */ NULL, /* flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
|
||||
if (r == 0) {
|
||||
@@ -338,11 +358,12 @@ int machine_id_commit(const char *root) {
|
||||
|
||||
/* Read existing machine-id */
|
||||
|
||||
_cleanup_close_ int fd = xopenat_full(etc_fd, "machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, XO_REGULAR, MODE_INVALID);
|
||||
_cleanup_close_ int fd = xopenat_full(etc_machine_id_fd, /* path= */ NULL,
|
||||
O_RDONLY|O_CLOEXEC|O_NOCTTY, XO_REGULAR, MODE_INVALID);
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Cannot open %s: %m", etc_machine_id);
|
||||
|
||||
etc_fd = safe_close(etc_fd);
|
||||
etc_machine_id_fd = safe_close(etc_machine_id_fd);
|
||||
|
||||
r = fd_is_temporary_fs(fd);
|
||||
if (r < 0)
|
||||
@@ -366,22 +387,34 @@ int machine_id_commit(const char *root) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up new mount namespace: %m");
|
||||
|
||||
/* Open /etc/ again after we transitioned into our own private mount namespace */
|
||||
_cleanup_close_ int etc_fd_again = -EBADF;
|
||||
r = chase("/etc/", root, CHASE_PREFIX_ROOT|CHASE_MUST_BE_DIRECTORY, /* ret_path= */ NULL, &etc_fd_again);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open %s: %m", "/etc/");
|
||||
_cleanup_free_ char *etc_machine_id_filename = NULL;
|
||||
_cleanup_close_ int etc_machine_id_dir_fd =
|
||||
chase_and_open_parent("/etc/machine-id", root, CHASE_PREFIX_ROOT, &etc_machine_id_filename);
|
||||
if (etc_machine_id_dir_fd < 0)
|
||||
return log_error_errno(etc_machine_id_dir_fd,
|
||||
"Failed to open parent directory of %s: %m",
|
||||
etc_machine_id);
|
||||
|
||||
r = umountat_detach_verbose(LOG_ERR, etc_fd_again, "machine-id");
|
||||
r = inode_same_at(fd, /* filea= */ NULL,
|
||||
etc_machine_id_dir_fd, etc_machine_id_filename, AT_EMPTY_PATH);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to verify %s before unmounting: %m", etc_machine_id);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESTALE),
|
||||
"%s changed while preparing to commit.",
|
||||
etc_machine_id);
|
||||
|
||||
r = umountat_detach_verbose(LOG_ERR, etc_machine_id_dir_fd, etc_machine_id_filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Update a persistent version of etc_machine_id */
|
||||
r = id128_write_at(etc_fd_again, "machine-id", ID128_FORMAT_PLAIN | ID128_SYNC_ON_WRITE, id);
|
||||
r = id128_write_at(etc_machine_id_dir_fd, etc_machine_id_filename,
|
||||
ID128_FORMAT_PLAIN | ID128_SYNC_ON_WRITE, id);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
|
||||
|
||||
etc_fd_again = safe_close(etc_fd_again);
|
||||
etc_machine_id_dir_fd = safe_close(etc_machine_id_dir_fd);
|
||||
|
||||
/* Return to initial namespace and proceed a lazy tmpfs unmount */
|
||||
r = namespace_enter(/* pidns_fd= */ -EBADF,
|
||||
|
||||
@@ -69,6 +69,27 @@ testcase_transient() {
|
||||
diff "$root/etc/machine-id" "$root/run/machine-id"
|
||||
}
|
||||
|
||||
testcase_transient_symlink() {
|
||||
local root transient_id committed_id
|
||||
|
||||
root="$(mktemp -d)"
|
||||
trap "root_cleanup $root" RETURN
|
||||
root_mock "$root"
|
||||
|
||||
mkdir -p "$root/persist/etc"
|
||||
echo abc >>"$root/persist/etc/machine-id"
|
||||
ln -s /persist/etc/machine-id "$root/etc/machine-id"
|
||||
mount -o remount,ro "$root"
|
||||
mount -t tmpfs tmpfs "$root/run"
|
||||
transient_id="$(systemd-machine-id-setup --print --root "$root")"
|
||||
mountpoint "$root/persist/etc/machine-id"
|
||||
mount -o remount,rw "$root"
|
||||
committed_id="$(systemd-machine-id-setup --print --commit --root "$root")"
|
||||
[[ "$transient_id" == "$committed_id" ]]
|
||||
(! mountpoint "$root/persist/etc/machine-id")
|
||||
diff "$root/persist/etc/machine-id" "$root/run/machine-id"
|
||||
}
|
||||
|
||||
# Check if we correctly processed the invalid machine ID we set up in the respective
|
||||
# test.sh file
|
||||
systemctl --state=failed --no-legend --no-pager | tee /failed
|
||||
|
||||
Reference in New Issue
Block a user