Add HierarchicalIntegrityVerificationStorage

This commit is contained in:
Alex Barney 2022-04-18 18:01:38 -07:00
parent b54f5d17fa
commit 6c9e6e5203
7 changed files with 922 additions and 6 deletions

View 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);
}
}

View file

@ -44,6 +44,7 @@ internal struct FileSystemServerGlobals : IDisposable
public LocationResolverSetGlobals LocationResolverSet;
public PooledBufferGlobals PooledBuffer;
public GameCardServiceGlobals GameCardService;
public HierarchicalIntegrityVerificationStorageGlobals HierarchicalIntegrityVerificationStorage;
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
{
@ -55,6 +56,7 @@ internal struct FileSystemServerGlobals : IDisposable
LocationResolverSet.Initialize();
PooledBuffer.Initialize();
GameCardService.Initialize();
HierarchicalIntegrityVerificationStorage.Initialize(fsServer);
}
public void Dispose()

View file

@ -5,11 +5,11 @@ namespace LibHac.FsSystem;
public static class BitmapUtils
{
// ReSharper disable once InconsistentNaming
public static uint ILog2(uint value)
public static int ILog2(uint value)
{
Assert.SdkRequiresGreater(value, 0u);
const uint intBitCount = 32;
return intBitCount - 1 - (uint)Util.BitUtil.CountLeadingZeros(value);
const int intBitCount = 32;
return intBitCount - 1 - Util.BitUtil.CountLeadingZeros(value);
}
}

View file

@ -59,8 +59,8 @@ public class BlockCacheBufferedStorage : IStorage
private int _shiftBytesVerificationBlock;
private int _flags;
private int _bufferLevel;
private StorageType _storageType;
private BlockCacheManager<CacheEntry, AccessRange> _cacheManager;
private bool _isWritable;
public BlockCacheBufferedStorage()
{
@ -93,7 +93,7 @@ public class BlockCacheBufferedStorage : IStorage
public Result Initialize(IBufferManager bufferManager, SdkRecursiveMutex mutex, IStorage data, long dataSize,
int sizeBytesVerificationBlock, int maxCacheEntries, bool useRealDataCache, sbyte bufferLevel,
bool useKeepBurstMode, StorageType storageType)
bool useKeepBurstMode, bool isWritable)
{
Assert.SdkNotNull(data);
Assert.SdkNotNull(mutex);
@ -111,7 +111,7 @@ public class BlockCacheBufferedStorage : IStorage
_lastResult = Result.Success;
_flags = 0;
_bufferLevel = bufferLevel;
_storageType = storageType;
_isWritable = isWritable;
_shiftBytesVerificationBlock = (int)BitmapUtils.ILog2((uint)sizeBytesVerificationBlock);
Assert.SdkEqual(1 << _shiftBytesVerificationBlock, _sizeBytesVerificationBlock);

View 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'
};
}

View file

@ -43,6 +43,11 @@ public struct Optional<T>
public static implicit operator Optional<T>(in T value) => new Optional<T>(in value);
public void Set()
{
_hasValue = true;
}
public void Set(T value)
{
_value = value;

View file

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.FsSystem;
using Xunit;
using static LibHac.Tests.Common.Layout;
@ -279,4 +280,78 @@ public class TypeLayoutTests
Assert.Equal(0x9, GetOffset(in s, in s.Reserved));
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);
}
}