mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Verify hashes while installing
This commit is contained in:
@ -51,6 +51,8 @@ CommandLineBuilder.RegisterCommand<UploadToNexus>(UploadToNexus.Definition, c =>
|
|||||||
services.AddSingleton<UploadToNexus>();
|
services.AddSingleton<UploadToNexus>();
|
||||||
CommandLineBuilder.RegisterCommand<ValidateLists>(ValidateLists.Definition, c => ((ValidateLists)c).Run);
|
CommandLineBuilder.RegisterCommand<ValidateLists>(ValidateLists.Definition, c => ((ValidateLists)c).Run);
|
||||||
services.AddSingleton<ValidateLists>();
|
services.AddSingleton<ValidateLists>();
|
||||||
|
CommandLineBuilder.RegisterCommand<VerifyModlistInstall>(VerifyModlistInstall.Definition, c => ((VerifyModlistInstall)c).Run);
|
||||||
|
services.AddSingleton<VerifyModlistInstall>();
|
||||||
CommandLineBuilder.RegisterCommand<VFSIndex>(VFSIndex.Definition, c => ((VFSIndex)c).Run);
|
CommandLineBuilder.RegisterCommand<VFSIndex>(VFSIndex.Definition, c => ((VFSIndex)c).Run);
|
||||||
services.AddSingleton<VFSIndex>();
|
services.AddSingleton<VFSIndex>();
|
||||||
}
|
}
|
||||||
|
74
Wabbajack.CLI/Verbs/VerifyModlistInstall.cs
Normal file
74
Wabbajack.CLI/Verbs/VerifyModlistInstall.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Wabbajack.CLI.Builder;
|
||||||
|
using Wabbajack.DTOs.Directives;
|
||||||
|
using Wabbajack.DTOs.JsonConverters;
|
||||||
|
using Wabbajack.Installer;
|
||||||
|
using Wabbajack.Paths;
|
||||||
|
using Wabbajack.Paths.IO;
|
||||||
|
|
||||||
|
namespace Wabbajack.CLI.Verbs;
|
||||||
|
|
||||||
|
public class VerifyModlistInstall
|
||||||
|
{
|
||||||
|
private readonly DTOSerializer _dtos;
|
||||||
|
private readonly ILogger<VerifyModlistInstall> _logger;
|
||||||
|
|
||||||
|
public VerifyModlistInstall(ILogger<VerifyModlistInstall> logger, DTOSerializer dtos)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_dtos = dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VerbDefinition Definition = new("verify-modlist-install", "Verify a modlist installed correctly",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new OptionDefinition(typeof(AbsolutePath), "m", "modlistLocation",
|
||||||
|
"The .wabbajack file used to install the modlist"),
|
||||||
|
new OptionDefinition(typeof(AbsolutePath), "i", "installFolder", "The installation folder of the modlist")
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<int> Run(AbsolutePath modlistLocation, AbsolutePath installFolder)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Loading modlist {ModList}", modlistLocation);
|
||||||
|
var list = await StandardInstaller.LoadFromFile(_dtos, modlistLocation);
|
||||||
|
|
||||||
|
var errors = new List<Result>();
|
||||||
|
|
||||||
|
_logger.LogInformation("Scanning files");
|
||||||
|
foreach (var directive in list.Directives)
|
||||||
|
{
|
||||||
|
var dest = directive.To.RelativeTo(installFolder);
|
||||||
|
if (dest.Size() != directive.Size)
|
||||||
|
{
|
||||||
|
errors.Add(new Result()
|
||||||
|
{
|
||||||
|
Path = directive.To,
|
||||||
|
Message = $"Sizes do not match got {dest.Size()} expected {directive.Size}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Found {Count} errors", errors.Count);
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var error in errors)
|
||||||
|
{
|
||||||
|
_logger.LogError("{File} | {Message}", error.Path, error.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Result
|
||||||
|
{
|
||||||
|
public RelativePath Path { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -21,4 +21,19 @@ public static class AbsolutePathExtensions
|
|||||||
await using var fs = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
await using var fs = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
return await fs.FromJson<T>(dtos);
|
return await fs.FromJson<T>(dtos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<Hash> WriteAllHashedAsync(this AbsolutePath file, Stream srcStream, CancellationToken token,
|
||||||
|
bool closeWhenDone = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var dest = file.Open(FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
return await srcStream.HashingCopy(dest, token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (closeWhenDone)
|
||||||
|
srcStream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -243,7 +243,8 @@ public abstract class AInstaller<T>
|
|||||||
await using var patchDataStream = await InlinedFileStream(pfa.PatchID);
|
await using var patchDataStream = await InlinedFileStream(pfa.PatchID);
|
||||||
{
|
{
|
||||||
await using var os = destPath.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
await using var os = destPath.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
||||||
await BinaryPatching.ApplyPatch(s, patchDataStream, os);
|
var hash = await BinaryPatching.ApplyPatch(s, patchDataStream, os);
|
||||||
|
ThrowOnNonMatchingHash(file, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -268,7 +269,8 @@ public abstract class AInstaller<T>
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
await using var s = await sf.GetStream();
|
await using var s = await sf.GetStream();
|
||||||
await destPath.WriteAllAsync(s, token, false);
|
var hash = await destPath.WriteAllHashedAsync(s, token, false);
|
||||||
|
ThrowOnNonMatchingHash(file, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -282,6 +284,17 @@ public abstract class AInstaller<T>
|
|||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void ThrowOnNonMatchingHash(Directive file, Hash gotHash)
|
||||||
|
{
|
||||||
|
if (file.Hash != gotHash)
|
||||||
|
ThrowNonMatchingError(file, gotHash);
|
||||||
|
}
|
||||||
|
private void ThrowNonMatchingError(Directive file, Hash gotHash)
|
||||||
|
{
|
||||||
|
_logger.LogError("Hashes for {Path} did not match, expected {Expected} got {Got}", file.To, file.Hash, gotHash);
|
||||||
|
throw new Exception($"Hashes for {file.To} did not match, expected {file.Hash} got {gotHash}");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DownloadArchives(CancellationToken token)
|
public async Task DownloadArchives(CancellationToken token)
|
||||||
{
|
{
|
||||||
var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList();
|
var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList();
|
||||||
|
@ -481,7 +481,8 @@ public class StandardInstaller : AInstaller<StandardInstaller>
|
|||||||
.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await BinaryPatching.ApplyPatch(new MemoryStream(srcData), new MemoryStream(patchData), fs);
|
var hash = await BinaryPatching.ApplyPatch(new MemoryStream(srcData), new MemoryStream(patchData), fs);
|
||||||
|
ThrowOnNonMatchingHash(m, hash);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Octodiff.Core;
|
using Octodiff.Core;
|
||||||
using Octodiff.Diagnostics;
|
using Octodiff.Diagnostics;
|
||||||
|
using Wabbajack.Hashing.xxHash64;
|
||||||
|
|
||||||
namespace Wabbajack.Installer.Utilities;
|
namespace Wabbajack.Installer.Utilities;
|
||||||
|
|
||||||
public class BinaryPatching
|
public class BinaryPatching
|
||||||
{
|
{
|
||||||
public static ValueTask ApplyPatch(Stream input, Stream deltaStream, Stream output)
|
public static async ValueTask<Hash> ApplyPatch(Stream input, Stream deltaStream, Stream output, CancellationToken? token = null)
|
||||||
{
|
{
|
||||||
var deltaApplier = new DeltaApplier();
|
var deltaApplier = new DeltaApplier();
|
||||||
deltaApplier.Apply(input, new BinaryDeltaReader(deltaStream, new NullProgressReporter()), output);
|
deltaApplier.Apply(input, new BinaryDeltaReader(deltaStream, new NullProgressReporter()), output);
|
||||||
return ValueTask.CompletedTask;
|
output.Position = 0;
|
||||||
|
return await output.Hash(token ?? CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
|
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
|
||||||
<PackageReference Include="Octodiff" Version="1.3.23" />
|
<PackageReference Include="Octopus.Octodiff" Version="1.3.93" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Reference in New Issue
Block a user