From fc3fb188c78248fa43c143a2bbe91d52bc3b4a4a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 14 Apr 2022 14:18:24 -0700 Subject: [PATCH] Update IStorage range check functions for 14.0.0 --- src/LibHac/Fs/Common/FileStorage.cs | 66 +++++++-------- src/LibHac/Fs/IStorage.cs | 80 +++++++++++++++---- src/LibHac/Fs/MemoryStorage.cs | 10 +-- src/LibHac/Fs/SubStorage.cs | 44 ++++++---- src/LibHac/Fs/ValueSubStorage.cs | 33 +++++--- .../FsSystem/AlignmentMatchingStorage.cs | 40 +++++----- src/LibHac/Tools/FsSystem/AesCbcStorage.cs | 6 +- src/LibHac/Tools/FsSystem/CachedStorage.cs | 8 +- .../Tools/FsSystem/ConcatenationStorage.cs | 12 +-- .../Tools/FsSystem/Save/DuplexStorage.cs | 12 +-- .../Tools/FsSystem/Save/JournalStorage.cs | 12 +-- src/LibHac/Util/IntUtil.cs | 37 +++++++++ 12 files changed, 236 insertions(+), 124 deletions(-) create mode 100644 src/LibHac/Util/IntUtil.cs diff --git a/src/LibHac/Fs/Common/FileStorage.cs b/src/LibHac/Fs/Common/FileStorage.cs index a12fce38..e6667c1c 100644 --- a/src/LibHac/Fs/Common/FileStorage.cs +++ b/src/LibHac/Fs/Common/FileStorage.cs @@ -11,7 +11,7 @@ namespace LibHac.Fs; /// /// Allows interacting with an via an interface. /// -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class FileStorage : IStorage { private const long InvalidSize = -1; @@ -60,8 +60,8 @@ public class FileStorage : IStorage Result rc = UpdateSize(); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, destination.Length, _fileSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, destination.Length, _fileSize); + if (rc.IsFailure()) return rc.Miss(); return _baseFile.Read(out _, offset, destination, ReadOption.None); } @@ -74,8 +74,8 @@ public class FileStorage : IStorage Result rc = UpdateSize(); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, source.Length, _fileSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, source.Length, _fileSize); + if (rc.IsFailure()) return rc.Miss(); return _baseFile.Write(offset, source, WriteOption.None); } @@ -104,35 +104,37 @@ public class FileStorage : IStorage public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { - if (operationId == OperationId.InvalidateCache) + switch (operationId) { - Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - if (operationId == OperationId.QueryRange) - { - if (size == 0) + case OperationId.InvalidateCache: { - if (outBuffer.Length != Unsafe.SizeOf()) - return ResultFs.InvalidSize.Log(); + Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size); + if (rc.IsFailure()) return rc.Miss(); - SpanHelpers.AsStruct(outBuffer).Clear(); return Result.Success; } + case OperationId.QueryRange: + { + if (size == 0) + { + if (outBuffer.Length != Unsafe.SizeOf()) + return ResultFs.InvalidSize.Log(); - Result rc = UpdateSize(); - if (rc.IsFailure()) return rc.Miss(); + SpanHelpers.AsStruct(outBuffer).Clear(); + return Result.Success; + } - if (!CheckOffsetAndSize(offset, size)) - return ResultFs.OutOfRange.Log(); + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc.Miss(); - return _baseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer); + rc = CheckOffsetAndSize(offset, size); + if (rc.IsFailure()) return rc.Miss(); + + return _baseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer); + } + default: + return ResultFs.UnsupportedOperateRangeForFileStorage.Log(); } - - return ResultFs.UnsupportedOperateRangeForFileStorage.Log(); } private Result UpdateSize() @@ -153,7 +155,7 @@ public class FileStorage : IStorage /// interface. The opened file will automatically be closed when the /// is disposed. /// -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class FileStorageBasedFileSystem : FileStorage { private SharedRef _baseFileSystem; @@ -196,7 +198,7 @@ public class FileStorageBasedFileSystem : FileStorage /// Provides an interface for interacting with an opened file from a mounted file system. /// The caller may choose whether or not the file will be closed when the is disposed. /// -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class FileHandleStorage : IStorage { private const long InvalidSize = -1; @@ -230,7 +232,7 @@ public class FileHandleStorage : IStorage _handle = handle; _closeFile = closeFile; _size = InvalidSize; - _mutex.Initialize(); + _mutex = new SdkMutexType(); } public override void Dispose() @@ -255,8 +257,8 @@ public class FileHandleStorage : IStorage Result rc = UpdateSize(); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, destination.Length, _size)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, destination.Length, _size); + if (rc.IsFailure()) return rc.Miss(); return _fsClient.ReadFile(_handle, offset, destination, ReadOption.None); } @@ -271,8 +273,8 @@ public class FileHandleStorage : IStorage Result rc = UpdateSize(); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, source.Length, _size)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, source.Length, _size); + if (rc.IsFailure()) return rc.Miss(); return _fsClient.WriteFile(_handle, offset, source, WriteOption.None); } diff --git a/src/LibHac/Fs/IStorage.cs b/src/LibHac/Fs/IStorage.cs index 8dd6c702..02b93c3a 100644 --- a/src/LibHac/Fs/IStorage.cs +++ b/src/LibHac/Fs/IStorage.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using LibHac.Util; namespace LibHac.Fs; @@ -7,12 +8,7 @@ namespace LibHac.Fs; /// /// Provides an interface for reading and writing a sequence of bytes. /// -/// -/// The official IStorage makes the Read etc. methods abstract and doesn't -/// have DoRead etc. methods. We're using them here so we can make sure -/// the object isn't disposed before calling the method implementation. -/// -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public abstract class IStorage : IDisposable { public virtual void Dispose() { } @@ -78,20 +74,70 @@ public abstract class IStorage : IDisposable return OperateRange(Span.Empty, operationId, offset, size, ReadOnlySpan.Empty); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CheckAccessRange(long offset, long size, long totalSize) + public static Result CheckAccessRange(long offset, long size, long totalSize) { - return offset >= 0 && - size >= 0 && - size <= totalSize && - offset <= totalSize - size; + if (offset < 0) + return ResultFs.InvalidOffset.Log(); + + if (size < 0) + return ResultFs.InvalidSize.Log(); + + if (!IntUtil.CanAddWithoutOverflow(offset, size)) + return ResultFs.OutOfRange.Log(); + + if (size + offset > totalSize) + return ResultFs.OutOfRange.Log(); + + return Result.Success; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CheckOffsetAndSize(long offset, long size) + public static Result CheckAccessRange(long offset, ulong size, long totalSize) { - return offset >= 0 && - size >= 0 && - offset <= offset + size; + Result rc = CheckAccessRange(offset, unchecked((long)size), totalSize); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result CheckOffsetAndSize(long offset, long size) + { + if (offset < 0) + return ResultFs.InvalidOffset.Log(); + + if (size < 0) + return ResultFs.InvalidSize.Log(); + + if (!IntUtil.CanAddWithoutOverflow(offset, size)) + return ResultFs.OutOfRange.Log(); + + return Result.Success; + } + + public static Result CheckOffsetAndSize(long offset, ulong size) + { + Result rc = CheckOffsetAndSize(offset, unchecked((long)size)); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result CheckOffsetAndSizeWithResult(long offset, long size, Result resultOnFailure) + { + Result rc = CheckOffsetAndSize(offset, size); + + if (rc.IsFailure()) + return resultOnFailure.Log(); + + return Result.Success; + } + + public static Result CheckOffsetAndSizeWithResult(long offset, ulong size, Result resultOnFailure) + { + Result rc = CheckOffsetAndSize(offset, size); + + if (rc.IsFailure()) + return resultOnFailure.Log(); + + return Result.Success; } } \ No newline at end of file diff --git a/src/LibHac/Fs/MemoryStorage.cs b/src/LibHac/Fs/MemoryStorage.cs index f9cdf523..0e4bf511 100644 --- a/src/LibHac/Fs/MemoryStorage.cs +++ b/src/LibHac/Fs/MemoryStorage.cs @@ -7,7 +7,7 @@ namespace LibHac.Fs; /// /// Allows interacting with a array via the interface. /// -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class MemoryStorage : IStorage { private byte[] _storageBuffer; @@ -22,8 +22,8 @@ public class MemoryStorage : IStorage if (destination.Length == 0) return Result.Success; - if (!CheckAccessRange(offset, destination.Length, _storageBuffer.Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, _storageBuffer.Length); + if (rc.IsFailure()) return rc.Miss(); _storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination); @@ -35,8 +35,8 @@ public class MemoryStorage : IStorage if (source.Length == 0) return Result.Success; - if (!CheckAccessRange(offset, source.Length, _storageBuffer.Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, source.Length, _storageBuffer.Length); + if (rc.IsFailure()) return rc.Miss(); source.CopyTo(_storageBuffer.AsSpan((int)offset)); diff --git a/src/LibHac/Fs/SubStorage.cs b/src/LibHac/Fs/SubStorage.cs index 72394195..e6f23741 100644 --- a/src/LibHac/Fs/SubStorage.cs +++ b/src/LibHac/Fs/SubStorage.cs @@ -8,16 +8,17 @@ namespace LibHac.Fs; /// Presents a subsection of a base IStorage as a new IStorage. /// /// -/// A SubStorage presents a sub-range of an IStorage as a separate IStorage. +/// A SubStorage presents a sub-range of an IStorage as a separate IStorage. /// -/// The SubStorage doesn't check if the offset and size provided are actually in the base storage. +/// The SubStorage doesn't check if the offset and size provided are actually in the base storage. /// GetSize will return the size given to the SubStorage at initialization and will not query -/// the base storage's size. +/// the base storage's size. /// -/// A SubStorage is non-resizable by default. may be used to mark +/// A SubStorage is non-resizable by default. may be used to mark /// the SubStorage as resizable. The SubStorage may only be resized if the end of the SubStorage /// is located at the end of the base storage. When resizing the SubStorage, the base storage -/// will be resized to the appropriate length. +/// will be resized to the appropriate length. +/// Based on FS 14.1.0 (nnSdk 14.3.0) /// public class SubStorage : IStorage { @@ -158,10 +159,13 @@ public class SubStorage : IStorage if (!IsValid()) return ResultFs.NotInitialized.Log(); if (destination.Length == 0) return Result.Success; - if (!CheckAccessRange(offset, destination.Length, _size)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, _size); + if (rc.IsFailure()) return rc.Miss(); - return BaseStorage.Read(_offset + offset, destination); + rc = BaseStorage.Read(_offset + offset, destination); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public override Result Write(long offset, ReadOnlySpan source) @@ -169,26 +173,34 @@ public class SubStorage : IStorage if (!IsValid()) return ResultFs.NotInitialized.Log(); if (source.Length == 0) return Result.Success; - if (!CheckAccessRange(offset, source.Length, _size)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, source.Length, _size); + if (rc.IsFailure()) return rc.Miss(); - return BaseStorage.Write(_offset + offset, source); + rc = BaseStorage.Write(_offset + offset, source); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public override Result Flush() { if (!IsValid()) return ResultFs.NotInitialized.Log(); - return BaseStorage.Flush(); + Result rc = BaseStorage.Flush(); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public override Result SetSize(long size) { if (!IsValid()) return ResultFs.NotInitialized.Log(); if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log(); - if (!CheckOffsetAndSize(_offset, size)) return ResultFs.InvalidSize.Log(); - Result rc = BaseStorage.GetSize(out long currentSize); + Result rc = CheckOffsetAndSize(_offset, size); + if (rc.IsFailure()) return rc.Miss(); + + rc = BaseStorage.GetSize(out long currentSize); if (rc.IsFailure()) return rc; if (currentSize != _offset + _size) @@ -222,7 +234,9 @@ public class SubStorage : IStorage if (operationId != OperationId.InvalidateCache) { if (size == 0) return Result.Success; - if (!CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log(); + + Result rc = CheckOffsetAndSize(_offset, size); + if (rc.IsFailure()) return rc.Miss(); } return BaseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer); diff --git a/src/LibHac/Fs/ValueSubStorage.cs b/src/LibHac/Fs/ValueSubStorage.cs index 4605acf8..7cabaf9c 100644 --- a/src/LibHac/Fs/ValueSubStorage.cs +++ b/src/LibHac/Fs/ValueSubStorage.cs @@ -112,10 +112,13 @@ public struct ValueSubStorage : IDisposable if (!IsValid()) return ResultFs.NotInitialized.Log(); if (destination.Length == 0) return Result.Success; - if (!IStorage.CheckAccessRange(offset, destination.Length, _size)) - return ResultFs.OutOfRange.Log(); + Result rc = IStorage.CheckAccessRange(offset, destination.Length, _size); + if (rc.IsFailure()) return rc.Miss(); - return _baseStorage.Read(_offset + offset, destination); + rc = _baseStorage.Read(_offset + offset, destination); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public readonly Result Write(long offset, ReadOnlySpan source) @@ -123,26 +126,34 @@ public struct ValueSubStorage : IDisposable if (!IsValid()) return ResultFs.NotInitialized.Log(); if (source.Length == 0) return Result.Success; - if (!IStorage.CheckAccessRange(offset, source.Length, _size)) - return ResultFs.OutOfRange.Log(); + Result rc = IStorage.CheckAccessRange(offset, source.Length, _size); + if (rc.IsFailure()) return rc.Miss(); - return _baseStorage.Write(_offset + offset, source); + rc = _baseStorage.Write(_offset + offset, source); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public readonly Result Flush() { if (!IsValid()) return ResultFs.NotInitialized.Log(); - return _baseStorage.Flush(); + Result rc = _baseStorage.Flush(); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } public Result SetSize(long size) { if (!IsValid()) return ResultFs.NotInitialized.Log(); if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log(); - if (!IStorage.CheckOffsetAndSize(_offset, size)) return ResultFs.InvalidSize.Log(); - Result rc = _baseStorage.GetSize(out long currentSize); + Result rc = IStorage.CheckOffsetAndSize(_offset, size); + if (rc.IsFailure()) return rc; + + rc = _baseStorage.GetSize(out long currentSize); if (rc.IsFailure()) return rc; if (currentSize != _offset + _size) @@ -181,7 +192,9 @@ public struct ValueSubStorage : IDisposable if (operationId != OperationId.InvalidateCache) { if (size == 0) return Result.Success; - if (!IStorage.CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log(); + + Result rc = IStorage.CheckOffsetAndSize(_offset, size); + if (rc.IsFailure()) return rc.Miss(); } return _baseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer); diff --git a/src/LibHac/FsSystem/AlignmentMatchingStorage.cs b/src/LibHac/FsSystem/AlignmentMatchingStorage.cs index a1aa15ff..bd2e239c 100644 --- a/src/LibHac/FsSystem/AlignmentMatchingStorage.cs +++ b/src/LibHac/FsSystem/AlignmentMatchingStorage.cs @@ -23,7 +23,7 @@ public interface IAlignmentMatchingStorageSize { } /// This class uses a work buffer on the stack to avoid allocations. Because of this the data alignment /// must be kept small; no larger than 0x200. The class /// should be used for data alignment sizes larger than this. -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) [SkipLocalsInit] public class AlignmentMatchingStorage : IStorage where TDataAlignment : struct, IAlignmentMatchingStorageSize @@ -80,8 +80,8 @@ public class AlignmentMatchingStorage : IStora Result rc = GetSize(out long totalSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, destination.Length, totalSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, destination.Length, totalSize); + if (rc.IsFailure()) return rc.Miss(); return AlignmentMatchingStorageImpl.Read(_baseStorage, workBuffer, DataAlign, BufferAlign, offset, destination); } @@ -96,8 +96,8 @@ public class AlignmentMatchingStorage : IStora Result rc = GetSize(out long totalSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, source.Length, totalSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, source.Length, totalSize); + if (rc.IsFailure()) return rc.Miss(); return AlignmentMatchingStorageImpl.Write(_baseStorage, workBuffer, DataAlign, BufferAlign, offset, source); } @@ -146,8 +146,8 @@ public class AlignmentMatchingStorage : IStora Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckOffsetAndSize(offset, size)) - return ResultFs.OutOfRange.Log(); + rc = CheckOffsetAndSize(offset, size); + if (rc.IsFailure()) return rc.Miss(); long validSize = Math.Min(size, baseStorageSize - offset); long alignedOffset = Alignment.AlignDownPow2(offset, DataAlign); @@ -166,7 +166,7 @@ public class AlignmentMatchingStorage : IStora /// the beginning or end of the requested range. For data alignment sizes of 0x200 or smaller /// should be used instead /// to avoid these allocations. -/// Based on FS 13.1.0 (nnSdk 13.4.0) +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class AlignmentMatchingStoragePooledBuffer : IStorage where TBufferAlignment : struct, IAlignmentMatchingStorageSize { @@ -220,8 +220,8 @@ public class AlignmentMatchingStoragePooledBuffer : IStorage Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, destination.Length, baseStorageSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, destination.Length, baseStorageSize); + if (rc.IsFailure()) return rc.Miss(); using var pooledBuffer = new PooledBuffer(); pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment); @@ -238,8 +238,8 @@ public class AlignmentMatchingStoragePooledBuffer : IStorage Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, source.Length, baseStorageSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, source.Length, baseStorageSize); + if (rc.IsFailure()) return rc.Miss(); using var pooledBuffer = new PooledBuffer(); pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment); @@ -292,8 +292,8 @@ public class AlignmentMatchingStoragePooledBuffer : IStorage Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckOffsetAndSize(offset, size)) - return ResultFs.OutOfRange.Log(); + rc = CheckOffsetAndSize(offset, size); + if (rc.IsFailure()) return rc.Miss(); long validSize = Math.Min(size, baseStorageSize - offset); long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment); @@ -362,8 +362,8 @@ public class AlignmentMatchingStorageInBulkRead : IStorage Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, destination.Length, baseStorageSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, destination.Length, baseStorageSize); + if (rc.IsFailure()) return rc.Miss(); // Calculate the aligned offsets of the requested region. long offsetEnd = offset + destination.Length; @@ -453,8 +453,8 @@ public class AlignmentMatchingStorageInBulkRead : IStorage Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckAccessRange(offset, source.Length, baseStorageSize)) - return ResultFs.OutOfRange.Log(); + rc = CheckAccessRange(offset, source.Length, baseStorageSize); + if (rc.IsFailure()) return rc.Miss(); using var pooledBuffer = new PooledBuffer((int)_dataAlignment, (int)_dataAlignment); @@ -505,8 +505,8 @@ public class AlignmentMatchingStorageInBulkRead : IStorage Result rc = GetSize(out long baseStorageSize); if (rc.IsFailure()) return rc.Miss(); - if (!CheckOffsetAndSize(offset, size)) - return ResultFs.OutOfRange.Log(); + rc = CheckOffsetAndSize(offset, size); + if (rc.IsFailure()) return rc.Miss(); long validSize = Math.Min(size, baseStorageSize - offset); long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment); diff --git a/src/LibHac/Tools/FsSystem/AesCbcStorage.cs b/src/LibHac/Tools/FsSystem/AesCbcStorage.cs index 8045d2f6..48fbd552 100644 --- a/src/LibHac/Tools/FsSystem/AesCbcStorage.cs +++ b/src/LibHac/Tools/FsSystem/AesCbcStorage.cs @@ -28,10 +28,10 @@ public class AesCbcStorage : SectorStorage public override Result Read(long offset, Span destination) { - if (!CheckAccessRange(offset, destination.Length, _size)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, _size); + if (rc.IsFailure()) return rc.Miss(); - Result rc = base.Read(offset, destination); + rc = base.Read(offset, destination); if (rc.IsFailure()) return rc; rc = GetDecryptor(out ICipher cipher, offset); diff --git a/src/LibHac/Tools/FsSystem/CachedStorage.cs b/src/LibHac/Tools/FsSystem/CachedStorage.cs index 89de720d..cf5f140c 100644 --- a/src/LibHac/Tools/FsSystem/CachedStorage.cs +++ b/src/LibHac/Tools/FsSystem/CachedStorage.cs @@ -39,8 +39,8 @@ public class CachedStorage : IStorage long inOffset = offset; int outOffset = 0; - if (!CheckAccessRange(offset, destination.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, Length); + if (rc.IsFailure()) return rc.Miss(); lock (Blocks) { @@ -69,8 +69,8 @@ public class CachedStorage : IStorage long inOffset = offset; int outOffset = 0; - if (!CheckAccessRange(offset, source.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, source.Length, Length); + if (rc.IsFailure()) return rc.Miss(); lock (Blocks) { diff --git a/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs b/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs index cbb8102d..10d722fc 100644 --- a/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs +++ b/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs @@ -34,8 +34,8 @@ public class ConcatenationStorage : IStorage int outPos = 0; int remaining = destination.Length; - if (!CheckAccessRange(offset, destination.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, Length); + if (rc.IsFailure()) return rc.Miss(); int sourceIndex = FindSource(inPos); @@ -47,7 +47,7 @@ public class ConcatenationStorage : IStorage int bytesToRead = (int)Math.Min(entryRemain, remaining); - Result rc = entry.Storage.Read(entryPos, destination.Slice(outPos, bytesToRead)); + rc = entry.Storage.Read(entryPos, destination.Slice(outPos, bytesToRead)); if (rc.IsFailure()) return rc; outPos += bytesToRead; @@ -65,8 +65,8 @@ public class ConcatenationStorage : IStorage int outPos = 0; int remaining = source.Length; - if (!CheckAccessRange(offset, source.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, source.Length, Length); + if (rc.IsFailure()) return rc.Miss(); int sourceIndex = FindSource(inPos); @@ -78,7 +78,7 @@ public class ConcatenationStorage : IStorage int bytesToWrite = (int)Math.Min(entryRemain, remaining); - Result rc = entry.Storage.Write(entryPos, source.Slice(outPos, bytesToWrite)); + rc = entry.Storage.Write(entryPos, source.Slice(outPos, bytesToWrite)); if (rc.IsFailure()) return rc; outPos += bytesToWrite; diff --git a/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs b/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs index af0e4d32..6b535543 100644 --- a/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs @@ -33,8 +33,8 @@ public class DuplexStorage : IStorage int outPos = 0; int remaining = destination.Length; - if (!CheckAccessRange(offset, destination.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, Length); + if (rc.IsFailure()) return rc.Miss(); while (remaining > 0) { @@ -45,7 +45,7 @@ public class DuplexStorage : IStorage IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA; - Result rc = data.Read(inPos, destination.Slice(outPos, bytesToRead)); + rc = data.Read(inPos, destination.Slice(outPos, bytesToRead)); if (rc.IsFailure()) return rc; outPos += bytesToRead; @@ -62,8 +62,8 @@ public class DuplexStorage : IStorage int outPos = 0; int remaining = source.Length; - if (!CheckAccessRange(offset, source.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, source.Length, Length); + if (rc.IsFailure()) return rc.Miss(); while (remaining > 0) { @@ -74,7 +74,7 @@ public class DuplexStorage : IStorage IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA; - Result rc = data.Write(inPos, source.Slice(outPos, bytesToWrite)); + rc = data.Write(inPos, source.Slice(outPos, bytesToWrite)); if (rc.IsFailure()) return rc; outPos += bytesToWrite; diff --git a/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs b/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs index 7d681963..4e3774a9 100644 --- a/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs @@ -40,8 +40,8 @@ public class JournalStorage : IStorage int outPos = 0; int remaining = destination.Length; - if (!CheckAccessRange(offset, destination.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, destination.Length, Length); + if (rc.IsFailure()) return rc.Miss(); while (remaining > 0) { @@ -52,7 +52,7 @@ public class JournalStorage : IStorage int bytesToRead = Math.Min(remaining, BlockSize - blockPos); - Result rc = BaseStorage.Read(physicalOffset, destination.Slice(outPos, bytesToRead)); + rc = BaseStorage.Read(physicalOffset, destination.Slice(outPos, bytesToRead)); if (rc.IsFailure()) return rc; outPos += bytesToRead; @@ -69,8 +69,8 @@ public class JournalStorage : IStorage int outPos = 0; int remaining = source.Length; - if (!CheckAccessRange(offset, source.Length, Length)) - return ResultFs.OutOfRange.Log(); + Result rc = CheckAccessRange(offset, source.Length, Length); + if (rc.IsFailure()) return rc.Miss(); while (remaining > 0) { @@ -81,7 +81,7 @@ public class JournalStorage : IStorage int bytesToWrite = Math.Min(remaining, BlockSize - blockPos); - Result rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite)); + rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite)); if (rc.IsFailure()) return rc; outPos += bytesToWrite; diff --git a/src/LibHac/Util/IntUtil.cs b/src/LibHac/Util/IntUtil.cs new file mode 100644 index 00000000..c0582580 --- /dev/null +++ b/src/LibHac/Util/IntUtil.cs @@ -0,0 +1,37 @@ +namespace LibHac.Util; + +public static class IntUtil +{ + // Todo: Use generic math once C# 11 is out + public static bool IsIntValueRepresentableAsLong(ulong value) + { + return value <= long.MaxValue; + } + + public static bool IsIntValueRepresentableAsULong(long value) + { + return value >= 0; + } + + public static bool IsIntValueRepresentableAsUInt(long value) + { + return value >= uint.MinValue && value <= uint.MaxValue; + } + + public static bool CanAddWithoutOverflow(long x, long y) + { + if (y >= 0) + { + return x <= long.MaxValue - y; + } + else + { + return x >= unchecked(long.MinValue - y); + } + } + + public static bool CanAddWithoutOverflow(ulong x, ulong y) + { + return x <= ulong.MaxValue - y; + } +} \ No newline at end of file