mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Implement BaseFileSystemService and some related classes
This commit is contained in:
parent
559b8c89f9
commit
e757dff9b9
14 changed files with 719 additions and 94 deletions
6
src/LibHac/Fat/FatFileSystem.cs
Normal file
6
src/LibHac/Fat/FatFileSystem.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace LibHac.Fat;
|
||||
|
||||
public class FatFileSystem
|
||||
{
|
||||
public static bool IsExFatSupported() => false;
|
||||
}
|
36
src/LibHac/Fat/FatFormatAttribute.cs
Normal file
36
src/LibHac/Fat/FatFormatAttribute.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
namespace LibHac.Fat;
|
||||
|
||||
public readonly struct FatFormatAttribute
|
||||
{
|
||||
public readonly ulong MinimumSectorCount;
|
||||
public readonly ulong MaximumSectorCount;
|
||||
public readonly uint HiddenSectorCount;
|
||||
public readonly short NumHeads;
|
||||
public readonly short SectorsPerTrack;
|
||||
public readonly short SectorsPerCluster;
|
||||
public readonly int FatTableEntrySizeBits;
|
||||
public readonly FatType FatType;
|
||||
public readonly uint Reserved;
|
||||
|
||||
public FatFormatAttribute(ulong minimumSectorCount, ulong maximumSectorCount, uint hiddenSectorCount,
|
||||
short numHeads, short sectorsPerTrack, short sectorsPerCluster, int fatTableEntrySizeBits, FatType fatType)
|
||||
{
|
||||
MinimumSectorCount = minimumSectorCount;
|
||||
MaximumSectorCount = maximumSectorCount;
|
||||
HiddenSectorCount = hiddenSectorCount;
|
||||
NumHeads = numHeads;
|
||||
SectorsPerTrack = sectorsPerTrack;
|
||||
SectorsPerCluster = sectorsPerCluster;
|
||||
FatTableEntrySizeBits = fatTableEntrySizeBits;
|
||||
FatType = fatType;
|
||||
Reserved = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum FatType
|
||||
{
|
||||
Fat12 = 0,
|
||||
Fat16 = 1,
|
||||
Fat32 = 2,
|
||||
FatEx = 3
|
||||
}
|
55
src/LibHac/Fat/Impl/FatFileSystemStorageAdapter.cs
Normal file
55
src/LibHac/Fat/Impl/FatFileSystemStorageAdapter.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.Fat.Impl;
|
||||
|
||||
public static class FatFileSystemStorageAdapter
|
||||
{
|
||||
private static readonly FatFormatAttribute[] FormatAttributes =
|
||||
[
|
||||
new FatFormatAttribute(minimumSectorCount: 0x1, maximumSectorCount: 0x1000, hiddenSectorCount: 0x10, numHeads: 0x2, sectorsPerTrack: 0x10, sectorsPerCluster: 0x10, fatTableEntrySizeBits: 0xC, FatType.Fat12),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x1000, maximumSectorCount: 0x4000, hiddenSectorCount: 0x10, numHeads: 0x2, sectorsPerTrack: 0x20, sectorsPerCluster: 0x10, fatTableEntrySizeBits: 0xC, FatType.Fat12),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x4000, maximumSectorCount: 0x8000, hiddenSectorCount: 0x20, numHeads: 0x2, sectorsPerTrack: 0x20, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0xC, FatType.Fat12),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x8000, maximumSectorCount: 0x10000, hiddenSectorCount: 0x20, numHeads: 0x4, sectorsPerTrack: 0x20, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0xC, FatType.Fat12),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x10000, maximumSectorCount: 0x20000, hiddenSectorCount: 0x20, numHeads: 0x8, sectorsPerTrack: 0x20, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0xC, FatType.Fat12),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x20000, maximumSectorCount: 0x40000, hiddenSectorCount: 0x40, numHeads: 0x8, sectorsPerTrack: 0x20, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x40000, maximumSectorCount: 0x80000, hiddenSectorCount: 0x40, numHeads: 0x10, sectorsPerTrack: 0x20, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x80000, maximumSectorCount: 0xFC000, hiddenSectorCount: 0x80, numHeads: 0x10, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0xFC000, maximumSectorCount: 0x1F8000, hiddenSectorCount: 0x80, numHeads: 0x20, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x1F8000, maximumSectorCount: 0x200000, hiddenSectorCount: 0x80, numHeads: 0x40, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x20, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x200000, maximumSectorCount: 0x3F0000, hiddenSectorCount: 0x80, numHeads: 0x40, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x40, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x3F0000, maximumSectorCount: 0x400000, hiddenSectorCount: 0x80, numHeads: 0x80, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x40, fatTableEntrySizeBits: 0x10, FatType.Fat16),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x414400, maximumSectorCount: 0x7E0000, hiddenSectorCount: 0x2000, numHeads: 0x80, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x40, fatTableEntrySizeBits: 0x20, FatType.Fat32),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x7E0000, maximumSectorCount: 0x4000000, hiddenSectorCount: 0x2000, numHeads: 0xFF, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x40, fatTableEntrySizeBits: 0x20, FatType.Fat32),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x4040000, maximumSectorCount: 0x10000000, hiddenSectorCount: 0x8000, numHeads: 0xFF, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x100, fatTableEntrySizeBits: 0x20, FatType.FatEx),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x10000000, maximumSectorCount: 0x40000000, hiddenSectorCount: 0x10000, numHeads: 0xFF, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x200, fatTableEntrySizeBits: 0x20, FatType.FatEx),
|
||||
new FatFormatAttribute(minimumSectorCount: 0x40000000, maximumSectorCount: 0x100000000, hiddenSectorCount: 0x20000, numHeads: 0xFF, sectorsPerTrack: 0x3F, sectorsPerCluster: 0x400, fatTableEntrySizeBits: 0x20, FatType.FatEx)
|
||||
];
|
||||
|
||||
public static Result FormatDryRun(uint userAreaSectorCount, uint protectedAreaSectorCount)
|
||||
{
|
||||
ulong totalSectorCount = (ulong)userAreaSectorCount + protectedAreaSectorCount;
|
||||
Result res = GetFormatAttributes(out ReadOnlyRef<FatFormatAttribute> attributes, totalSectorCount);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Call nn::fat::detail::GetPfFormatParams
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result GetFormatAttributes(out ReadOnlyRef<FatFormatAttribute> outAttributes, ulong sectorCount)
|
||||
{
|
||||
for (int i = 0; i < FormatAttributes.Length; i++)
|
||||
{
|
||||
ref readonly FatFormatAttribute attribute = ref FormatAttributes[i];
|
||||
if (attribute.MinimumSectorCount <= sectorCount && attribute.MaximumSectorCount >= sectorCount)
|
||||
{
|
||||
outAttributes = new(in attribute);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
outAttributes = default;
|
||||
return ResultFs.FatFsFormatUnsupportedSize.Log();
|
||||
}
|
||||
}
|
|
@ -1217,7 +1217,7 @@ public static class PathFunctions
|
|||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.InvalidArgument"/>: <paramref name="pathBuffer"/> was too small to contain the built path.</returns>
|
||||
internal static Result SetUpFixedPathSingleEntry(scoped ref Path path, Span<byte> pathBuffer,
|
||||
ReadOnlySpan<byte> entryName)
|
||||
scoped ReadOnlySpan<byte> entryName)
|
||||
{
|
||||
var sb = new U8StringBuilder(pathBuffer);
|
||||
sb.Append((byte)'/').Append(entryName);
|
||||
|
@ -1225,7 +1225,7 @@ public static class PathFunctions
|
|||
if (sb.Overflowed)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
return SetUpFixedPath(ref path, pathBuffer);
|
||||
return SetUpFixedPath(ref path, pathBuffer).Ret();
|
||||
}
|
||||
|
||||
// /%s/%s
|
||||
|
@ -1248,7 +1248,7 @@ public static class PathFunctions
|
|||
if (sb.Overflowed)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
return SetUpFixedPath(ref path, pathBuffer);
|
||||
return SetUpFixedPath(ref path, pathBuffer).Ret();
|
||||
}
|
||||
|
||||
// /%016llx
|
||||
|
@ -1268,7 +1268,7 @@ public static class PathFunctions
|
|||
if (sb.Overflowed)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
return SetUpFixedPath(ref path, pathBuffer);
|
||||
return SetUpFixedPath(ref path, pathBuffer).Ret();
|
||||
}
|
||||
|
||||
// /%08x.meta
|
||||
|
@ -1290,7 +1290,7 @@ public static class PathFunctions
|
|||
if (sb.Overflowed)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
return SetUpFixedPath(ref path, pathBuffer);
|
||||
return SetUpFixedPath(ref path, pathBuffer).Ret();
|
||||
}
|
||||
|
||||
// /saveMeta/%016llx
|
||||
|
@ -1312,6 +1312,30 @@ public static class PathFunctions
|
|||
if (sb.Overflowed)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
return SetUpFixedPath(ref path, pathBuffer);
|
||||
return SetUpFixedPath(ref path, pathBuffer).Ret();
|
||||
}
|
||||
|
||||
// %s/%08x
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="Path"/> using the format string <c>%s/%08x</c>
|
||||
/// </summary>
|
||||
/// <param name="path">The <see cref="Path"/> to be initialized.</param>
|
||||
/// <param name="pathBuffer">The buffer that will contain the built string.</param>
|
||||
/// <param name="entryName">The first entry in the generated path.</param>
|
||||
/// <param name="value">The value to insert into the path.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.InvalidArgument"/>: <paramref name="pathBuffer"/> was too small to contain the built path.<br/>
|
||||
/// <see cref="ResultFs.InvalidCharacter"/>: The path contains an invalid character.<br/>
|
||||
/// <see cref="ResultFs.InvalidPathFormat"/>: The path is in an invalid format or is not normalized.</returns>
|
||||
internal static Result SetUpFixedPathEntryWithInt(scoped ref Path path, Span<byte> pathBuffer, scoped ReadOnlySpan<byte> entryName, int value)
|
||||
{
|
||||
var sb = new U8StringBuilder(pathBuffer);
|
||||
sb.Append(entryName)
|
||||
.Append((byte)'/').AppendFormat(value, 'x', 8);
|
||||
|
||||
if (sb.Overflowed)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
return SetUpFixedPath(ref path, pathBuffer).Ret();
|
||||
}
|
||||
}
|
|
@ -9,4 +9,7 @@ internal static class CommonDirNames
|
|||
|
||||
/// <summary>"<c>Contents</c>"</summary>
|
||||
public static ReadOnlySpan<byte> ContentStorageDirectoryName => "Contents"u8;
|
||||
|
||||
/// <summary>"<c>Album</c>"</summary>
|
||||
public static ReadOnlySpan<byte> ImageDirectoryName => "Album"u8;
|
||||
}
|
|
@ -4,6 +4,7 @@ using LibHac.FsSrv.Impl;
|
|||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Sf;
|
||||
using static LibHac.FsSrv.Anonymous;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using Path = LibHac.Fs.Path;
|
||||
|
@ -11,44 +12,31 @@ using Utility = LibHac.FsSrv.Impl.Utility;
|
|||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
public readonly struct BaseFileSystemService
|
||||
file static class Anonymous
|
||||
{
|
||||
private readonly BaseFileSystemServiceImpl _serviceImpl;
|
||||
private readonly ulong _processId;
|
||||
|
||||
public BaseFileSystemService(BaseFileSystemServiceImpl serviceImpl, ulong processId)
|
||||
public static Result GetProgramInfo(FileSystemServer fsServer, out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
_serviceImpl = serviceImpl;
|
||||
_processId = processId;
|
||||
var programRegistry = new ProgramRegistryImpl(fsServer);
|
||||
return programRegistry.GetProgramInfo(out programInfo, processId).Ret();
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
public static Result CheckCapabilityById(FileSystemServer fsServer, BaseFileSystemId id, ulong processId)
|
||||
{
|
||||
return GetProgramInfo(out programInfo, _processId);
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return _serviceImpl.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
|
||||
private Result CheckCapabilityById(BaseFileSystemId id, ulong processId)
|
||||
{
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo, processId);
|
||||
Result res = GetProgramInfo(fsServer, out ProgramInfo programInfo, processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
AccessControl accessControl = programInfo.AccessControl;
|
||||
|
||||
if (id == BaseFileSystemId.TemporaryDirectory)
|
||||
{
|
||||
Accessibility accessibility =
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountTemporaryDirectory);
|
||||
Accessibility accessibility = accessControl.GetAccessibilityFor(AccessibilityType.MountTemporaryDirectory);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
}
|
||||
else
|
||||
{
|
||||
Accessibility accessibility =
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountAllBaseFileSystem);
|
||||
Accessibility accessibility = accessControl.GetAccessibilityFor(AccessibilityType.MountAllBaseFileSystem);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
@ -56,10 +44,28 @@ public readonly struct BaseFileSystemService
|
|||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles managing and opening file systems that aren't NCAs or save data.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public readonly struct BaseFileSystemService
|
||||
{
|
||||
private readonly BaseFileSystemServiceImpl _serviceImpl;
|
||||
private readonly ulong _processId;
|
||||
|
||||
private FileSystemServer FsServer => _serviceImpl.FsServer;
|
||||
|
||||
public BaseFileSystemService(BaseFileSystemServiceImpl serviceImpl, ulong processId)
|
||||
{
|
||||
_serviceImpl = serviceImpl;
|
||||
_processId = processId;
|
||||
}
|
||||
|
||||
public Result OpenBaseFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, BaseFileSystemId fileSystemId)
|
||||
{
|
||||
Result res = CheckCapabilityById(fileSystemId, _processId);
|
||||
Result res = CheckCapabilityById(FsServer, fileSystemId, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Open the file system
|
||||
|
@ -68,8 +74,7 @@ public readonly struct BaseFileSystemService
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Create an SF adapter for the file system
|
||||
using SharedRef<IFileSystemSf> fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(in fileSystem, false);
|
||||
|
||||
using SharedRef<IFileSystemSf> fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(in fileSystem, allowAllOperations: false);
|
||||
outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
|
||||
|
||||
return Result.Success;
|
||||
|
@ -77,38 +82,72 @@ public readonly struct BaseFileSystemService
|
|||
|
||||
public Result FormatBaseFileSystem(BaseFileSystemId fileSystemId)
|
||||
{
|
||||
Result res = CheckCapabilityById(fileSystemId, _processId);
|
||||
Result res = CheckCapabilityById(FsServer, fileSystemId, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return _serviceImpl.FormatBaseFileSystem(fileSystemId);
|
||||
return _serviceImpl.FormatBaseFileSystem(fileSystemId).Ret();
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, ref readonly FspPath rootPath,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// 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.InvalidArgument.Log();
|
||||
|
||||
// Verify the caller has the required permissions
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(requiredAccess);
|
||||
switch (partitionId)
|
||||
{
|
||||
case BisPartitionId.CalibrationFile:
|
||||
{
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountBisCalibrationFile);
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
}
|
||||
case BisPartitionId.SafeMode:
|
||||
{
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountBisSafeMode);
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
}
|
||||
case BisPartitionId.System:
|
||||
{
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountBisSystem);
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
}
|
||||
case BisPartitionId.System0:
|
||||
{
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountBisSystem);
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
}
|
||||
case BisPartitionId.User:
|
||||
{
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountBisUser);
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
}
|
||||
case BisPartitionId.SystemProperPartition:
|
||||
{
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountBisSystemProperPartition);
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.Bis;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
@ -125,7 +164,7 @@ public readonly struct BaseFileSystemService
|
|||
|
||||
// Open the file system
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
res = _serviceImpl.OpenBisFileSystem(ref fileSystem.Ref, partitionId, false);
|
||||
res = _serviceImpl.OpenBisFileSystem(ref fileSystem.Ref, partitionId, caseSensitive: false);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var subDirFileSystem = new SharedRef<IFileSystem>();
|
||||
|
@ -149,46 +188,46 @@ public readonly struct BaseFileSystemService
|
|||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
// Caller must have the FillBis permission
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.FillBis))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.CreatePaddingFile(size);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
|
||||
|
||||
return _serviceImpl.CreatePaddingFile(size).Ret();
|
||||
}
|
||||
|
||||
public Result DeleteAllPaddingFiles()
|
||||
{
|
||||
// Caller must have the FillBis permission
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.FillBis))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.DeleteAllPaddingFiles();
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
|
||||
|
||||
return _serviceImpl.DeleteAllPaddingFiles().Ret();
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
{
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
|
||||
res = _serviceImpl.OpenGameCardFileSystem(ref fileSystem.Ref, handle, partitionId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var asyncFileSystem =
|
||||
new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(in fileSystem));
|
||||
|
||||
using SharedRef<IFileSystemSf> fileSystemAdapter =
|
||||
FileSystemInterfaceAdapter.CreateShared(in asyncFileSystem, false);
|
||||
using var asyncFileSystem = new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(in fileSystem));
|
||||
using SharedRef<IFileSystemSf> fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(in asyncFileSystem, allowAllOperations: false);
|
||||
|
||||
outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
|
||||
|
||||
|
@ -197,7 +236,7 @@ public readonly struct BaseFileSystemService
|
|||
|
||||
public Result OpenSdCardFileSystem(ref SharedRef<IFileSystemSf> outFileSystem)
|
||||
{
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSdCard);
|
||||
|
@ -205,7 +244,7 @@ public readonly struct BaseFileSystemService
|
|||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.Bis;
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.SdCard;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
|
@ -225,20 +264,24 @@ public readonly struct BaseFileSystemService
|
|||
public Result FormatSdCardFileSystem()
|
||||
{
|
||||
// Caller must have the FormatSdCard permission
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.FormatSdCard))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.FormatSdCardProxyFileSystem();
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.SdCard);
|
||||
|
||||
return _serviceImpl.FormatSdCardProxyFileSystem().Ret();
|
||||
}
|
||||
|
||||
public Result FormatSdCardDryRun()
|
||||
{
|
||||
// No permissions are needed to call this method
|
||||
|
||||
return _serviceImpl.FormatSdCardProxyFileSystem();
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.SdCard);
|
||||
|
||||
return _serviceImpl.FormatSdCardDryRun().Ret();
|
||||
}
|
||||
|
||||
public Result IsExFatSupported(out bool isSupported)
|
||||
|
@ -251,8 +294,11 @@ public readonly struct BaseFileSystemService
|
|||
|
||||
public Result OpenImageDirectoryFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, ImageDirectoryId directoryId)
|
||||
{
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.NonGameCard;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
// Caller must have the MountImageAndVideoStorage permission
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Accessibility accessibility =
|
||||
|
@ -280,7 +326,7 @@ public readonly struct BaseFileSystemService
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using SharedRef<IFileSystemSf> fileSystemAdapter =
|
||||
FileSystemInterfaceAdapter.CreateShared(in baseFileSystem, false);
|
||||
FileSystemInterfaceAdapter.CreateShared(in baseFileSystem, allowAllOperations: false);
|
||||
|
||||
outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
|
||||
|
||||
|
@ -290,8 +336,11 @@ public readonly struct BaseFileSystemService
|
|||
public Result OpenBisWiper(ref SharedRef<IWiper> outBisWiper, NativeHandle transferMemoryHandle,
|
||||
ulong transferMemorySize)
|
||||
{
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.Bis;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
// Caller must have the OpenBisWiper permission
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
Result res = GetProgramInfo(FsServer, out ProgramInfo programInfo, _processId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.OpenBisWiper))
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fat;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.FsCreator;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSrv.Storage;
|
||||
using LibHac.Os;
|
||||
using LibHac.Sf;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using Path = LibHac.Fs.Path;
|
||||
using Utility = LibHac.FsSystem.Utility;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
|
@ -13,12 +19,18 @@ public class BaseFileSystemServiceImpl
|
|||
{
|
||||
private Configuration _config;
|
||||
|
||||
public FileSystemServer FsServer => _config.FsServer;
|
||||
|
||||
private static ReadOnlySpan<byte> PaddingDirectoryName => "/Padding"u8;
|
||||
private const int PaddingFileCountMax = 0x10000000;
|
||||
|
||||
public delegate Result BisWiperCreator(ref UniqueRef<IWiper> outWiper, NativeHandle transferMemoryHandle,
|
||||
ulong transferMemorySize);
|
||||
|
||||
public BaseFileSystemServiceImpl(in Configuration configuration)
|
||||
{
|
||||
_config = configuration;
|
||||
// nn::fat::SetCurrentTimeStampCallback(configuration.CurrentTimeFunction);
|
||||
}
|
||||
|
||||
public struct Configuration
|
||||
|
@ -27,8 +39,8 @@ public class BaseFileSystemServiceImpl
|
|||
public IGameCardFileSystemCreator GameCardFileSystemCreator;
|
||||
public ISdCardProxyFileSystemCreator SdCardFileSystemCreator;
|
||||
// CurrentTimeFunction
|
||||
// FatFileSystemCacheManager
|
||||
// BaseFileSystemCreatorHolder
|
||||
public FatFileSystemCacheManager FatFileSystemCacheManager;
|
||||
public BaseFileSystemCreatorHolder BaseFileSystemCreatorHolder;
|
||||
public BisWiperCreator BisWiperCreator;
|
||||
|
||||
// LibHac additions
|
||||
|
@ -37,36 +49,97 @@ public class BaseFileSystemServiceImpl
|
|||
|
||||
public Result OpenBaseFileSystem(ref SharedRef<IFileSystem> outFileSystem, BaseFileSystemId fileSystemId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result res = _config.BaseFileSystemCreatorHolder.Get(out IBaseFileSystemCreator baseFsCreator, fileSystemId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return baseFsCreator.Create(ref outFileSystem, fileSystemId).Ret();
|
||||
}
|
||||
|
||||
public Result FormatBaseFileSystem(BaseFileSystemId fileSystemId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result res = _config.BaseFileSystemCreatorHolder.Get(out IBaseFileSystemCreator baseFsCreator, fileSystemId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return baseFsCreator.Format(fileSystemId).Ret();
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(ref SharedRef<IFileSystem> outFileSystem, BisPartitionId partitionId)
|
||||
{
|
||||
return OpenBisFileSystem(ref outFileSystem, partitionId, false);
|
||||
return OpenBisFileSystem(ref outFileSystem, partitionId, caseSensitive: false).Ret();
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(ref SharedRef<IFileSystem> outFileSystem, BisPartitionId partitionId,
|
||||
bool caseSensitive)
|
||||
{
|
||||
Result res = _config.BisFileSystemCreator.Create(ref outFileSystem, partitionId, caseSensitive);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return Result.Success;
|
||||
return _config.BisFileSystemCreator.Create(ref outFileSystem, partitionId, caseSensitive).Ret();
|
||||
}
|
||||
|
||||
public Result CreatePaddingFile(long size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
Result res = OpenBisFileSystem(ref fileSystem.Ref, BisPartitionId.User);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var pathPaddingDirectory = new Path();
|
||||
res = PathFunctions.SetUpFixedPath(ref pathPaddingDirectory.Ref(), PaddingDirectoryName);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = Utility.EnsureDirectory(fileSystem.Get, in pathPaddingDirectory);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Span<byte> pathPaddingFileBuffer = stackalloc byte[0x80];
|
||||
|
||||
for (int i = 0; i < PaddingFileCountMax; i++)
|
||||
{
|
||||
using scoped var pathPaddingFile = new Path();
|
||||
res = PathFunctions.SetUpFixedPathEntryWithInt(ref pathPaddingFile.Ref(), pathPaddingFileBuffer, PaddingDirectoryName, i);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.Get.CreateFile(in pathPaddingFile, size, CreateFileOptions.CreateConcatenationFile);
|
||||
if (!res.IsSuccess())
|
||||
{
|
||||
if (ResultFs.PathAlreadyExists.Includes(res))
|
||||
{
|
||||
res.Catch().Handle();
|
||||
}
|
||||
else
|
||||
{
|
||||
return res.Miss();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
}
|
||||
|
||||
public Result DeleteAllPaddingFiles()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
Result res = OpenBisFileSystem(ref fileSystem.Ref, BisPartitionId.User);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var pathPaddingDirectory = new Path();
|
||||
res = PathFunctions.SetUpFixedPath(ref pathPaddingDirectory.Ref(), PaddingDirectoryName);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.Get.DeleteDirectoryRecursively(in pathPaddingDirectory);
|
||||
if (!res.IsSuccess())
|
||||
{
|
||||
if (ResultFs.PathNotFound.Includes(res))
|
||||
{
|
||||
res.Catch().Handle();
|
||||
}
|
||||
else
|
||||
{
|
||||
return res.Miss();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(ref SharedRef<IFileSystem> outFileSystem, GameCardHandle handle,
|
||||
|
@ -83,44 +156,69 @@ public class BaseFileSystemServiceImpl
|
|||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenSdCardProxyFileSystem(ref SharedRef<IFileSystem> outFileSystem)
|
||||
{
|
||||
return OpenSdCardProxyFileSystem(ref outFileSystem, false);
|
||||
return OpenSdCardProxyFileSystem(ref outFileSystem, openCaseSensitive: false).Ret();
|
||||
}
|
||||
|
||||
public Result OpenSdCardProxyFileSystem(ref SharedRef<IFileSystem> outFileSystem, bool openCaseSensitive)
|
||||
{
|
||||
return _config.SdCardFileSystemCreator.Create(ref outFileSystem, openCaseSensitive);
|
||||
return _config.SdCardFileSystemCreator.Create(ref outFileSystem, openCaseSensitive).Ret();
|
||||
}
|
||||
|
||||
public Result FormatSdCardProxyFileSystem()
|
||||
{
|
||||
return _config.SdCardFileSystemCreator.Format();
|
||||
return _config.SdCardFileSystemCreator.Format().Ret();
|
||||
}
|
||||
|
||||
public Result FormatSdCardDryRun()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result res = FsServer.Storage.GetSdCardUserAreaNumSectors(out uint userAreaSectors);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = FsServer.Storage.GetSdCardProtectedAreaNumSectors(out uint protectedAreaSectors);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return Fat.Impl.FatFileSystemStorageAdapter.FormatDryRun(userAreaSectors, protectedAreaSectors).Ret();
|
||||
}
|
||||
|
||||
public bool IsExFatSupported()
|
||||
{
|
||||
// Returning false should probably be fine
|
||||
return false;
|
||||
return FatFileSystem.IsExFatSupported();
|
||||
}
|
||||
|
||||
public void FlushFatCache()
|
||||
{
|
||||
using UniqueLock<SdkRecursiveMutex> scopedLock = _config.FatFileSystemCacheManager.GetScopedLock();
|
||||
|
||||
FatFileSystemCacheManager.Iterator iter = _config.FatFileSystemCacheManager.GetIterator();
|
||||
|
||||
while (!iter.IsEnd())
|
||||
{
|
||||
using SharedRef<IFileSystem> fileSystem = iter.Get();
|
||||
if (fileSystem.HasValue)
|
||||
{
|
||||
fileSystem.Get.Flush().IgnoreResult();
|
||||
}
|
||||
|
||||
iter.Next();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenBisWiper(ref UniqueRef<IWiper> outBisWiper, NativeHandle transferMemoryHandle,
|
||||
ulong transferMemorySize)
|
||||
{
|
||||
return _config.BisWiperCreator(ref outBisWiper, transferMemoryHandle, transferMemorySize);
|
||||
return _config.BisWiperCreator(ref outBisWiper, transferMemoryHandle, transferMemorySize).Ret();
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
var registry = new ProgramRegistryImpl(_config.FsServer);
|
||||
return registry.GetProgramInfo(out programInfo, processId);
|
||||
return registry.GetProgramInfo(out programInfo, processId).Ret();
|
||||
}
|
||||
}
|
43
src/LibHac/FsSrv/FsCreator/BaseFileSystemCreatorHolder.cs
Normal file
43
src/LibHac/FsSrv/FsCreator/BaseFileSystemCreatorHolder.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.FsCreator;
|
||||
|
||||
public interface IBaseFileSystemCreator
|
||||
{
|
||||
Result Create(ref SharedRef<IFileSystem> outFileSystem, BaseFileSystemId id);
|
||||
Result Format(BaseFileSystemId id);
|
||||
}
|
||||
|
||||
public class BaseFileSystemCreatorHolder : IDisposable
|
||||
{
|
||||
private Dictionary<BaseFileSystemId, IBaseFileSystemCreator> _creators;
|
||||
|
||||
public BaseFileSystemCreatorHolder()
|
||||
{
|
||||
_creators = new Dictionary<BaseFileSystemId, IBaseFileSystemCreator>();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Get(out IBaseFileSystemCreator outCreator, BaseFileSystemId id)
|
||||
{
|
||||
if (!_creators.TryGetValue(id, out outCreator))
|
||||
{
|
||||
return ResultFs.StorageDeviceInvalidOperation.Log();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Register(IBaseFileSystemCreator creator, BaseFileSystemId id)
|
||||
{
|
||||
_creators.TryAdd(id, creator);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSystem;
|
||||
using Utility = LibHac.FsSystem.Utility;
|
||||
|
||||
namespace LibHac.FsSrv.FsCreator;
|
||||
|
||||
public class BisFsSubDirectoryFileSystemCreator : IBaseFileSystemCreator
|
||||
{
|
||||
private IBuiltInStorageFileSystemCreator _bisFileSystemCreator;
|
||||
private BisPartitionId _partitionId;
|
||||
private ISubDirectoryFileSystemCreator _subDirectoryFileSystemCreator;
|
||||
private U8String _path;
|
||||
|
||||
public BisFsSubDirectoryFileSystemCreator(IBuiltInStorageFileSystemCreator bisFileSystemCreator,
|
||||
BisPartitionId partitionId, ISubDirectoryFileSystemCreator subDirectoryFileSystemCreator, U8Span path)
|
||||
{
|
||||
_bisFileSystemCreator = bisFileSystemCreator;
|
||||
_partitionId = partitionId;
|
||||
_subDirectoryFileSystemCreator = subDirectoryFileSystemCreator;
|
||||
_path = path.ToU8String();
|
||||
}
|
||||
|
||||
public Result Create(ref SharedRef<IFileSystem> outFileSystem, BaseFileSystemId id)
|
||||
{
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
|
||||
using var subDirPath = new Path();
|
||||
Result res = PathFunctions.SetUpFixedPath(ref subDirPath.Ref(), _path);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Open the base file system
|
||||
res = _bisFileSystemCreator.Create(ref fileSystem.Ref, _partitionId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Ensure the subdirectory exists and get an IFileSystem over it
|
||||
res = Utility.EnsureDirectory(fileSystem.Get, in subDirPath);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var subDirFs = new SharedRef<IFileSystem>();
|
||||
res = _subDirectoryFileSystemCreator.Create(ref subDirFs.Ref, in fileSystem, in subDirPath);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Add all the file system wrappers
|
||||
using var typeSetFileSystem = new SharedRef<IFileSystem>(new StorageLayoutTypeSetFileSystem(in fileSystem, StorageLayoutType.Bis));
|
||||
using var asyncFileSystem = new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(in typeSetFileSystem));
|
||||
|
||||
outFileSystem.SetByMove(ref asyncFileSystem.Ref);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Format(BaseFileSystemId id)
|
||||
{
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
}
|
|
@ -7,4 +7,9 @@ namespace LibHac.FsSrv.FsCreator;
|
|||
public interface IBuiltInStorageFileSystemCreator
|
||||
{
|
||||
Result Create(ref SharedRef<IFileSystem> outFileSystem, BisPartitionId partitionId, bool caseSensitive);
|
||||
|
||||
public Result Create(ref SharedRef<IFileSystem> outFileSystem, BisPartitionId partitionId)
|
||||
{
|
||||
return Create(ref outFileSystem, partitionId, caseSensitive: false).Ret();
|
||||
}
|
||||
}
|
|
@ -20,4 +20,9 @@ public interface ISdCardProxyFileSystemCreator
|
|||
/// </summary>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
Result Format();
|
||||
|
||||
public Result Create(ref SharedRef<IFileSystem> outFileSystem)
|
||||
{
|
||||
return Create(ref outFileSystem, openCaseSensitive: false).Ret();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSystem;
|
||||
using Utility = LibHac.FsSystem.Utility;
|
||||
|
||||
namespace LibHac.FsSrv.FsCreator;
|
||||
|
||||
public class ImageDirectoryFileSystemCreator : IBaseFileSystemCreator
|
||||
{
|
||||
private Configuration _config;
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public IBuiltInStorageFileSystemCreator BisFileSystemCreator;
|
||||
public ISdCardProxyFileSystemCreator SdCardFileSystemCreator;
|
||||
public ILocalFileSystemCreator LocalFileSystemCreator;
|
||||
public ISubDirectoryFileSystemCreator SubDirectoryFileSystemCreator;
|
||||
}
|
||||
|
||||
public ImageDirectoryFileSystemCreator(in Configuration config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public Result Create(ref SharedRef<IFileSystem> outFileSystem, BaseFileSystemId id)
|
||||
{
|
||||
Result res;
|
||||
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
using scoped var imageDirectoryPath = new Path();
|
||||
Span<byte> imageDirectoryPathBuffer = stackalloc byte[0x40];
|
||||
|
||||
// Open the base file system containing the image directory, and get the path of the image directory
|
||||
switch (id)
|
||||
{
|
||||
case BaseFileSystemId.ImageDirectoryNand:
|
||||
{
|
||||
res = _config.BisFileSystemCreator.Create(ref fileSystem.Ref, BisPartitionId.User);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = PathFunctions.SetUpFixedPathSingleEntry(ref imageDirectoryPath.Ref(), imageDirectoryPathBuffer,
|
||||
CommonDirNames.ImageDirectoryName);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
break;
|
||||
}
|
||||
case BaseFileSystemId.ImageDirectorySdCard:
|
||||
{
|
||||
res = _config.SdCardFileSystemCreator.Create(ref fileSystem.Ref);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = PathFunctions.SetUpFixedPathDoubleEntry(ref imageDirectoryPath.Ref(), imageDirectoryPathBuffer,
|
||||
CommonDirNames.SdCardNintendoRootDirectoryName, CommonDirNames.ImageDirectoryName);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
res = Utility.EnsureDirectory(fileSystem.Get, in imageDirectoryPath);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var subDirFs = new SharedRef<IFileSystem>();
|
||||
res = _config.SubDirectoryFileSystemCreator.Create(ref subDirFs.Ref, in fileSystem, in imageDirectoryPath);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.NonGameCard;
|
||||
using var typeSetFileSystem = new SharedRef<IFileSystem>(new StorageLayoutTypeSetFileSystem(in subDirFs, storageFlag));
|
||||
using var asyncFileSystem = new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(in typeSetFileSystem));
|
||||
|
||||
outFileSystem.SetByMove(ref asyncFileSystem.Ref);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Format(BaseFileSystemId id)
|
||||
{
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
}
|
152
src/LibHac/FsSrv/Impl/FatFileSystemCacheManager.cs
Normal file
152
src/LibHac/FsSrv/Impl/FatFileSystemCacheManager.cs
Normal file
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSrv.Impl;
|
||||
|
||||
/// <summary>
|
||||
/// Caches opened FatFileSystems.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public class FatFileSystemCacheManager : IDisposable
|
||||
{
|
||||
public readonly struct CacheId
|
||||
{
|
||||
internal readonly int Value;
|
||||
|
||||
internal CacheId(int value) => Value = value;
|
||||
}
|
||||
|
||||
private struct CacheNode : IDisposable
|
||||
{
|
||||
public int Id;
|
||||
public SharedRef<IFileSystem> FileSystem;
|
||||
|
||||
public CacheNode(int id, ref readonly SharedRef<IFileSystem> fileSystem)
|
||||
{
|
||||
Id = id;
|
||||
FileSystem = SharedRef<IFileSystem>.CreateCopy(in fileSystem);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FileSystem.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public struct Iterator
|
||||
{
|
||||
private LinkedListNode<CacheNode> _current;
|
||||
|
||||
internal Iterator(FatFileSystemCacheManager manager)
|
||||
{
|
||||
_current = manager._cache.First;
|
||||
}
|
||||
|
||||
public readonly bool IsEnd() => _current is not null;
|
||||
public void Next() => _current = _current.Next;
|
||||
public readonly SharedRef<IFileSystem> Get() => SharedRef<IFileSystem>.CreateCopy(in _current.ValueRef.FileSystem);
|
||||
}
|
||||
|
||||
private SdkRecursiveMutex _mutex;
|
||||
private int _nextCacheId;
|
||||
private LinkedList<CacheNode> _cache;
|
||||
|
||||
public FatFileSystemCacheManager()
|
||||
{
|
||||
_mutex = new SdkRecursiveMutex();
|
||||
_nextCacheId = 0;
|
||||
_cache = new LinkedList<CacheNode>();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
while (_cache.First is not null)
|
||||
{
|
||||
LinkedListNode<CacheNode> currentNode = _cache.First;
|
||||
_cache.Remove(currentNode);
|
||||
currentNode.ValueRef.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public UniqueLock<SdkRecursiveMutex> GetScopedLock()
|
||||
{
|
||||
return new UniqueLock<SdkRecursiveMutex>(_mutex);
|
||||
}
|
||||
|
||||
private SharedRef<IFileSystem> GetCache(int cacheId)
|
||||
{
|
||||
LinkedListNode<CacheNode> currentNode = _cache.First;
|
||||
|
||||
while (currentNode is not null)
|
||||
{
|
||||
if (currentNode.ValueRef.Id == cacheId)
|
||||
{
|
||||
return SharedRef<IFileSystem>.CreateCopy(in currentNode.ValueRef.FileSystem);
|
||||
}
|
||||
|
||||
currentNode = currentNode.Next;
|
||||
}
|
||||
|
||||
return new SharedRef<IFileSystem>();
|
||||
}
|
||||
|
||||
public SharedRef<IFileSystem> GetCache(CacheId cacheId)
|
||||
{
|
||||
return GetCache(cacheId.Value);
|
||||
}
|
||||
|
||||
public Result SetCache(out CacheId outCacheId, ref readonly SharedRef<IFileSystem> fileSystem)
|
||||
{
|
||||
int originalId = _nextCacheId;
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool cacheIdInUse;
|
||||
using (SharedRef<IFileSystem> fs = GetCache(_nextCacheId))
|
||||
{
|
||||
cacheIdInUse = fs.HasValue;
|
||||
}
|
||||
|
||||
if (!cacheIdInUse)
|
||||
break;
|
||||
|
||||
_nextCacheId++;
|
||||
if (_nextCacheId == originalId)
|
||||
Abort.DoAbort();
|
||||
}
|
||||
|
||||
int id = _nextCacheId;
|
||||
var node = new LinkedListNode<CacheNode>(new CacheNode(_nextCacheId, in fileSystem));
|
||||
_cache.AddLast(node);
|
||||
|
||||
_nextCacheId++;
|
||||
outCacheId = new CacheId(id);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void UnsetCache(CacheId cacheId)
|
||||
{
|
||||
LinkedListNode<CacheNode> currentNode = _cache.First;
|
||||
|
||||
while (currentNode is not null)
|
||||
{
|
||||
if (currentNode.ValueRef.Id == cacheId.Value)
|
||||
{
|
||||
_cache.Remove(currentNode);
|
||||
currentNode.ValueRef.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
currentNode = currentNode.Next;
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator GetIterator()
|
||||
{
|
||||
return new Iterator(this);
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ public struct SdkMutexType : ILockable
|
|||
}
|
||||
}
|
||||
|
||||
public class SdkRecursiveMutex : IBasicLockable
|
||||
public class SdkRecursiveMutex : ILockable
|
||||
{
|
||||
private SdkRecursiveMutexType _impl;
|
||||
|
||||
|
@ -95,6 +95,11 @@ public class SdkRecursiveMutex : IBasicLockable
|
|||
_impl.Lock();
|
||||
}
|
||||
|
||||
public bool TryLock()
|
||||
{
|
||||
return _impl.TryLock();
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
_impl.Unlock();
|
||||
|
|
Loading…
Reference in a new issue