From 955fff8efc41293fd20dfe8e40318a3dc5eae839 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 25 Sep 2019 17:16:59 -0500 Subject: [PATCH] Add FileHandleStorage and a new SubStorage class --- src/LibHac/Fs/FileHandleStorage.cs | 98 +++++++++++++++++++++++++ src/LibHac/Fs/FileSystemClient.File.cs | 2 +- src/LibHac/Fs/FileSystemManager.cs | 10 +-- src/LibHac/Fs/FileSystemManagerUtils.cs | 2 +- src/LibHac/Fs/StorageBase.cs | 20 +++++ src/LibHac/Fs/SubStorage2.cs | 85 +++++++++++++++++++++ src/LibHac/FsSystem/AesXtsFile.cs | 1 + src/LibHac/FsSystem/AesXtsFileSystem.cs | 6 +- 8 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 src/LibHac/Fs/FileHandleStorage.cs create mode 100644 src/LibHac/Fs/StorageBase.cs create mode 100644 src/LibHac/Fs/SubStorage2.cs diff --git a/src/LibHac/Fs/FileHandleStorage.cs b/src/LibHac/Fs/FileHandleStorage.cs new file mode 100644 index 00000000..04052833 --- /dev/null +++ b/src/LibHac/Fs/FileHandleStorage.cs @@ -0,0 +1,98 @@ +using System; + +namespace LibHac.Fs +{ + public class FileHandleStorage : StorageBase + { + private const long InvalidSize = -1; + private readonly object _locker = new object(); + + private FileSystemManager FsManager { get; } + private FileHandle Handle { get; } + private long FileSize { get; set; } = InvalidSize; + private bool CloseHandle { get; } + + public FileHandleStorage(FileHandle handle) : this(handle, false) { } + + public FileHandleStorage(FileHandle handle, bool closeHandleOnDispose) + { + Handle = handle; + CloseHandle = closeHandleOnDispose; + FsManager = Handle.File.Parent.FsManager; + } + + public override Result Read(long offset, Span destination) + { + lock (_locker) + { + if (destination.Length == 0) return Result.Success; + + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc; + + if (destination.Length < 0 || offset < 0) return ResultFs.ValueOutOfRange.Log(); + if (!IsRangeValid(offset, destination.Length, FileSize)) return ResultFs.ValueOutOfRange.Log(); + + return FsManager.ReadFile(Handle, offset, destination); + } + } + + public override Result Write(long offset, ReadOnlySpan source) + { + lock (_locker) + { + if (source.Length == 0) return Result.Success; + + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc; + + if (source.Length < 0 || offset < 0) return ResultFs.ValueOutOfRange.Log(); + if (!IsRangeValid(offset, source.Length, FileSize)) return ResultFs.ValueOutOfRange.Log(); + + return FsManager.WriteFile(Handle, offset, source); + } + } + + public override Result Flush() + { + return FsManager.FlushFile(Handle); + } + + public override Result SetSize(long size) + { + FileSize = InvalidSize; + + return FsManager.SetFileSize(Handle, size); + } + + public override Result GetSize(out long size) + { + size = default; + + Result rc = UpdateSize(); + if (rc.IsFailure()) return rc; + + size = FileSize; + return Result.Success; + } + + private Result UpdateSize() + { + if (FileSize != InvalidSize) return Result.Success; + + Result rc = FsManager.GetFileSize(out long fileSize, Handle); + if (rc.IsFailure()) return rc; + + FileSize = fileSize; + return Result.Success; + } + + public override void Dispose() + { + if (CloseHandle) + { + FsManager.CloseFile(Handle); + } + } + } +} diff --git a/src/LibHac/Fs/FileSystemClient.File.cs b/src/LibHac/Fs/FileSystemClient.File.cs index fa4bbbc0..83cd3760 100644 --- a/src/LibHac/Fs/FileSystemClient.File.cs +++ b/src/LibHac/Fs/FileSystemClient.File.cs @@ -26,7 +26,7 @@ namespace LibHac.Fs public Result WriteFile(FileHandle handle, long offset, ReadOnlySpan source, WriteOption options) { - return FsManager.WriteFile(handle, source, offset, options); + return FsManager.WriteFile(handle, offset, source, options); } public Result FlushFile(FileHandle handle) diff --git a/src/LibHac/Fs/FileSystemManager.cs b/src/LibHac/Fs/FileSystemManager.cs index 29e33b55..0a685f34 100644 --- a/src/LibHac/Fs/FileSystemManager.cs +++ b/src/LibHac/Fs/FileSystemManager.cs @@ -433,12 +433,12 @@ namespace LibHac.Fs return rc; } - public Result WriteFile(FileHandle handle, ReadOnlySpan source, long offset) + public Result WriteFile(FileHandle handle, long offset, ReadOnlySpan source) { - return WriteFile(handle, source, offset, WriteOption.None); + return WriteFile(handle, offset, source, WriteOption.None); } - public Result WriteFile(FileHandle handle, ReadOnlySpan source, long offset, WriteOption option) + public Result WriteFile(FileHandle handle, long offset, ReadOnlySpan source, WriteOption option) { Result rc; @@ -607,9 +607,9 @@ namespace LibHac.Fs mountName = path.Slice(0, mountLen); - if (mountLen + 2 < path.Length) + if (mountLen + 1 < path.Length) { - subPath = path.Slice(mountLen + 2); + subPath = path.Slice(mountLen + 1); } else { diff --git a/src/LibHac/Fs/FileSystemManagerUtils.cs b/src/LibHac/Fs/FileSystemManagerUtils.cs index 4aa01e30..be795316 100644 --- a/src/LibHac/Fs/FileSystemManagerUtils.cs +++ b/src/LibHac/Fs/FileSystemManagerUtils.cs @@ -76,7 +76,7 @@ namespace LibHac.Fs rc = fs.ReadFile(out long _, sourceHandle, offset, buf); if (rc.IsFailure()) return rc; - rc = fs.WriteFile(destHandle, buf, offset); + rc = fs.WriteFile(destHandle, offset, buf); if (rc.IsFailure()) return rc; logger?.ReportAdd(toRead); diff --git a/src/LibHac/Fs/StorageBase.cs b/src/LibHac/Fs/StorageBase.cs new file mode 100644 index 00000000..732b611f --- /dev/null +++ b/src/LibHac/Fs/StorageBase.cs @@ -0,0 +1,20 @@ +using System; + +namespace LibHac.Fs +{ + public abstract class StorageBase : IStorage + { + public abstract Result Read(long offset, Span destination); + public abstract Result Write(long offset, ReadOnlySpan source); + public abstract Result Flush(); + public abstract Result SetSize(long size); + public abstract Result GetSize(out long size); + + public virtual void Dispose() { } + + public static bool IsRangeValid(long offset, long size, long totalSize) + { + return size <= totalSize && offset <= totalSize - size; + } + } +} diff --git a/src/LibHac/Fs/SubStorage2.cs b/src/LibHac/Fs/SubStorage2.cs new file mode 100644 index 00000000..e919a04e --- /dev/null +++ b/src/LibHac/Fs/SubStorage2.cs @@ -0,0 +1,85 @@ +using System; + +namespace LibHac.Fs +{ + public class SubStorage2 : StorageBase + { + private IStorage BaseStorage { get; } + private long Offset { get; } + private long Size { get; set; } + public bool IsResizable { get; set; } + + public SubStorage2(IStorage baseStorage, long offset, long size) + { + BaseStorage = baseStorage; + Offset = offset; + Size = size; + } + + public SubStorage2(SubStorage2 baseStorage, long offset, long size) + { + BaseStorage = baseStorage.BaseStorage; + Offset = baseStorage.Offset + offset; + Size = size; + } + + public override Result Read(long offset, Span destination) + { + if (BaseStorage == null) return ResultFs.Result6902.Log(); + if (destination.Length == 0) return Result.Success; + if (Size < 0 || offset < 0) return ResultFs.ValueOutOfRange.Log(); + if (!IsRangeValid(offset, destination.Length, Size)) return ResultFs.ValueOutOfRange.Log(); + + return BaseStorage.Read(Offset + offset, destination); + } + + public override Result Write(long offset, ReadOnlySpan source) + { + if (BaseStorage == null) return ResultFs.Result6902.Log(); + if (source.Length == 0) return Result.Success; + if (Size < 0 || offset < 0) return ResultFs.ValueOutOfRange.Log(); + if (!IsRangeValid(offset, source.Length, Size)) return ResultFs.ValueOutOfRange.Log(); + + return BaseStorage.Write(Offset + offset, source); + } + + public override Result Flush() + { + if (BaseStorage == null) return ResultFs.Result6902.Log(); + + return BaseStorage.Flush(); + } + + public override Result SetSize(long size) + { + if (BaseStorage == null) return ResultFs.Result6902.Log(); + if (!IsResizable) return ResultFs.SubStorageNotResizable.Log(); + if (size < 0 || Offset < 0) return ResultFs.ValueOutOfRange.Log(); + + Result rc = BaseStorage.GetSize(out long baseSize); + if (rc.IsFailure()) return rc; + + if (baseSize != Offset + Size) + { + // SubStorage cannot be resized unless it is located at the end of the base storage. + return ResultFs.SubStorageNotResizableMiddleOfFile.Log(); + } + + rc = BaseStorage.SetSize(Offset + size); + if (rc.IsFailure()) return rc; + + Size = size; + return Result.Success; + } + + public override Result GetSize(out long size) + { + size = default; + + if (BaseStorage == null) return ResultFs.Result6902.Log(); + + size = Size; + return Result.Success; + } + } +} diff --git a/src/LibHac/FsSystem/AesXtsFile.cs b/src/LibHac/FsSystem/AesXtsFile.cs index 00fb3403..9455126a 100644 --- a/src/LibHac/FsSystem/AesXtsFile.cs +++ b/src/LibHac/FsSystem/AesXtsFile.cs @@ -63,6 +63,7 @@ namespace LibHac.FsSystem rc = BaseStorage.Read(offset, destination.Slice(0, (int)toRead)); if (rc.IsFailure()) return rc; + bytesRead = toRead; return Result.Success; } diff --git a/src/LibHac/FsSystem/AesXtsFileSystem.cs b/src/LibHac/FsSystem/AesXtsFileSystem.cs index ccfca772..118200bb 100644 --- a/src/LibHac/FsSystem/AesXtsFileSystem.cs +++ b/src/LibHac/FsSystem/AesXtsFileSystem.cs @@ -49,11 +49,13 @@ namespace LibHac.FsSystem public Result CreateFile(string path, long size, CreateFileOptions options, byte[] key) { long containerSize = AesXtsFile.HeaderLength + Util.AlignUp(size, 0x10); - BaseFileSystem.CreateFile(path, containerSize, options); + + Result rc = BaseFileSystem.CreateFile(path, containerSize, options); + if (rc.IsFailure()) return rc; var header = new AesXtsFileHeader(key, size, path, KekSource, ValidationKey); - Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, OpenMode.Write); + rc = BaseFileSystem.OpenFile(out IFile baseFile, path, OpenMode.Write); if (rc.IsFailure()) return rc; using (baseFile)