From 6a449b7da18afc6abea26f2ad3be75a5eb1139be Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 21 Sep 2019 23:38:47 -0500 Subject: [PATCH] Remove IFile.Mode and create a new FileBase class --- src/LibHac/Fs/Accessors/FileAccessor.cs | 5 - src/LibHac/Fs/FileBase2.cs | 131 ++++++++++++++++++ src/LibHac/Fs/FileSystemClient.File.cs | 1 - src/LibHac/Fs/FileSystemClient.FileSystem.cs | 2 - src/LibHac/Fs/FsEnums.cs | 29 +++- src/LibHac/Fs/IFile.cs | 6 - .../Creators/ISaveDataFileSystemCreator.cs | 1 - src/LibHac/FsSystem/DirectorySaveDataFile.cs | 30 ++-- .../FsSystem/DirectorySaveDataFileSystem.cs | 2 +- src/LibHac/FsSystem/FileBase.cs | 25 ---- src/LibHac/FsSystem/FileSystemExtensions.cs | 2 +- src/LibHac/FsSystem/LocalFile.cs | 36 +++-- src/LibHac/FsSystem/NxFileStream.cs | 10 +- src/LibHac/FsSystem/Save/SaveDataFile.cs | 1 + src/LibHac/Result.cs | 7 + 15 files changed, 211 insertions(+), 77 deletions(-) create mode 100644 src/LibHac/Fs/FileBase2.cs diff --git a/src/LibHac/Fs/Accessors/FileAccessor.cs b/src/LibHac/Fs/Accessors/FileAccessor.cs index d6ef410f..da052d71 100644 --- a/src/LibHac/Fs/Accessors/FileAccessor.cs +++ b/src/LibHac/Fs/Accessors/FileAccessor.cs @@ -1,5 +1,4 @@ using System; -using LibHac.FsSystem; namespace LibHac.Fs.Accessors { @@ -11,10 +10,6 @@ namespace LibHac.Fs.Accessors public WriteState WriteState { get; private set; } public OpenMode OpenMode { get; } - // Todo: Consider removing Mode from interface because OpenMode is in FileAccessor - // Todo: Set WriteState to Error based on returned results - OpenMode IFile.Mode => OpenMode; - public FileAccessor(IFile baseFile, FileSystemAccessor parent, OpenMode mode) { File = baseFile; diff --git a/src/LibHac/Fs/FileBase2.cs b/src/LibHac/Fs/FileBase2.cs new file mode 100644 index 00000000..07cfed1a --- /dev/null +++ b/src/LibHac/Fs/FileBase2.cs @@ -0,0 +1,131 @@ +using System; +using System.Threading; + +namespace LibHac.Fs +{ + public abstract class FileBase2 : IFile + { + // 0 = not disposed; 1 = disposed + private int _disposedState; + private bool IsDisposed => _disposedState != 0; + + public abstract Result ReadImpl(out long bytesRead, long offset, Span destination, ReadOption options); + public abstract Result WriteImpl(long offset, ReadOnlySpan source, WriteOption options); + public abstract Result FlushImpl(); + public abstract Result GetSizeImpl(out long size); + public abstract Result SetSizeImpl(long size); + + public Result Read(out long bytesRead, long offset, Span destination, ReadOption options) + { + bytesRead = default; + + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + if (destination.Length == 0) return Result.Success; + if (offset < 0) return ResultFs.ValueOutOfRange.Log(); + + return ReadImpl(out bytesRead, offset, destination, options); + } + + public Result Write(long offset, ReadOnlySpan source, WriteOption options) + { + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + if (source.Length == 0) + { + if (options.HasFlag(WriteOption.Flush)) + { + return Flush(); + } + + return Result.Success; + } + + if (offset < 0) return ResultFs.ValueOutOfRange.Log(); + + return WriteImpl(offset, source, options); + } + + public Result Flush() + { + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + return FlushImpl(); + } + + public Result GetSize(out long size) + { + size = 0; + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + return GetSizeImpl(out size); + } + + public Result SetSize(long size) + { + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + if (size < 0) return ResultFs.ValueOutOfRange.Log(); + + return SetSizeImpl(size); + } + + public void Dispose() + { + // Make sure Dispose is only called once + if (Interlocked.CompareExchange(ref _disposedState, 1, 0) == 0) + { + Dispose(true); + GC.SuppressFinalize(this); + } + } + + protected virtual void Dispose(bool disposing) { } + + protected Result ValidateReadParams(out long bytesToRead, long offset, int size, OpenMode openMode) + { + bytesToRead = default; + + if (!openMode.HasFlag(OpenMode.Read)) + { + return ResultFs.InvalidOpenModeForRead.Log(); + } + + Result rc = GetSize(out long fileSize); + if (rc.IsFailure()) return rc; + + if (offset > fileSize) + { + return ResultFs.ValueOutOfRange.Log(); + } + + bytesToRead = Math.Min(fileSize - offset, size); + + return Result.Success; + } + + protected Result ValidateWriteParams(long offset, int size, OpenMode openMode, out bool isResizeNeeded) + { + isResizeNeeded = false; + + if (!openMode.HasFlag(OpenMode.Write)) + { + return ResultFs.InvalidOpenModeForWrite.Log(); + } + + Result rc = GetSize(out long fileSize); + if (rc.IsFailure()) return rc; + + if (offset + size > fileSize) + { + isResizeNeeded = true; + + if (!openMode.HasFlag(OpenMode.AllowAppend)) + { + return ResultFs.AllowAppendRequiredForImplicitExtension.Log(); + } + } + + return Result.Success; + } + } +} diff --git a/src/LibHac/Fs/FileSystemClient.File.cs b/src/LibHac/Fs/FileSystemClient.File.cs index 82ae189b..fa4bbbc0 100644 --- a/src/LibHac/Fs/FileSystemClient.File.cs +++ b/src/LibHac/Fs/FileSystemClient.File.cs @@ -1,5 +1,4 @@ using System; -using LibHac.FsSystem; namespace LibHac.Fs { diff --git a/src/LibHac/Fs/FileSystemClient.FileSystem.cs b/src/LibHac/Fs/FileSystemClient.FileSystem.cs index e701a656..2f5707f1 100644 --- a/src/LibHac/Fs/FileSystemClient.FileSystem.cs +++ b/src/LibHac/Fs/FileSystemClient.FileSystem.cs @@ -1,5 +1,3 @@ -using LibHac.FsSystem; - namespace LibHac.Fs { public partial class FileSystemClient diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index 223f36fb..08758ff7 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -1,4 +1,6 @@ -namespace LibHac.Fs +using System; + +namespace LibHac.Fs { public enum BisPartitionId { @@ -92,4 +94,29 @@ Nand = 0, SdCard = 1 } + + /// + /// Specifies which operations are available on an . + /// + [Flags] + public enum OpenMode + { + Read = 1, + Write = 2, + AllowAppend = 4, + ReadWrite = Read | Write + } + + [Flags] + public enum ReadOption + { + None = 0 + } + + [Flags] + public enum WriteOption + { + None = 0, + Flush = 1 + } } diff --git a/src/LibHac/Fs/IFile.cs b/src/LibHac/Fs/IFile.cs index bec15141..1c98e082 100644 --- a/src/LibHac/Fs/IFile.cs +++ b/src/LibHac/Fs/IFile.cs @@ -1,5 +1,4 @@ using System; -using LibHac.FsSystem; namespace LibHac.Fs { @@ -19,11 +18,6 @@ namespace LibHac.Fs /// the file will be expanded so that it is large enough to contain the written data. public interface IFile : IDisposable { - /// - /// The permissions mode for the current file. - /// - OpenMode Mode { get; } - /// /// Reads a sequence of bytes from the current . /// diff --git a/src/LibHac/FsService/Creators/ISaveDataFileSystemCreator.cs b/src/LibHac/FsService/Creators/ISaveDataFileSystemCreator.cs index 29f45fb5..dd62d5ee 100644 --- a/src/LibHac/FsService/Creators/ISaveDataFileSystemCreator.cs +++ b/src/LibHac/FsService/Creators/ISaveDataFileSystemCreator.cs @@ -1,7 +1,6 @@ using System; using LibHac.Common; using LibHac.Fs; -using LibHac.FsSystem; using LibHac.FsSystem.Save; namespace LibHac.FsService.Creators diff --git a/src/LibHac/FsSystem/DirectorySaveDataFile.cs b/src/LibHac/FsSystem/DirectorySaveDataFile.cs index 09c61ba9..1a420ae3 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFile.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFile.cs @@ -3,57 +3,49 @@ using LibHac.Fs; namespace LibHac.FsSystem { - public class DirectorySaveDataFile : FileBase + public class DirectorySaveDataFile : FileBase2 { private IFile BaseFile { get; } private DirectorySaveDataFileSystem ParentFs { get; } - private object DisposeLocker { get; } = new object(); + private OpenMode Mode { get; } - public DirectorySaveDataFile(DirectorySaveDataFileSystem parentFs, IFile baseFile) + public DirectorySaveDataFile(DirectorySaveDataFileSystem parentFs, IFile baseFile, OpenMode mode) { ParentFs = parentFs; BaseFile = baseFile; - Mode = BaseFile.Mode; - ToDispose.Add(BaseFile); + Mode = mode; } - public override Result Read(out long bytesRead, long offset, Span destination, ReadOption options) + public override Result ReadImpl(out long bytesRead, long offset, Span destination, ReadOption options) { return BaseFile.Read(out bytesRead, offset, destination, options); } - public override Result Write(long offset, ReadOnlySpan source, WriteOption options) + public override Result WriteImpl(long offset, ReadOnlySpan source, WriteOption options) { return BaseFile.Write(offset, source, options); } - public override Result Flush() + public override Result FlushImpl() { return BaseFile.Flush(); } - public override Result GetSize(out long size) + public override Result GetSizeImpl(out long size) { return BaseFile.GetSize(out size); } - public override Result SetSize(long size) + public override Result SetSizeImpl(long size) { return BaseFile.SetSize(size); } protected override void Dispose(bool disposing) { - lock (DisposeLocker) + if (Mode.HasFlag(OpenMode.Write)) { - if (IsDisposed) return; - - base.Dispose(disposing); - - if (Mode.HasFlag(OpenMode.Write)) - { - ParentFs.NotifyCloseWritableFile(); - } + ParentFs.NotifyCloseWritableFile(); } } } diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index cfa98fbe..361f6ce7 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -114,7 +114,7 @@ namespace LibHac.FsSystem Result rc = BaseFs.OpenFile(out IFile baseFile, fullPath, mode); if (rc.IsFailure()) return rc; - file = new DirectorySaveDataFile(this, baseFile); + file = new DirectorySaveDataFile(this, baseFile, mode); if (mode.HasFlag(OpenMode.Write)) { diff --git a/src/LibHac/FsSystem/FileBase.cs b/src/LibHac/FsSystem/FileBase.cs index 33b30489..4079ca85 100644 --- a/src/LibHac/FsSystem/FileBase.cs +++ b/src/LibHac/FsSystem/FileBase.cs @@ -83,29 +83,4 @@ namespace LibHac.FsSystem IsDisposed = true; } } - - /// - /// Specifies which operations are available on an . - /// - [Flags] - public enum OpenMode - { - Read = 1, - Write = 2, - AllowAppend = 4, - ReadWrite = Read | Write - } - - [Flags] - public enum ReadOption - { - None = 0 - } - - [Flags] - public enum WriteOption - { - None = 0, - Flush = 1 - } } diff --git a/src/LibHac/FsSystem/FileSystemExtensions.cs b/src/LibHac/FsSystem/FileSystemExtensions.cs index fd7ba09b..1c89dd2c 100644 --- a/src/LibHac/FsSystem/FileSystemExtensions.cs +++ b/src/LibHac/FsSystem/FileSystemExtensions.cs @@ -152,7 +152,7 @@ namespace LibHac.FsSystem public static IStorage AsStorage(this IFile file) => new FileStorage(file); public static Stream AsStream(this IFile file) => new NxFileStream(file, true); - public static Stream AsStream(this IFile file, bool keepOpen) => new NxFileStream(file, keepOpen); + public static Stream AsStream(this IFile file, OpenMode mode, bool keepOpen) => new NxFileStream(file, mode, keepOpen); public static IFile AsIFile(this Stream stream, OpenMode mode) => new StreamFile(stream, mode); diff --git a/src/LibHac/FsSystem/LocalFile.cs b/src/LibHac/FsSystem/LocalFile.cs index 92fc844b..370de75d 100644 --- a/src/LibHac/FsSystem/LocalFile.cs +++ b/src/LibHac/FsSystem/LocalFile.cs @@ -4,49 +4,51 @@ using LibHac.Fs; namespace LibHac.FsSystem { - public class LocalFile : FileBase + public class LocalFile : FileBase2 { private const int ErrorHandleDiskFull = unchecked((int)0x80070027); private const int ErrorDiskFull = unchecked((int)0x80070070); private FileStream Stream { get; } private StreamFile File { get; } + private OpenMode Mode { get; } public LocalFile(string path, OpenMode mode) { Mode = mode; Stream = OpenFile(path, mode); File = new StreamFile(Stream, mode); - - ToDispose.Add(File); - ToDispose.Add(Stream); } - public override Result Read(out long bytesRead, long offset, Span destination, ReadOption options) + public override Result ReadImpl(out long bytesRead, long offset, Span destination, ReadOption options) { - int toRead = ValidateReadParamsAndGetSize(destination, offset); + bytesRead = 0; - return File.Read(out bytesRead, offset, destination.Slice(0, toRead), options); + Result rc = ValidateReadParams(out long toRead, offset, destination.Length, Mode); + if (rc.IsFailure()) return rc; + + return File.Read(out bytesRead, offset, destination.Slice(0, (int)toRead), options); } - public override Result Write(long offset, ReadOnlySpan source, WriteOption options) + public override Result WriteImpl(long offset, ReadOnlySpan source, WriteOption options) { - ValidateWriteParams(source, offset); + Result rc = ValidateWriteParams(offset, source.Length, Mode, out _); + if (rc.IsFailure()) return rc; return File.Write(offset, source, options); } - public override Result Flush() + public override Result FlushImpl() { return File.Flush(); } - public override Result GetSize(out long size) + public override Result GetSizeImpl(out long size) { return File.GetSize(out size); } - public override Result SetSize(long size) + public override Result SetSizeImpl(long size) { try { @@ -60,6 +62,16 @@ namespace LibHac.FsSystem return Result.Success; } + protected override void Dispose(bool disposing) + { + if (disposing) + { + File?.Dispose(); + } + + Stream?.Dispose(); + } + private static FileAccess GetFileAccess(OpenMode mode) { // FileAccess and OpenMode have the same flags diff --git a/src/LibHac/FsSystem/NxFileStream.cs b/src/LibHac/FsSystem/NxFileStream.cs index 48b5d99f..00d8593e 100644 --- a/src/LibHac/FsSystem/NxFileStream.cs +++ b/src/LibHac/FsSystem/NxFileStream.cs @@ -8,11 +8,15 @@ namespace LibHac.FsSystem { private IFile BaseFile { get; } private bool LeaveOpen { get; } + private OpenMode Mode { get; } private long _length; - public NxFileStream(IFile baseFile, bool leaveOpen) + public NxFileStream(IFile baseFile, bool leaveOpen) : this(baseFile, OpenMode.ReadWrite, leaveOpen) { } + + public NxFileStream(IFile baseFile, OpenMode mode, bool leaveOpen) { BaseFile = baseFile; + Mode = mode; LeaveOpen = leaveOpen; baseFile.GetSize(out _length).ThrowIfFailure(); @@ -63,9 +67,9 @@ namespace LibHac.FsSystem BaseFile.GetSize(out _length).ThrowIfFailure(); } - public override bool CanRead => BaseFile.Mode.HasFlag(OpenMode.Read); + public override bool CanRead => Mode.HasFlag(OpenMode.Read); public override bool CanSeek => true; - public override bool CanWrite => BaseFile.Mode.HasFlag(OpenMode.Write); + public override bool CanWrite => Mode.HasFlag(OpenMode.Write); public override long Length => _length; public override long Position { get; set; } diff --git a/src/LibHac/FsSystem/Save/SaveDataFile.cs b/src/LibHac/FsSystem/Save/SaveDataFile.cs index 8dc2f3d8..56a56ec5 100644 --- a/src/LibHac/FsSystem/Save/SaveDataFile.cs +++ b/src/LibHac/FsSystem/Save/SaveDataFile.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Fs; namespace LibHac.FsSystem.Save { diff --git a/src/LibHac/Result.cs b/src/LibHac/Result.cs index 86e90ba1..33c2875d 100644 --- a/src/LibHac/Result.cs +++ b/src/LibHac/Result.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics; namespace LibHac { [Serializable] + [DebuggerDisplay("{ToString()}")] public struct Result : IEquatable { public readonly int Value; @@ -43,6 +45,11 @@ namespace LibHac return this; } + public override string ToString() + { + return IsSuccess() ? "Success" : ErrorCode; + } + public override bool Equals(object obj) { return obj is Result result && Equals(result);