From: Lennart Poettering Date: Wed, 7 May 2025 13:34:51 +0000 (+0200) Subject: bless-boot: never try to rename an entry file onto itself X-Git-Tag: v258-rc1~647^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F37375%2Fhead;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/bless-boot/bless-boot.c b/src/bless-boot/bless-boot.c index e0143d97347..3fbd09b7e1e 100644 --- a/src/bless-boot/bless-boot.c +++ b/src/bless-boot/bless-boot.c @@ -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));