diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 2edd60ee..61c9365e 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -1358,9 +1358,7 @@ private async Task DownloadPackage(Package package) _totalDownloadedBytes += bytesRead; UpdateProgressBar(); } - - fileStream.Seek(0, SeekOrigin.Begin); - + if (MD5Hash.FromStream(fileStream) != package.Signature) throw new Exception("Signature does not match!"); @@ -1427,17 +1425,36 @@ private async Task ExtractPackage(Package package) if (directory is not null) Directory.CreateDirectory(directory); + var fileManifest = _versionFileManifest.FirstOrDefault(x => x.Name == Path.Combine(PackageDirectories[package.Name], entry.FullName)); + string? signature = fileManifest?.Signature; + if (File.Exists(extractPath)) { - var fileManifest = _versionFileManifest.FirstOrDefault(x => x.Name == Path.Combine(PackageDirectories[package.Name], entry.FullName)); - - if (fileManifest is not null && MD5Hash.FromFile(extractPath) == fileManifest.Signature) + if (signature is not null && MD5Hash.FromFile(extractPath) == signature) continue; File.Delete(extractPath); } - entry.ExtractToFile(extractPath, true); + bool retry = false; + + do + { + using var entryStream = entry.Open(); + using var fileStream = new FileStream(extractPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 0x1000); + await entryStream.CopyToAsync(fileStream); + + if (signature is not null && MD5Hash.FromStream(fileStream) != signature) + { + if (retry) + throw new AssertionException($"Checksum of {entry.FullName} post-extraction did not match manifest"); + + retry = true; + } + } + while (retry); + + File.SetLastWriteTime(extractPath, entry.LastWriteTime.DateTime); } App.Logger.WriteLine(LOG_IDENT, $"Finished extracting {package.Name}"); diff --git a/Bloxstrap/Exceptions/AssertionException.cs b/Bloxstrap/Exceptions/AssertionException.cs new file mode 100644 index 00000000..5555baa3 --- /dev/null +++ b/Bloxstrap/Exceptions/AssertionException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Bloxstrap.Exceptions +{ + internal class AssertionException : Exception + { + public AssertionException(string message) + : base($"{message}\n\nThis is very likely just an off-chance error. Please report this first, and then start {App.ProjectName} again.") + { + } + } +} diff --git a/Bloxstrap/Utility/MD5Hash.cs b/Bloxstrap/Utility/MD5Hash.cs index 9b012cd0..cec547df 100644 --- a/Bloxstrap/Utility/MD5Hash.cs +++ b/Bloxstrap/Utility/MD5Hash.cs @@ -12,6 +12,8 @@ public static string FromBytes(byte[] data) public static string FromStream(Stream stream) { + stream.Seek(0, SeekOrigin.Begin); + using MD5 md5 = MD5.Create(); return Stringify(md5.ComputeHash(stream)); }