2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 15:18:19 +00:00

fix(packimage): keep a checksum symlink through a failed publication

A failed publication put back only the content of the previous checksum
file. A checksum symlink became a regular file, which a later switch of
the symlinks would leave stale.

publish_rootimg now keeps the previous checksum entry, and a rollback
renames it back. When that rename fails, the previous content goes
back, and the error reports it.
This commit is contained in:
Vinícius Ferrão
2026-10-13 15:29:27 -03:00
parent c5c28f1448
commit c0f3d212f5
+38 -13
View File
@@ -2859,7 +2859,7 @@ sub publish_rootimg {
}
# Afterwards, only a staging directory that keeps previous files for a failed publication holds them.
unlink(grep { -e $_ || -l $_ } ($state{previous}, "$state{previous}.metainfo")) if !$state{retained};
unlink(grep { -e $_ || -l $_ } map { "$state{previous}$_" } ('', '.metainfo', '.sha256')) if !$state{retained};
_release_publish_lock($lock);
return if !defined($error);
return "Cannot publish $target: $error";
@@ -2883,6 +2883,26 @@ sub _replace_rootimg {
# A failed publication puts the previous archive back from a link, or from a copy without hard links.
return "$!" if -e $target && !_keep_copy($target, $previous);
return "$!" if !-s $staged || chmod(0644, @files) != @files;
my $error = _replace_sums($state);
return $error if $error;
# The previous metainfo leaves first, so it never describes the new archive.
if (-e "$target.metainfo") {
$state->{metainfo_moved} = rename("$target.metainfo", "$previous.metainfo") or return "$!";
}
$state->{replaced} = rename($staged, $target) or return "$!";
return "$!" if -e "$staged.metainfo" && !rename("$staged.metainfo", "$target.metainfo");
return;
}
# Writes the checksum file of the publication in $state for the time that the archive is replaced, and returns
# why it cannot.
sub _replace_sums {
my ($state) = @_;
my ($staged, $target, $previous) = @$state{qw(staged target previous)};
# A rollback renames the previous checksum entry back, so a symlink stays a symlink.
$state->{sums_entry} = (-e "$target.sha256" || -l "$target.sha256") && link("$target.sha256", "$previous.sha256");
# Until the archive is replaced, the checksum file lists the new and the previous archive, so the published
# archive matches a line of it, also when the publisher is killed.
@@ -2892,19 +2912,12 @@ sub _replace_rootimg {
($previous_line, $error) = _rootimg_checksum_line($target) if !defined($previous_line) && -e $target;
return $error if $error;
$state->{sums_changed} = 1;
$error = _write_published_file("$target.sha256", _read_published_file("$staged.sha256") . ($previous_line // ''));
return $error if $error;
} elsif (defined($state->{sums})) {
return _write_published_file("$target.sha256", _read_published_file("$staged.sha256") . ($previous_line // ''));
}
if (defined($state->{sums})) {
$state->{sums_changed} = 1;
unlink("$target.sha256") or return "$!";
}
# The previous metainfo leaves first, so it never describes the new archive.
if (-e "$target.metainfo") {
$state->{metainfo_moved} = rename("$target.metainfo", "$previous.metainfo") or return "$!";
}
$state->{replaced} = rename($staged, $target) or return "$!";
return "$!" if -e "$staged.metainfo" && !rename("$staged.metainfo", "$target.metainfo");
return;
}
@@ -2960,13 +2973,25 @@ sub _restore_rootimg {
push @errors, "the previous metainfo cannot be put back: $error" . (@kept ? '. It is kept in ' . join(' and ', @kept) : ', nor kept');
}
if ($state->{sums_changed}) {
my $error = defined($state->{sums}) ? _write_published_file("$target.sha256", $state->{sums})
: (-e "$target.sha256" && !unlink("$target.sha256")) ? "Cannot remove $target.sha256: $!" : undef;
my $error = _restore_sums($state);
push @errors, "the previous checksum cannot be put back: $error" if $error;
}
return @errors ? join(', and ', @errors) : undef;
}
# Puts back the previous checksum file of the publication in $state, or returns why it cannot. The kept entry goes
# back as it was, also a symlink. Otherwise the previous content goes back, so that nodes can check the archive.
sub _restore_sums {
my ($state) = @_;
my ($target, $previous) = @$state{qw(target previous)};
return if $state->{sums_entry} && rename("$previous.sha256", "$target.sha256");
my $entry_error = $state->{sums_entry} ? "$!" : undef;
my $error = defined($state->{sums}) ? _write_published_file("$target.sha256", $state->{sums})
: (-e "$target.sha256" && !unlink("$target.sha256")) ? "Cannot remove $target.sha256: $!" : undef;
return $error if $error;
return defined($entry_error) ? "$entry_error. Its previous content went back instead" : undef;
}
#--------------------------------------------------------------------------------------------------------
#publish_missing_rootimg_checksums:
#description: write the checksum of each published compressed rootimage of an image directory that has