From be55aa7e9c750a8654868fe1082c0c9107c9e5b2 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 5 Sep 2020 16:47:02 -0700 Subject: [PATCH] Add BaseFileSystemService --- build/CodeGen/results.csv | 1 + src/LibHac/Fs/ResultFs.cs | 2 + src/LibHac/FsSrv/BaseFileSystemService.cs | 205 ++++++++++++++++++ src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs | 113 ++++++++++ ....cs => EmulatedSdCardFileSystemCreator.cs} | 15 +- .../FsSrv/Creators/FileSystemCreators.cs | 2 +- .../Creators/ISdCardProxyFileSystemCreator.cs | 23 ++ .../FsSrv/Creators/ISdFileSystemCreator.cs | 10 - src/LibHac/FsSrv/DefaultFsServerObjects.cs | 2 +- src/LibHac/FsSrv/FileSystemProxyCore.cs | 4 +- src/LibHac/FsSrv/ProgramRegistryImpl.cs | 2 +- src/LibHac/FsSrv/Sf/IWiper.cs | 8 + src/LibHac/Sf/NativeHandle.cs | 21 ++ 13 files changed, 388 insertions(+), 20 deletions(-) create mode 100644 src/LibHac/FsSrv/BaseFileSystemService.cs create mode 100644 src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs rename src/LibHac/FsSrv/Creators/{EmulatedSdFileSystemCreator.cs => EmulatedSdCardFileSystemCreator.cs} (73%) create mode 100644 src/LibHac/FsSrv/Creators/ISdCardProxyFileSystemCreator.cs delete mode 100644 src/LibHac/FsSrv/Creators/ISdFileSystemCreator.cs create mode 100644 src/LibHac/FsSrv/Sf/IWiper.cs create mode 100644 src/LibHac/Sf/NativeHandle.cs diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index fa74307a..e9b91cf2 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -205,6 +205,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,4812,,IncompleteBlockInZeroBitmapHashStorageFile, 2,5000,5999,Unexpected, +2,5121,,UnexpectedFatFileSystemSectorCount, 2,5307,,UnexpectedErrorInHostFileFlush, 2,5308,,UnexpectedErrorInHostFileGetSize, 2,5309,,UnknownHostFileSystemError, diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index eaf025fb..d25479c8 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -305,6 +305,8 @@ namespace LibHac.Fs /// Error code: 2002-5000; Range: 5000-5999; Inner value: 0x271002 public static Result.Base Unexpected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 5000, 5999); } + /// Error code: 2002-5121; Inner value: 0x280202 + public static Result.Base UnexpectedFatFileSystemSectorCount => new Result.Base(ModuleFs, 5121); /// Error code: 2002-5307; Inner value: 0x297602 public static Result.Base UnexpectedErrorInHostFileFlush => new Result.Base(ModuleFs, 5307); /// Error code: 2002-5308; Inner value: 0x297802 diff --git a/src/LibHac/FsSrv/BaseFileSystemService.cs b/src/LibHac/FsSrv/BaseFileSystemService.cs new file mode 100644 index 00000000..41d083de --- /dev/null +++ b/src/LibHac/FsSrv/BaseFileSystemService.cs @@ -0,0 +1,205 @@ +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSrv.Impl; +using LibHac.FsSrv.Sf; +using LibHac.Sf; + +namespace LibHac.FsSrv +{ + public readonly struct BaseFileSystemService + { + private readonly BaseFileSystemServiceImpl _serviceImpl; + private readonly ulong _processId; + + public BaseFileSystemService(BaseFileSystemServiceImpl serviceImpl, ulong processId) + { + _serviceImpl = serviceImpl; + _processId = processId; + } + + public Result OpenBisFileSystem(out IFileSystem fileSystem, in FspPath rootPath, BisPartitionId partitionId) + { + fileSystem = default; + + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + // Get the permissions the caller needs + AccessibilityType requiredAccess = partitionId switch + { + BisPartitionId.CalibrationFile => AccessibilityType.MountBisCalibrationFile, + BisPartitionId.SafeMode => AccessibilityType.MountBisSafeMode, + BisPartitionId.User => AccessibilityType.MountBisUser, + BisPartitionId.System => AccessibilityType.MountBisSystem, + BisPartitionId.SystemProperPartition => AccessibilityType.MountBisSystemProperPartition, + _ => AccessibilityType.NotMount + }; + + // Reject opening invalid partitions + if (requiredAccess == AccessibilityType.NotMount) + return ResultFs.InvalidAlignment.Log(); + + // Verify the caller has the required permissions + Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(requiredAccess); + + if (!accessibility.CanRead || !accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + // Normalize the path + var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty); + if (normalizer.Result.IsFailure()) return normalizer.Result; + + rc = _serviceImpl.OpenBisFileSystem(out IFileSystem bisFs, normalizer.Path, partitionId); + if (rc.IsFailure()) return rc; + + fileSystem = bisFs; + return Result.Success; + } + + public Result CreatePaddingFile(long size) + { + // File size must be non-negative + if (size < 0) + return ResultFs.InvalidSize.Log(); + + // Caller must have the FillBis permission + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + if (!programInfo.AccessControl.CanCall(OperationType.FillBis)) + return ResultFs.PermissionDenied.Log(); + + return _serviceImpl.CreatePaddingFile(size); + } + + public Result DeleteAllPaddingFiles() + { + // Caller must have the FillBis permission + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + if (!programInfo.AccessControl.CanCall(OperationType.FillBis)) + return ResultFs.PermissionDenied.Log(); + + return _serviceImpl.DeleteAllPaddingFiles(); + } + + public Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle, + GameCardPartition partitionId) + { + fileSystem = default; + + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead) + return ResultFs.PermissionDenied.Log(); + + rc = _serviceImpl.OpenGameCardFileSystem(out IFileSystem gcFs, handle, partitionId); + if (rc.IsFailure()) return rc; + + fileSystem = gcFs; + return Result.Success; + } + + public Result OpenSdCardFileSystem(out IFileSystem fileSystem) + { + fileSystem = default; + + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSdCard); + + if (!accessibility.CanRead || !accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + rc = _serviceImpl.OpenSdCardProxyFileSystem(out IFileSystem sdCardFs); + if (rc.IsFailure()) return rc; + + fileSystem = sdCardFs; + return Result.Success; + } + + public Result FormatSdCardFileSystem() + { + // Caller must have the FormatSdCard permission + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + if (!programInfo.AccessControl.CanCall(OperationType.FormatSdCard)) + return ResultFs.PermissionDenied.Log(); + + return _serviceImpl.FormatSdCardProxyFileSystem(); + } + + public Result FormatSdCardDryRun() + { + // No permissions are needed to call this method + + return _serviceImpl.FormatSdCardProxyFileSystem(); + } + + public Result IsExFatSupported(out bool isSupported) + { + // No permissions are needed to call this method + + isSupported = _serviceImpl.IsExFatSupported(); + return Result.Success; + } + + public Result OpenImageDirectoryFileSystem(out IFileSystem fileSystem, ImageDirectoryId directoryId) + { + fileSystem = default; + + // Caller must have the MountImageAndVideoStorage permission + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + Accessibility accessibility = + programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountImageAndVideoStorage); + + if (!accessibility.CanRead || !accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + // Get the base file system ID + int id; + switch (directoryId) + { + case ImageDirectoryId.Nand: id = 0; break; + case ImageDirectoryId.SdCard: id = 1; break; + default: + return ResultFs.InvalidArgument.Log(); + } + + rc = _serviceImpl.OpenBaseFileSystem(out IFileSystem imageFs, id); + if (rc.IsFailure()) return rc; + + fileSystem = imageFs; + return Result.Success; + } + + public Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize) + { + bisWiper = default; + + // Caller must have the OpenBisWiper permission + Result rc = GetProgramInfo(out ProgramInfo programInfo); + if (rc.IsFailure()) return rc; + + if (!programInfo.AccessControl.CanCall(OperationType.OpenBisWiper)) + return ResultFs.PermissionDenied.Log(); + + rc = _serviceImpl.OpenBisWiper(out IWiper wiper, transferMemoryHandle, transferMemorySize); + if (rc.IsFailure()) return rc; + + bisWiper = wiper; + return Result.Success; + } + + private Result GetProgramInfo(out ProgramInfo programInfo) + { + return _serviceImpl.GetProgramInfo(out programInfo, _processId); + } + } +} diff --git a/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs b/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs new file mode 100644 index 00000000..1fa1f998 --- /dev/null +++ b/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs @@ -0,0 +1,113 @@ +using System; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSrv.Creators; +using LibHac.FsSrv.Impl; +using LibHac.FsSrv.Sf; +using LibHac.Sf; + +namespace LibHac.FsSrv +{ + public class BaseFileSystemServiceImpl + { + private Configuration _config; + + public delegate Result BisWiperCreator(out IWiper wiper, NativeHandle transferMemoryHandle, + ulong transferMemorySize); + + public BaseFileSystemServiceImpl(in Configuration configuration) + { + _config = configuration; + } + + public struct Configuration + { + public IBuiltInStorageFileSystemCreator BisFileSystemCreator; + public IGameCardFileSystemCreator GameCardFileSystemCreator; + public ISdCardProxyFileSystemCreator SdCardFileSystemCreator; + // CurrentTimeFunction + // FatFileSystemCacheManager + // AlbumDirectoryFileSystemManager + public BisWiperCreator BisWiperCreator; + + // Note: The program registry service is global as of FS 10.0.0 + public ProgramRegistryImpl ProgramRegistry; + } + + public Result OpenBaseFileSystem(out IFileSystem fileSystem, int fileSystemId) + { + throw new NotImplementedException(); + } + + public Result OpenBisFileSystem(out IFileSystem fileSystem, U8Span rootPath, BisPartitionId partitionId) + { + return _config.BisFileSystemCreator.Create(out fileSystem, rootPath.ToString(), partitionId); + } + + public Result CreatePaddingFile(long size) + { + throw new NotImplementedException(); + } + + public Result DeleteAllPaddingFiles() + { + throw new NotImplementedException(); + } + + public Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle, + GameCardPartition partitionId) + { + Result rc; + int tries = 0; + + do + { + rc = _config.GameCardFileSystemCreator.Create(out fileSystem, handle, partitionId); + + if (!ResultFs.DataCorrupted.Includes(rc)) + break; + + tries++; + } while (tries < 2); + + return rc; + } + + public Result OpenSdCardProxyFileSystem(out IFileSystem fileSystem) + { + return OpenSdCardProxyFileSystem(out fileSystem, false); + } + + public Result OpenSdCardProxyFileSystem(out IFileSystem fileSystem, bool isCaseSensitive) + { + return _config.SdCardFileSystemCreator.Create(out fileSystem, isCaseSensitive); + } + + public Result FormatSdCardProxyFileSystem() + { + return _config.SdCardFileSystemCreator.Format(); + } + + public Result FormatSdCardDryRun() + { + throw new NotImplementedException(); + } + + public bool IsExFatSupported() + { + // Returning false should probably be fine + return false; + } + + public Result OpenBisWiper(out IWiper wiper, NativeHandle transferMemoryHandle, ulong transferMemorySize) + { + return _config.BisWiperCreator(out wiper, transferMemoryHandle, transferMemorySize); + } + + internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId) + { + return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId); + } + } +} diff --git a/src/LibHac/FsSrv/Creators/EmulatedSdFileSystemCreator.cs b/src/LibHac/FsSrv/Creators/EmulatedSdCardFileSystemCreator.cs similarity index 73% rename from src/LibHac/FsSrv/Creators/EmulatedSdFileSystemCreator.cs rename to src/LibHac/FsSrv/Creators/EmulatedSdCardFileSystemCreator.cs index a861c57f..022ce7ae 100644 --- a/src/LibHac/FsSrv/Creators/EmulatedSdFileSystemCreator.cs +++ b/src/LibHac/FsSrv/Creators/EmulatedSdCardFileSystemCreator.cs @@ -4,7 +4,7 @@ using LibHac.Fs.Fsa; namespace LibHac.FsSrv.Creators { - public class EmulatedSdFileSystemCreator : ISdFileSystemCreator + public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator { private const string DefaultPath = "/sdcard"; @@ -14,20 +14,20 @@ namespace LibHac.FsSrv.Creators private IFileSystem SdCardFileSystem { get; set; } - public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem) + public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem) { SdCard = sdCard; RootFileSystem = rootFileSystem; } - public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path) + public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path) { SdCard = sdCard; RootFileSystem = rootFileSystem; Path = path; } - public Result Create(out IFileSystem fileSystem) + public Result Create(out IFileSystem fileSystem, bool isCaseSensitive) { fileSystem = default; @@ -61,7 +61,12 @@ namespace LibHac.FsSrv.Creators return Result.Success; } - public Result Format(bool closeOpenEntries) + public Result Format(bool removeFromFatFsCache) + { + throw new NotImplementedException(); + } + + public Result Format() { throw new NotImplementedException(); } diff --git a/src/LibHac/FsSrv/Creators/FileSystemCreators.cs b/src/LibHac/FsSrv/Creators/FileSystemCreators.cs index 1e15191f..22ece1f3 100644 --- a/src/LibHac/FsSrv/Creators/FileSystemCreators.cs +++ b/src/LibHac/FsSrv/Creators/FileSystemCreators.cs @@ -17,6 +17,6 @@ public IEncryptedFileSystemCreator EncryptedFileSystemCreator { get; set; } public IMemoryStorageCreator MemoryStorageCreator { get; set; } public IBuiltInStorageFileSystemCreator BuiltInStorageFileSystemCreator { get; set; } - public ISdFileSystemCreator SdFileSystemCreator { get; set; } + public ISdCardProxyFileSystemCreator SdCardFileSystemCreator { get; set; } } } diff --git a/src/LibHac/FsSrv/Creators/ISdCardProxyFileSystemCreator.cs b/src/LibHac/FsSrv/Creators/ISdCardProxyFileSystemCreator.cs new file mode 100644 index 00000000..6a8c978b --- /dev/null +++ b/src/LibHac/FsSrv/Creators/ISdCardProxyFileSystemCreator.cs @@ -0,0 +1,23 @@ +using LibHac.Fs.Fsa; + +namespace LibHac.FsSrv.Creators +{ + public interface ISdCardProxyFileSystemCreator + { + Result Create(out IFileSystem fileSystem, bool isCaseSensitive); + + /// + /// Formats the SD card. + /// + /// Should the SD card file system be removed from the + /// FAT file system cache? + /// The of the operation. + Result Format(bool removeFromFatFsCache); + + /// + /// Automatically closes all open proxy file system entries and formats the SD card. + /// + /// The of the operation. + Result Format(); + } +} diff --git a/src/LibHac/FsSrv/Creators/ISdFileSystemCreator.cs b/src/LibHac/FsSrv/Creators/ISdFileSystemCreator.cs deleted file mode 100644 index 08167cdc..00000000 --- a/src/LibHac/FsSrv/Creators/ISdFileSystemCreator.cs +++ /dev/null @@ -1,10 +0,0 @@ -using LibHac.Fs.Fsa; - -namespace LibHac.FsSrv.Creators -{ - public interface ISdFileSystemCreator - { - Result Create(out IFileSystem fileSystem); - Result Format(bool closeOpenEntries); - } -} diff --git a/src/LibHac/FsSrv/DefaultFsServerObjects.cs b/src/LibHac/FsSrv/DefaultFsServerObjects.cs index d9f0e7e0..d764f4f2 100644 --- a/src/LibHac/FsSrv/DefaultFsServerObjects.cs +++ b/src/LibHac/FsSrv/DefaultFsServerObjects.cs @@ -29,7 +29,7 @@ namespace LibHac.FsSrv creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard); creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet); creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem); - creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(sdCard, rootFileSystem); + creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdCard, rootFileSystem); var deviceOperator = new EmulatedDeviceOperator(gameCard, sdCard); diff --git a/src/LibHac/FsSrv/FileSystemProxyCore.cs b/src/LibHac/FsSrv/FileSystemProxyCore.cs index a216178b..6441ef3b 100644 --- a/src/LibHac/FsSrv/FileSystemProxyCore.cs +++ b/src/LibHac/FsSrv/FileSystemProxyCore.cs @@ -627,7 +627,7 @@ namespace LibHac.FsSrv public Result OpenSdCardFileSystem(out IFileSystem fileSystem) { - return FsCreators.SdFileSystemCreator.Create(out fileSystem); + return FsCreators.SdCardFileSystemCreator.Create(out fileSystem, false); } public Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId) @@ -707,7 +707,7 @@ namespace LibHac.FsSrv { case CustomStorageId.SdCard: { - Result rc = FsCreators.SdFileSystemCreator.Create(out IFileSystem sdFs); + Result rc = FsCreators.SdCardFileSystemCreator.Create(out IFileSystem sdFs, false); if (rc.IsFailure()) return rc; string customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.SdCard); diff --git a/src/LibHac/FsSrv/ProgramRegistryImpl.cs b/src/LibHac/FsSrv/ProgramRegistryImpl.cs index 7946b915..febbdc7f 100644 --- a/src/LibHac/FsSrv/ProgramRegistryImpl.cs +++ b/src/LibHac/FsSrv/ProgramRegistryImpl.cs @@ -14,7 +14,7 @@ namespace LibHac.FsSrv /// storage location and file system permissions. This allows FS to resolve the program ID and /// verify the permissions of any process calling it. ///
Based on FS 10.0.0 (nnSdk 10.4.0) - internal class ProgramRegistryImpl : IProgramRegistry + public class ProgramRegistryImpl : IProgramRegistry { private ulong _processId; diff --git a/src/LibHac/FsSrv/Sf/IWiper.cs b/src/LibHac/FsSrv/Sf/IWiper.cs new file mode 100644 index 00000000..08b550b4 --- /dev/null +++ b/src/LibHac/FsSrv/Sf/IWiper.cs @@ -0,0 +1,8 @@ +namespace LibHac.FsSrv.Sf +{ + public interface IWiper + { + public Result Startup(out long spaceToWipe); + public Result Process(out long remainingSpaceToWipe); + } +} diff --git a/src/LibHac/Sf/NativeHandle.cs b/src/LibHac/Sf/NativeHandle.cs new file mode 100644 index 00000000..fd2ed0ac --- /dev/null +++ b/src/LibHac/Sf/NativeHandle.cs @@ -0,0 +1,21 @@ +namespace LibHac.Sf +{ + // How should this be handled? Using a C# struct would be more accurate, but C# + // doesn't have copy constructors or any way to prevent a struct from being copied. + public class NativeHandle + { + public uint Handle { get; private set; } + public bool IsManaged { get; private set; } + + public NativeHandle(uint handle) + { + Handle = handle; + } + + public NativeHandle(uint handle, bool isManaged) + { + Handle = handle; + IsManaged = isManaged; + } + } +}