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 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()

View file

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

View file

@ -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);

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 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;

View file

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