mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add HierarchicalIntegrityVerificationStorage
This commit is contained in:
parent
b54f5d17fa
commit
6c9e6e5203
7 changed files with 922 additions and 6 deletions
17
src/LibHac/Crypto/HmacSha256.cs
Normal file
17
src/LibHac/Crypto/HmacSha256.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public static class HmacSha256
|
||||||
|
{
|
||||||
|
public const int HashSize = Sha256.DigestSize;
|
||||||
|
|
||||||
|
public static void GenerateHmacSha256(Span<byte> outMac, ReadOnlySpan<byte> data, ReadOnlySpan<byte> key)
|
||||||
|
{
|
||||||
|
bool success = HMACSHA256.TryHashData(key, data, outMac, out int bytesWritten);
|
||||||
|
|
||||||
|
Abort.DoAbortUnless(success && bytesWritten == HashSize);
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ internal struct FileSystemServerGlobals : IDisposable
|
||||||
public LocationResolverSetGlobals LocationResolverSet;
|
public LocationResolverSetGlobals LocationResolverSet;
|
||||||
public PooledBufferGlobals PooledBuffer;
|
public PooledBufferGlobals PooledBuffer;
|
||||||
public GameCardServiceGlobals GameCardService;
|
public GameCardServiceGlobals GameCardService;
|
||||||
|
public HierarchicalIntegrityVerificationStorageGlobals HierarchicalIntegrityVerificationStorage;
|
||||||
|
|
||||||
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
||||||
{
|
{
|
||||||
|
@ -55,6 +56,7 @@ internal struct FileSystemServerGlobals : IDisposable
|
||||||
LocationResolverSet.Initialize();
|
LocationResolverSet.Initialize();
|
||||||
PooledBuffer.Initialize();
|
PooledBuffer.Initialize();
|
||||||
GameCardService.Initialize();
|
GameCardService.Initialize();
|
||||||
|
HierarchicalIntegrityVerificationStorage.Initialize(fsServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -5,11 +5,11 @@ namespace LibHac.FsSystem;
|
||||||
public static class BitmapUtils
|
public static class BitmapUtils
|
||||||
{
|
{
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static uint ILog2(uint value)
|
public static int ILog2(uint value)
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresGreater(value, 0u);
|
Assert.SdkRequiresGreater(value, 0u);
|
||||||
|
|
||||||
const uint intBitCount = 32;
|
const int intBitCount = 32;
|
||||||
return intBitCount - 1 - (uint)Util.BitUtil.CountLeadingZeros(value);
|
return intBitCount - 1 - Util.BitUtil.CountLeadingZeros(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -59,8 +59,8 @@ public class BlockCacheBufferedStorage : IStorage
|
||||||
private int _shiftBytesVerificationBlock;
|
private int _shiftBytesVerificationBlock;
|
||||||
private int _flags;
|
private int _flags;
|
||||||
private int _bufferLevel;
|
private int _bufferLevel;
|
||||||
private StorageType _storageType;
|
|
||||||
private BlockCacheManager<CacheEntry, AccessRange> _cacheManager;
|
private BlockCacheManager<CacheEntry, AccessRange> _cacheManager;
|
||||||
|
private bool _isWritable;
|
||||||
|
|
||||||
public BlockCacheBufferedStorage()
|
public BlockCacheBufferedStorage()
|
||||||
{
|
{
|
||||||
|
@ -93,7 +93,7 @@ public class BlockCacheBufferedStorage : IStorage
|
||||||
|
|
||||||
public Result Initialize(IBufferManager bufferManager, SdkRecursiveMutex mutex, IStorage data, long dataSize,
|
public Result Initialize(IBufferManager bufferManager, SdkRecursiveMutex mutex, IStorage data, long dataSize,
|
||||||
int sizeBytesVerificationBlock, int maxCacheEntries, bool useRealDataCache, sbyte bufferLevel,
|
int sizeBytesVerificationBlock, int maxCacheEntries, bool useRealDataCache, sbyte bufferLevel,
|
||||||
bool useKeepBurstMode, StorageType storageType)
|
bool useKeepBurstMode, bool isWritable)
|
||||||
{
|
{
|
||||||
Assert.SdkNotNull(data);
|
Assert.SdkNotNull(data);
|
||||||
Assert.SdkNotNull(mutex);
|
Assert.SdkNotNull(mutex);
|
||||||
|
@ -111,7 +111,7 @@ public class BlockCacheBufferedStorage : IStorage
|
||||||
_lastResult = Result.Success;
|
_lastResult = Result.Success;
|
||||||
_flags = 0;
|
_flags = 0;
|
||||||
_bufferLevel = bufferLevel;
|
_bufferLevel = bufferLevel;
|
||||||
_storageType = storageType;
|
_isWritable = isWritable;
|
||||||
_shiftBytesVerificationBlock = (int)BitmapUtils.ILog2((uint)sizeBytesVerificationBlock);
|
_shiftBytesVerificationBlock = (int)BitmapUtils.ILog2((uint)sizeBytesVerificationBlock);
|
||||||
|
|
||||||
Assert.SdkEqual(1 << _shiftBytesVerificationBlock, _sizeBytesVerificationBlock);
|
Assert.SdkEqual(1 << _shiftBytesVerificationBlock, _sizeBytesVerificationBlock);
|
||||||
|
|
817
src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs
Normal file
817
src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs
Normal file
|
@ -0,0 +1,817 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
|
using LibHac.Crypto;
|
||||||
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.Os;
|
||||||
|
using LibHac.Util;
|
||||||
|
using static LibHac.FsSystem.Constants;
|
||||||
|
using static LibHac.FsSystem.HierarchicalIntegrityVerificationStorage;
|
||||||
|
using static LibHac.Util.BitUtil;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem;
|
||||||
|
|
||||||
|
internal static class Constants
|
||||||
|
{
|
||||||
|
public const int IntegrityMinLayerCount = 2;
|
||||||
|
public const int IntegrityMaxLayerCount = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct HierarchicalIntegrityVerificationStorageGlobals
|
||||||
|
{
|
||||||
|
public RandomDataGenerator GenerateRandom;
|
||||||
|
public Semaphore GlobalWriteSemaphore;
|
||||||
|
public Semaphore GlobalReadSemaphore;
|
||||||
|
|
||||||
|
public void Initialize(FileSystemServer fsServer)
|
||||||
|
{
|
||||||
|
GlobalWriteSemaphore = new Semaphore(fsServer.Hos.Os, AccessCountMax, AccessCountMax);
|
||||||
|
GlobalReadSemaphore = new Semaphore(fsServer.Hos.Os, AccessCountMax, AccessCountMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GlobalReadSemaphore.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FileSystemBufferManagerSet
|
||||||
|
{
|
||||||
|
public IBufferManager[] Buffers;
|
||||||
|
|
||||||
|
public FileSystemBufferManagerSet()
|
||||||
|
{
|
||||||
|
Buffers = new IBufferManager[IntegrityMaxLayerCount];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct HierarchicalIntegrityVerificationLevelInformation
|
||||||
|
{
|
||||||
|
public Fs.Int64 Offset;
|
||||||
|
public Fs.Int64 Size;
|
||||||
|
public int BlockOrder;
|
||||||
|
public uint Reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct HierarchicalIntegrityVerificationInformation
|
||||||
|
{
|
||||||
|
public uint MaxLayers;
|
||||||
|
public Array6<HierarchicalIntegrityVerificationLevelInformation> Layers;
|
||||||
|
public HashSalt HashSalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct HierarchicalIntegrityVerificationMetaInformation
|
||||||
|
{
|
||||||
|
public uint Magic;
|
||||||
|
public uint Version;
|
||||||
|
public uint MasterHashSize;
|
||||||
|
public HierarchicalIntegrityVerificationInformation LevelHashInfo;
|
||||||
|
|
||||||
|
public void Format()
|
||||||
|
{
|
||||||
|
var hashSalt = new Optional<HashSalt>();
|
||||||
|
Format(in hashSalt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Format(in Optional<HashSalt> hashSalt)
|
||||||
|
{
|
||||||
|
Magic = IntegrityVerificationStorageMagic;
|
||||||
|
Version = IntegrityVerificationStorageVersion;
|
||||||
|
MasterHashSize = 0;
|
||||||
|
LevelHashInfo = default;
|
||||||
|
|
||||||
|
if (hashSalt.HasValue)
|
||||||
|
{
|
||||||
|
LevelHashInfo.HashSalt = hashSalt.ValueRo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct HierarchicalIntegrityVerificationSizeSet
|
||||||
|
{
|
||||||
|
public long ControlSize;
|
||||||
|
public long MasterHashSize;
|
||||||
|
public Array5<long> LayeredHashSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HierarchicalIntegrityVerificationStorageControlArea : IDisposable
|
||||||
|
{
|
||||||
|
public struct InputParam
|
||||||
|
{
|
||||||
|
public Array6<int> LevelBlockSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int HashSize = Sha256.DigestSize;
|
||||||
|
|
||||||
|
private ValueSubStorage _storage;
|
||||||
|
private HierarchicalIntegrityVerificationMetaInformation _meta;
|
||||||
|
|
||||||
|
public HierarchicalIntegrityVerificationStorageControlArea()
|
||||||
|
{
|
||||||
|
_storage = new ValueSubStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_storage.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result QuerySize(out HierarchicalIntegrityVerificationSizeSet outSizeSet, in InputParam inputParam,
|
||||||
|
int layerCount, long dataSize)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out outSizeSet);
|
||||||
|
|
||||||
|
Assert.SdkRequires(layerCount >= IntegrityMinLayerCount && layerCount <= IntegrityMaxLayerCount);
|
||||||
|
|
||||||
|
for (int level = 0; level < layerCount - 1; level++)
|
||||||
|
{
|
||||||
|
Assert.SdkRequires(inputParam.LevelBlockSizes[level] > 0 && IsPowerOfTwo(inputParam.LevelBlockSizes[level]));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
outSizeSet.ControlSize = Unsafe.SizeOf<HierarchicalIntegrityVerificationMetaInformation>();
|
||||||
|
|
||||||
|
Span<long> levelSize = stackalloc long[IntegrityMaxLayerCount];
|
||||||
|
int level = layerCount - 1;
|
||||||
|
|
||||||
|
levelSize[level] = Alignment.AlignUpPow2(dataSize, (uint)inputParam.LevelBlockSizes[level - 1]);
|
||||||
|
level--;
|
||||||
|
|
||||||
|
for (; level > 0; level--)
|
||||||
|
{
|
||||||
|
// Calculate how much space is needed to store the hashes of the above level, rounding up to the next block size.
|
||||||
|
levelSize[level] =
|
||||||
|
Alignment.AlignUpPow2(levelSize[level + 1] / inputParam.LevelBlockSizes[level] * HashSize,
|
||||||
|
(uint)inputParam.LevelBlockSizes[level - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The size of the master hash does not get rounded up to the next block size.
|
||||||
|
levelSize[0] = levelSize[1] / inputParam.LevelBlockSizes[0] * HashSize;
|
||||||
|
outSizeSet.MasterHashSize = levelSize[0];
|
||||||
|
|
||||||
|
// Write the sizes of each level to the output struct.
|
||||||
|
for (level = 1; level < layerCount - 1; level++)
|
||||||
|
{
|
||||||
|
outSizeSet.LayeredHashSizes[level - 1] = levelSize[level];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result Format(in ValueSubStorage metaStorage,
|
||||||
|
in HierarchicalIntegrityVerificationMetaInformation metaInfo)
|
||||||
|
{
|
||||||
|
// Ensure the storage is large enough to hold the meta info.
|
||||||
|
Result rc = metaStorage.GetSize(out long metaSize);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (metaSize < Unsafe.SizeOf<HierarchicalIntegrityVerificationMetaInformation>())
|
||||||
|
return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
|
// Validate the meta magic and version.
|
||||||
|
if (metaInfo.Magic != IntegrityVerificationStorageMagic)
|
||||||
|
return ResultFs.IncorrectIntegrityVerificationMagicCode.Log();
|
||||||
|
|
||||||
|
if ((metaInfo.Version & IntegrityVerificationStorageVersionMask) != IntegrityVerificationStorageVersion)
|
||||||
|
return ResultFs.UnsupportedVersion.Log();
|
||||||
|
|
||||||
|
// Write the meta info to the storage.
|
||||||
|
rc = metaStorage.Write(0, SpanHelpers.AsReadOnlyByteSpan(in metaInfo));
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
rc = metaStorage.Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result Expand(in ValueSubStorage metaStorage,
|
||||||
|
in HierarchicalIntegrityVerificationMetaInformation newMeta)
|
||||||
|
{
|
||||||
|
// Ensure the storage is large enough to hold the meta info.
|
||||||
|
Result rc = metaStorage.GetSize(out long metaSize);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (metaSize < Unsafe.SizeOf<HierarchicalIntegrityVerificationMetaInformation>())
|
||||||
|
return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
|
// Validate both the previous and new metas.
|
||||||
|
HierarchicalIntegrityVerificationMetaInformation previousMeta = default;
|
||||||
|
rc = metaStorage.Read(0, SpanHelpers.AsByteSpan(ref previousMeta));
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (newMeta.Magic != IntegrityVerificationStorageMagic || newMeta.Magic != previousMeta.Magic)
|
||||||
|
return ResultFs.IncorrectIntegrityVerificationMagicCode.Log();
|
||||||
|
|
||||||
|
if (newMeta.Version != IntegrityVerificationStorageVersion || newMeta.Version != previousMeta.Version)
|
||||||
|
return ResultFs.UnsupportedVersion.Log();
|
||||||
|
|
||||||
|
// Write the new meta.
|
||||||
|
rc = metaStorage.Write(0, SpanHelpers.AsReadOnlyByteSpan(in newMeta));
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
rc = metaStorage.Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetMasterHashSize()
|
||||||
|
{
|
||||||
|
return _meta.MasterHashSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetLevelHashInfo(out HierarchicalIntegrityVerificationInformation outInfo)
|
||||||
|
{
|
||||||
|
outInfo = _meta.LevelHashInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Initialize(in ValueSubStorage metaStorage)
|
||||||
|
{
|
||||||
|
// Ensure the storage is large enough to hold the meta info.
|
||||||
|
Result rc = metaStorage.GetSize(out long metaSize);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
if (metaSize < Unsafe.SizeOf<HierarchicalIntegrityVerificationMetaInformation>())
|
||||||
|
return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
|
// Set the storage and read the meta.
|
||||||
|
_storage.Set(in metaStorage);
|
||||||
|
rc = _storage.Read(0, SpanHelpers.AsByteSpan(ref _meta));
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
// Validate the meta magic and version.
|
||||||
|
if (_meta.Magic != IntegrityVerificationStorageMagic)
|
||||||
|
return ResultFs.IncorrectIntegrityVerificationMagicCode.Log();
|
||||||
|
|
||||||
|
if ((_meta.Version & IntegrityVerificationStorageVersionMask) != IntegrityVerificationStorageVersion)
|
||||||
|
return ResultFs.UnsupportedVersion.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinalizeObject()
|
||||||
|
{
|
||||||
|
using var emptySubStorage = new ValueSubStorage();
|
||||||
|
_storage.Set(in emptySubStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HierarchicalIntegrityVerificationStorage : IStorage
|
||||||
|
{
|
||||||
|
[NonCopyableDisposable]
|
||||||
|
public struct HierarchicalStorageInformation : IDisposable
|
||||||
|
{
|
||||||
|
public enum Storage
|
||||||
|
{
|
||||||
|
MasterStorage = 0,
|
||||||
|
Layer1Storage = 1,
|
||||||
|
Layer2Storage = 2,
|
||||||
|
Layer3Storage = 3,
|
||||||
|
Layer4Storage = 4,
|
||||||
|
Layer5Storage = 5,
|
||||||
|
DataStorage = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueSubStorage[] _storages;
|
||||||
|
|
||||||
|
public HierarchicalStorageInformation(in HierarchicalStorageInformation other)
|
||||||
|
{
|
||||||
|
_storages = new ValueSubStorage[(int)Storage.DataStorage + 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < _storages.Length; i++)
|
||||||
|
{
|
||||||
|
_storages[i] = new ValueSubStorage(in other._storages[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HierarchicalStorageInformation()
|
||||||
|
{
|
||||||
|
_storages = new ValueSubStorage[(int)Storage.DataStorage + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _storages.Length; i++)
|
||||||
|
{
|
||||||
|
_storages[i].Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref ValueSubStorage this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Assert.SdkRequiresInRange(index, (int)Storage.MasterStorage, (int)Storage.DataStorage + 1);
|
||||||
|
return ref _storages[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMasterHashStorage(in ValueSubStorage storage) => _storages[(int)Storage.MasterStorage].Set(in storage);
|
||||||
|
public void SetLayer1HashStorage(in ValueSubStorage storage) => _storages[(int)Storage.Layer1Storage].Set(in storage);
|
||||||
|
public void SetLayer2HashStorage(in ValueSubStorage storage) => _storages[(int)Storage.Layer2Storage].Set(in storage);
|
||||||
|
public void SetLayer3HashStorage(in ValueSubStorage storage) => _storages[(int)Storage.Layer3Storage].Set(in storage);
|
||||||
|
public void SetLayer4HashStorage(in ValueSubStorage storage) => _storages[(int)Storage.Layer4Storage].Set(in storage);
|
||||||
|
public void SetLayer5HashStorage(in ValueSubStorage storage) => _storages[(int)Storage.Layer5Storage].Set(in storage);
|
||||||
|
public void SetDataStorage(in ValueSubStorage storage) => _storages[(int)Storage.DataStorage].Set(in storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const uint IntegrityVerificationStorageMagic = 0x43465649; // IVFC
|
||||||
|
internal const uint IntegrityVerificationStorageVersion = 0x00020000;
|
||||||
|
internal const uint IntegrityVerificationStorageVersionMask = 0xFFFF0000;
|
||||||
|
|
||||||
|
internal const int HashSize = Sha256.DigestSize;
|
||||||
|
|
||||||
|
internal const int AccessCountMax = 5;
|
||||||
|
internal TimeSpan AccessTimeout => TimeSpan.FromMilliSeconds(10);
|
||||||
|
|
||||||
|
private const sbyte BaseBufferLevel = 0x10;
|
||||||
|
|
||||||
|
private FileSystemBufferManagerSet _bufferManagers;
|
||||||
|
private SdkRecursiveMutex _mutex;
|
||||||
|
private IntegrityVerificationStorage[] _integrityStorages;
|
||||||
|
private BlockCacheBufferedStorage[] _bufferedStorages;
|
||||||
|
private Semaphore _readSemaphore;
|
||||||
|
private Semaphore _writeSemaphore;
|
||||||
|
private long _dataSize;
|
||||||
|
private int _layerCount;
|
||||||
|
|
||||||
|
// LibHac addition
|
||||||
|
private FileSystemServer _fsServer;
|
||||||
|
|
||||||
|
private static readonly byte[][] KeyArray =
|
||||||
|
{ MasterKey.ToArray(), L1Key.ToArray(), L2Key.ToArray(), L3Key.ToArray(), L4Key.ToArray(), L5Key.ToArray() };
|
||||||
|
|
||||||
|
public HierarchicalIntegrityVerificationStorage(FileSystemServer fsServer)
|
||||||
|
{
|
||||||
|
_integrityStorages = new IntegrityVerificationStorage[IntegrityMaxLayerCount - 1];
|
||||||
|
_bufferedStorages = new BlockCacheBufferedStorage[IntegrityMaxLayerCount - 1];
|
||||||
|
|
||||||
|
_dataSize = -1;
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
FinalizeObject();
|
||||||
|
|
||||||
|
if (_integrityStorages is not null)
|
||||||
|
{
|
||||||
|
foreach (IntegrityVerificationStorage storage in _integrityStorages)
|
||||||
|
{
|
||||||
|
storage.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_bufferedStorages is not null)
|
||||||
|
{
|
||||||
|
foreach (BlockCacheBufferedStorage storage in _bufferedStorages)
|
||||||
|
{
|
||||||
|
storage.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileSystemBufferManagerSet GetBuffers()
|
||||||
|
{
|
||||||
|
return _bufferManagers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetParameters(out HierarchicalIntegrityVerificationStorageControlArea.InputParam outParam)
|
||||||
|
{
|
||||||
|
outParam = default;
|
||||||
|
|
||||||
|
for (int i = 0; i < _layerCount - 2; i++)
|
||||||
|
{
|
||||||
|
outParam.LevelBlockSizes[i] = _integrityStorages[i].GetBlockSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsInitialized()
|
||||||
|
{
|
||||||
|
return _dataSize >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueSubStorage GetL1HashStorage()
|
||||||
|
{
|
||||||
|
return new ValueSubStorage(_bufferedStorages[_layerCount - 3], 0,
|
||||||
|
DivideUp(_dataSize, GetL1HashVerificationBlockSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetL1HashVerificationBlockSize()
|
||||||
|
{
|
||||||
|
return _integrityStorages[_layerCount - 2].GetBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Initialize(in HierarchicalIntegrityVerificationInformation info,
|
||||||
|
ref HierarchicalStorageInformation storageInfo, FileSystemBufferManagerSet buffers,
|
||||||
|
IHash256GeneratorFactory hashGeneratorFactory, bool isHashSaltEnabled, SdkRecursiveMutex mutex,
|
||||||
|
int maxDataCacheEntries, int maxHashCacheEntries, sbyte bufferLevel, bool isWritable, bool allowClearedBlocks)
|
||||||
|
{
|
||||||
|
return Initialize(in info, ref storageInfo, buffers, hashGeneratorFactory, isHashSaltEnabled, mutex, null, null,
|
||||||
|
maxDataCacheEntries, maxHashCacheEntries, bufferLevel, isWritable, allowClearedBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Initialize(in HierarchicalIntegrityVerificationInformation info,
|
||||||
|
ref HierarchicalStorageInformation storageInfo, FileSystemBufferManagerSet buffers,
|
||||||
|
IHash256GeneratorFactory hashGeneratorFactory, bool isHashSaltEnabled, SdkRecursiveMutex mutex,
|
||||||
|
Semaphore readSemaphore, Semaphore writeSemaphore, int maxDataCacheEntries, int maxHashCacheEntries,
|
||||||
|
sbyte bufferLevel, bool isWritable, bool allowClearedBlocks)
|
||||||
|
{
|
||||||
|
// Validate preconditions.
|
||||||
|
Assert.SdkNotNull(buffers);
|
||||||
|
Assert.SdkAssert(info.MaxLayers >= IntegrityMinLayerCount && info.MaxLayers <= IntegrityMaxLayerCount);
|
||||||
|
|
||||||
|
// Set member variables.
|
||||||
|
_layerCount = (int)info.MaxLayers;
|
||||||
|
_bufferManagers = buffers;
|
||||||
|
_mutex = mutex;
|
||||||
|
_readSemaphore = readSemaphore;
|
||||||
|
_writeSemaphore = writeSemaphore;
|
||||||
|
|
||||||
|
{
|
||||||
|
// If hash salt is enabled, generate it.
|
||||||
|
var mac = new Optional<HashSalt>();
|
||||||
|
if (isHashSaltEnabled)
|
||||||
|
{
|
||||||
|
mac.Set();
|
||||||
|
HmacSha256.GenerateHmacSha256(mac.Value.Hash, info.HashSalt.HashRo, KeyArray[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the top level verification storage.
|
||||||
|
_integrityStorages[0] = new IntegrityVerificationStorage();
|
||||||
|
_integrityStorages[0].Initialize(in storageInfo[(int)HierarchicalStorageInformation.Storage.MasterStorage],
|
||||||
|
in storageInfo[(int)HierarchicalStorageInformation.Storage.Layer1Storage],
|
||||||
|
1 << info.Layers[0].BlockOrder, HashSize, _bufferManagers.Buffers[_layerCount - 2],
|
||||||
|
hashGeneratorFactory, in mac, false, isWritable, allowClearedBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the top level buffer storage.
|
||||||
|
_bufferedStorages[0] = new BlockCacheBufferedStorage();
|
||||||
|
Result rc = _bufferedStorages[0].Initialize(_bufferManagers.Buffers[0], _mutex, _integrityStorages[0],
|
||||||
|
info.Layers[0].Size, 1 << info.Layers[0].BlockOrder, maxHashCacheEntries, false, BaseBufferLevel, false,
|
||||||
|
isWritable);
|
||||||
|
|
||||||
|
if (!rc.IsFailure())
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
|
||||||
|
// Initialize the level storages.
|
||||||
|
for (level = 0; level < _layerCount - 3; level++)
|
||||||
|
{
|
||||||
|
// If hash salt is enabled, generate it.
|
||||||
|
var mac = new Optional<HashSalt>();
|
||||||
|
if (isHashSaltEnabled)
|
||||||
|
{
|
||||||
|
mac.Set();
|
||||||
|
HmacSha256.GenerateHmacSha256(mac.Value.Hash, info.HashSalt.HashRo, KeyArray[level + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the verification storage.
|
||||||
|
using (var hashStorage = new ValueSubStorage(_bufferedStorages[level], 0, info.Layers[level].Size))
|
||||||
|
{
|
||||||
|
_integrityStorages[level + 1] = new IntegrityVerificationStorage();
|
||||||
|
_integrityStorages[level + 1].Initialize(in hashStorage, in storageInfo[level + 2],
|
||||||
|
1 << info.Layers[level + 1].BlockOrder, 1 << info.Layers[level].BlockOrder,
|
||||||
|
_bufferManagers.Buffers[_layerCount - 2], hashGeneratorFactory, in mac, false, isWritable,
|
||||||
|
allowClearedBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the buffer storage.
|
||||||
|
_bufferedStorages[level + 1] = new BlockCacheBufferedStorage();
|
||||||
|
rc = _bufferedStorages[level + 1].Initialize(_bufferManagers.Buffers[level + 1], _mutex,
|
||||||
|
_integrityStorages[level + 1], info.Layers[level + 1].Size, 1 << info.Layers[level + 1].BlockOrder,
|
||||||
|
maxHashCacheEntries, false, (sbyte)(BaseBufferLevel + (level + 1)), false, isWritable);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// Cleanup initialized storages if we failed.
|
||||||
|
_integrityStorages[level + 1].FinalizeObject();
|
||||||
|
|
||||||
|
for (; level > 0; level--)
|
||||||
|
{
|
||||||
|
_bufferedStorages[level].FinalizeObject();
|
||||||
|
_integrityStorages[level].FinalizeObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rc.IsFailure())
|
||||||
|
{
|
||||||
|
// Initialize the final level storage.
|
||||||
|
// If hash salt is enabled, generate it.
|
||||||
|
var mac = new Optional<HashSalt>();
|
||||||
|
if (isHashSaltEnabled)
|
||||||
|
{
|
||||||
|
mac.Set();
|
||||||
|
HmacSha256.GenerateHmacSha256(mac.Value.Hash, info.HashSalt.HashRo, KeyArray[level + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the verification storage.
|
||||||
|
using (var hashStorage = new ValueSubStorage(_bufferedStorages[level], 0, info.Layers[level].Size))
|
||||||
|
{
|
||||||
|
_integrityStorages[level + 1] = new IntegrityVerificationStorage();
|
||||||
|
_integrityStorages[level + 1].Initialize(in hashStorage,
|
||||||
|
in storageInfo[(int)HierarchicalStorageInformation.Storage.DataStorage],
|
||||||
|
1 << info.Layers[level + 1].BlockOrder, 1 << info.Layers[level].BlockOrder,
|
||||||
|
_bufferManagers.Buffers[_layerCount - 2], hashGeneratorFactory, in mac, true, isWritable,
|
||||||
|
allowClearedBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the buffer storage.
|
||||||
|
_bufferedStorages[level + 1] = new BlockCacheBufferedStorage();
|
||||||
|
rc = _bufferedStorages[level + 1].Initialize(_bufferManagers.Buffers[level + 1], _mutex,
|
||||||
|
_integrityStorages[level + 1], info.Layers[level + 1].Size, 1 << info.Layers[level + 1].BlockOrder,
|
||||||
|
maxDataCacheEntries, true, bufferLevel, true, isWritable);
|
||||||
|
|
||||||
|
if (!rc.IsFailure())
|
||||||
|
{
|
||||||
|
_dataSize = info.Layers[level + 1].Size;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup initialized storages if we failed.
|
||||||
|
_integrityStorages[level + 1].FinalizeObject();
|
||||||
|
|
||||||
|
for (; level > 0; level--)
|
||||||
|
{
|
||||||
|
_bufferedStorages[level].FinalizeObject();
|
||||||
|
_integrityStorages[level].FinalizeObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_bufferedStorages[0].FinalizeObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
_integrityStorages[0].FinalizeObject();
|
||||||
|
|
||||||
|
// Ensure we're uninitialized if we failed.
|
||||||
|
_dataSize = -1;
|
||||||
|
_bufferManagers = null;
|
||||||
|
_mutex = null;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinalizeObject()
|
||||||
|
{
|
||||||
|
if (_dataSize >= 0)
|
||||||
|
{
|
||||||
|
_dataSize = 0;
|
||||||
|
_bufferManagers = null;
|
||||||
|
_mutex = null;
|
||||||
|
|
||||||
|
for (int level = _layerCount - 2; level >= 0; level--)
|
||||||
|
{
|
||||||
|
_bufferedStorages[level].FinalizeObject();
|
||||||
|
_integrityStorages[level].FinalizeObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
_dataSize = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result GetSize(out long size)
|
||||||
|
{
|
||||||
|
Assert.SdkRequires(_dataSize >= 0);
|
||||||
|
|
||||||
|
size = _dataSize;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result SetSize(long size)
|
||||||
|
{
|
||||||
|
return ResultFs.UnsupportedSetSizeForHierarchicalIntegrityVerificationStorage.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Read(long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
Assert.SdkRequires(_dataSize >= 0);
|
||||||
|
|
||||||
|
if (destination.Length == 0)
|
||||||
|
return Result.Success;
|
||||||
|
|
||||||
|
ref HierarchicalIntegrityVerificationStorageGlobals g =
|
||||||
|
ref _fsServer.Globals.HierarchicalIntegrityVerificationStorage;
|
||||||
|
|
||||||
|
_readSemaphore?.Acquire();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!g.GlobalReadSemaphore.TimedAcquire(AccessTimeout))
|
||||||
|
{
|
||||||
|
for (int level = _layerCount - 2; level >= 0; level--)
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[level].Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
|
g.GlobalReadSemaphore.Acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[_layerCount - 2].Read(offset, destination);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
g.GlobalReadSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_readSemaphore?.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||||
|
{
|
||||||
|
Assert.SdkRequires(_dataSize >= 0);
|
||||||
|
|
||||||
|
if (source.Length == 0)
|
||||||
|
return Result.Success;
|
||||||
|
|
||||||
|
ref HierarchicalIntegrityVerificationStorageGlobals g =
|
||||||
|
ref _fsServer.Globals.HierarchicalIntegrityVerificationStorage;
|
||||||
|
|
||||||
|
_writeSemaphore?.Acquire();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!g.GlobalWriteSemaphore.TimedAcquire(AccessTimeout))
|
||||||
|
{
|
||||||
|
for (int level = _layerCount - 2; level >= 0; level--)
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[level].Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
|
g.GlobalWriteSemaphore.Acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[_layerCount - 2].Write(offset, source);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
g.GlobalWriteSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_writeSemaphore?.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Flush()
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
||||||
|
ReadOnlySpan<byte> inBuffer)
|
||||||
|
{
|
||||||
|
switch (operationId)
|
||||||
|
{
|
||||||
|
case OperationId.FillZero:
|
||||||
|
case OperationId.DestroySignature:
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[_layerCount - 2]
|
||||||
|
.OperateRange(outBuffer, operationId, offset, size, inBuffer);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
case OperationId.InvalidateCache:
|
||||||
|
case OperationId.QueryRange:
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[_layerCount - 2]
|
||||||
|
.OperateRange(outBuffer, operationId, offset, size, inBuffer);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ResultFs.UnsupportedOperateRangeForHierarchicalIntegrityVerificationStorage.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Commit()
|
||||||
|
{
|
||||||
|
for (int level = _layerCount - 2; level >= 0; level--)
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[level].Commit();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OnRollback()
|
||||||
|
{
|
||||||
|
for (int level = _layerCount - 2; level >= 0; level--)
|
||||||
|
{
|
||||||
|
Result rc = _bufferedStorages[level].OnRollback();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetGenerateRandomFunction(FileSystemServer fsServer, RandomDataGenerator function)
|
||||||
|
{
|
||||||
|
fsServer.Globals.HierarchicalIntegrityVerificationStorage.GenerateRandom = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static sbyte GetDefaultDataCacheBufferLevel(int maxLayers)
|
||||||
|
{
|
||||||
|
return (sbyte)(BaseBufferLevel + maxLayers - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>"<c>HierarchicalIntegrityVerificationStorage::Master</c>"</summary>
|
||||||
|
public static ReadOnlySpan<byte> MasterKey => // "HierarchicalIntegrityVerificationStorage::Master"
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte)'H', (byte)'i', (byte)'e', (byte)'r', (byte)'a', (byte)'r', (byte)'c', (byte)'h',
|
||||||
|
(byte)'i', (byte)'c', (byte)'a', (byte)'l', (byte)'I', (byte)'n', (byte)'t', (byte)'e',
|
||||||
|
(byte)'g', (byte)'r', (byte)'i', (byte)'t', (byte)'y', (byte)'V', (byte)'e', (byte)'r',
|
||||||
|
(byte)'i', (byte)'f', (byte)'i', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o',
|
||||||
|
(byte)'n', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e',
|
||||||
|
(byte)':', (byte)':', (byte)'M', (byte)'a', (byte)'s', (byte)'t', (byte)'e', (byte)'r'
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>"<c>HierarchicalIntegrityVerificationStorage::L1</c>"</summary>
|
||||||
|
public static ReadOnlySpan<byte> L1Key => // "HierarchicalIntegrityVerificationStorage::L1"
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte)'H', (byte)'i', (byte)'e', (byte)'r', (byte)'a', (byte)'r', (byte)'c', (byte)'h',
|
||||||
|
(byte)'i', (byte)'c', (byte)'a', (byte)'l', (byte)'I', (byte)'n', (byte)'t', (byte)'e',
|
||||||
|
(byte)'g', (byte)'r', (byte)'i', (byte)'t', (byte)'y', (byte)'V', (byte)'e', (byte)'r',
|
||||||
|
(byte)'i', (byte)'f', (byte)'i', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o',
|
||||||
|
(byte)'n', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e',
|
||||||
|
(byte)':', (byte)':', (byte)'L', (byte)'1'
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>"<c>HierarchicalIntegrityVerificationStorage::L2</c>"</summary>
|
||||||
|
public static ReadOnlySpan<byte> L2Key => // "HierarchicalIntegrityVerificationStorage::L2"
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte)'H', (byte)'i', (byte)'e', (byte)'r', (byte)'a', (byte)'r', (byte)'c', (byte)'h',
|
||||||
|
(byte)'i', (byte)'c', (byte)'a', (byte)'l', (byte)'I', (byte)'n', (byte)'t', (byte)'e',
|
||||||
|
(byte)'g', (byte)'r', (byte)'i', (byte)'t', (byte)'y', (byte)'V', (byte)'e', (byte)'r',
|
||||||
|
(byte)'i', (byte)'f', (byte)'i', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o',
|
||||||
|
(byte)'n', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e',
|
||||||
|
(byte)':', (byte)':', (byte)'L', (byte)'2'
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>"<c>HierarchicalIntegrityVerificationStorage::L3</c>"</summary>
|
||||||
|
public static ReadOnlySpan<byte> L3Key => // "HierarchicalIntegrityVerificationStorage::L3"
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte)'H', (byte)'i', (byte)'e', (byte)'r', (byte)'a', (byte)'r', (byte)'c', (byte)'h',
|
||||||
|
(byte)'i', (byte)'c', (byte)'a', (byte)'l', (byte)'I', (byte)'n', (byte)'t', (byte)'e',
|
||||||
|
(byte)'g', (byte)'r', (byte)'i', (byte)'t', (byte)'y', (byte)'V', (byte)'e', (byte)'r',
|
||||||
|
(byte)'i', (byte)'f', (byte)'i', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o',
|
||||||
|
(byte)'n', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e',
|
||||||
|
(byte)':', (byte)':', (byte)'L', (byte)'3'
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>"<c>HierarchicalIntegrityVerificationStorage::L4</c>"</summary>
|
||||||
|
public static ReadOnlySpan<byte> L4Key => // "HierarchicalIntegrityVerificationStorage::L4"
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte)'H', (byte)'i', (byte)'e', (byte)'r', (byte)'a', (byte)'r', (byte)'c', (byte)'h',
|
||||||
|
(byte)'i', (byte)'c', (byte)'a', (byte)'l', (byte)'I', (byte)'n', (byte)'t', (byte)'e',
|
||||||
|
(byte)'g', (byte)'r', (byte)'i', (byte)'t', (byte)'y', (byte)'V', (byte)'e', (byte)'r',
|
||||||
|
(byte)'i', (byte)'f', (byte)'i', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o',
|
||||||
|
(byte)'n', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e',
|
||||||
|
(byte)':', (byte)':', (byte)'L', (byte)'4'
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>"<c>HierarchicalIntegrityVerificationStorage::L5</c>"</summary>
|
||||||
|
public static ReadOnlySpan<byte> L5Key => // "HierarchicalIntegrityVerificationStorage::L5"
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte)'H', (byte)'i', (byte)'e', (byte)'r', (byte)'a', (byte)'r', (byte)'c', (byte)'h',
|
||||||
|
(byte)'i', (byte)'c', (byte)'a', (byte)'l', (byte)'I', (byte)'n', (byte)'t', (byte)'e',
|
||||||
|
(byte)'g', (byte)'r', (byte)'i', (byte)'t', (byte)'y', (byte)'V', (byte)'e', (byte)'r',
|
||||||
|
(byte)'i', (byte)'f', (byte)'i', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o',
|
||||||
|
(byte)'n', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e',
|
||||||
|
(byte)':', (byte)':', (byte)'L', (byte)'5'
|
||||||
|
};
|
||||||
|
}
|
|
@ -43,6 +43,11 @@ public struct Optional<T>
|
||||||
|
|
||||||
public static implicit operator Optional<T>(in T value) => new Optional<T>(in value);
|
public static implicit operator Optional<T>(in T value) => new Optional<T>(in value);
|
||||||
|
|
||||||
|
public void Set()
|
||||||
|
{
|
||||||
|
_hasValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void Set(T value)
|
public void Set(T value)
|
||||||
{
|
{
|
||||||
_value = value;
|
_value = value;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static LibHac.Tests.Common.Layout;
|
using static LibHac.Tests.Common.Layout;
|
||||||
|
@ -279,4 +280,78 @@ public class TypeLayoutTests
|
||||||
Assert.Equal(0x9, GetOffset(in s, in s.Reserved));
|
Assert.Equal(0x9, GetOffset(in s, in s.Reserved));
|
||||||
Assert.Equal(0xC, GetOffset(in s, in s.Generation));
|
Assert.Equal(0xC, GetOffset(in s, in s.Generation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void HierarchicalIntegrityVerificationLevelInformation_Layout()
|
||||||
|
{
|
||||||
|
HierarchicalIntegrityVerificationLevelInformation s = default;
|
||||||
|
|
||||||
|
Assert.Equal(0x18, Unsafe.SizeOf<HierarchicalIntegrityVerificationLevelInformation>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Offset));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.Size));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.BlockOrder));
|
||||||
|
Assert.Equal(0x14, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct HierarchicalIntegrityVerificationLevelInformationAlignmentTest
|
||||||
|
{
|
||||||
|
public byte A;
|
||||||
|
public HierarchicalIntegrityVerificationLevelInformation B;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void HierarchicalIntegrityVerificationLevelInformation_Alignment()
|
||||||
|
{
|
||||||
|
var s = new HierarchicalIntegrityVerificationLevelInformationAlignmentTest();
|
||||||
|
|
||||||
|
Assert.Equal(0x1C, Unsafe.SizeOf<HierarchicalIntegrityVerificationLevelInformationAlignmentTest>());
|
||||||
|
|
||||||
|
Assert.Equal(0, GetOffset(in s, in s.A));
|
||||||
|
Assert.Equal(4, GetOffset(in s, in s.B));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void HierarchicalIntegrityVerificationInformation_Layout()
|
||||||
|
{
|
||||||
|
HierarchicalIntegrityVerificationInformation s = default;
|
||||||
|
|
||||||
|
Assert.Equal(0xB4, Unsafe.SizeOf<HierarchicalIntegrityVerificationInformation>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.MaxLayers));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.Layers));
|
||||||
|
Assert.Equal(0x94, GetOffset(in s, in s.HashSalt));
|
||||||
|
|
||||||
|
Assert.Equal(Constants.IntegrityMaxLayerCount - 1, s.Layers.ItemsRo.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void HierarchicalIntegrityVerificationMetaInformation_Layout()
|
||||||
|
{
|
||||||
|
HierarchicalIntegrityVerificationMetaInformation s = default;
|
||||||
|
|
||||||
|
Assert.Equal(0xC0, Unsafe.SizeOf<HierarchicalIntegrityVerificationMetaInformation>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Magic));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.Version));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.MasterHashSize));
|
||||||
|
Assert.Equal(0x0C, GetOffset(in s, in s.LevelHashInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void HierarchicalIntegrityVerificationSizeSet_Layout()
|
||||||
|
{
|
||||||
|
HierarchicalIntegrityVerificationSizeSet s = default;
|
||||||
|
|
||||||
|
Assert.Equal(Constants.IntegrityMaxLayerCount - 2, s.LayeredHashSizes.ItemsRo.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void HierarchicalIntegrityVerificationStorageControlArea_InputParam_Layout()
|
||||||
|
{
|
||||||
|
HierarchicalIntegrityVerificationStorageControlArea.InputParam s = default;
|
||||||
|
|
||||||
|
Assert.Equal(Constants.IntegrityMaxLayerCount - 1, s.LevelBlockSizes.ItemsRo.Length);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue