bless-boot: never try to rename an entry file onto itself

If we are booting a known bad entry, and we are asked to mark it as bad,
we so far would end up renaming the entry onto itself, which resulted in
EEXIST and is really borked operation. Let's catch that case and handle
it explicitly.
This commit is contained in:
Lennart Poettering
2025-05-12 13:04:16 +02:00
parent 9420a0e6cb
commit 30284e3a59
+33 -4
View File
@@ -406,6 +406,30 @@ static int verb_status(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Couldn't determine boot state.");
}
static int rename_in_dir_idempotent(int fd, const char *from, const char *to) {
int r;
assert(fd >= 0);
assert(from);
assert(to);
/* A wrapper around rename_noreplace() which executes no operation if the source and target are the
* same. */
if (streq(from, to)) {
if (faccessat(fd, from, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
return 0;
}
r = rename_noreplace(fd, from, fd, to);
if (r < 0)
return r;
return 1;
}
static int verb_set(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
const char *target, *source1, *source2;
@@ -457,12 +481,12 @@ static int verb_set(int argc, char *argv[], void *userdata) {
if (fd < 0)
return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
r = rename_noreplace(fd, skip_leading_slash(source1), fd, skip_leading_slash(target));
r = rename_in_dir_idempotent(fd, skip_leading_slash(source1), skip_leading_slash(target));
if (r == -EEXIST)
goto exists;
if (r == -ENOENT) {
r = rename_noreplace(fd, skip_leading_slash(source2), fd, skip_leading_slash(target));
r = rename_in_dir_idempotent(fd, skip_leading_slash(source2), skip_leading_slash(target));
if (r == -EEXIST)
goto exists;
if (r == -ENOENT) {
@@ -479,11 +503,16 @@ static int verb_set(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
log_debug("Successfully renamed '%s' to '%s'.", source2, target);
if (r > 0)
log_debug("Successfully renamed '%s' to '%s'.", source2, target);
else
log_debug("Not renaming, as '%s' already matches target name.", source2);
} else if (r < 0)
return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
else
else if (r > 0)
log_debug("Successfully renamed '%s' to '%s'.", source1, target);
else
log_debug("Not renaming, as '%s' already matches target name.", source1);
/* First, fsync() the directory these files are located in */
r = fsync_parent_at(fd, skip_leading_slash(target));