Add BaseFileSystemService

This commit is contained in:
Alex Barney 2020-09-05 16:47:02 -07:00
parent 122e83defe
commit be55aa7e9c
13 changed files with 388 additions and 20 deletions

View file

@ -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,

Can't render this file because it has a wrong number of fields in line 203.

View file

@ -305,6 +305,8 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-5000; Range: 5000-5999; Inner value: 0x271002</summary>
public static Result.Base Unexpected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 5000, 5999); }
/// <summary>Error code: 2002-5121; Inner value: 0x280202</summary>
public static Result.Base UnexpectedFatFileSystemSectorCount => new Result.Base(ModuleFs, 5121);
/// <summary>Error code: 2002-5307; Inner value: 0x297602</summary>
public static Result.Base UnexpectedErrorInHostFileFlush => new Result.Base(ModuleFs, 5307);
/// <summary>Error code: 2002-5308; Inner value: 0x297802</summary>

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,23 @@
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public interface ISdCardProxyFileSystemCreator
{
Result Create(out IFileSystem fileSystem, bool isCaseSensitive);
/// <summary>
/// Formats the SD card.
/// </summary>
/// <param name="removeFromFatFsCache">Should the SD card file system be removed from the
/// FAT file system cache?</param>
/// <returns>The <see cref="Result"/> of the operation.</returns>
Result Format(bool removeFromFatFsCache);
/// <summary>
/// Automatically closes all open proxy file system entries and formats the SD card.
/// </summary>
/// <returns>The <see cref="Result"/> of the operation.</returns>
Result Format();
}
}

View file

@ -1,10 +0,0 @@
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public interface ISdFileSystemCreator
{
Result Create(out IFileSystem fileSystem);
Result Format(bool closeOpenEntries);
}
}

View file

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

View file

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

View file

@ -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.
/// <br/>Based on FS 10.0.0 (nnSdk 10.4.0)</remarks>
internal class ProgramRegistryImpl : IProgramRegistry
public class ProgramRegistryImpl : IProgramRegistry
{
private ulong _processId;

View file

@ -0,0 +1,8 @@
namespace LibHac.FsSrv.Sf
{
public interface IWiper
{
public Result Startup(out long spaceToWipe);
public Result Process(out long remainingSpaceToWipe);
}
}

View file

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