From e757dff9b9e225e5e0d5b2b3c4b0db97088dbc58 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 30 Jun 2024 10:07:49 -0700 Subject: [PATCH] Implement BaseFileSystemService and some related classes --- src/LibHac/Fat/FatFileSystem.cs | 6 + src/LibHac/Fat/FatFormatAttribute.cs | 36 ++++ .../Fat/Impl/FatFileSystemStorageAdapter.cs | 55 ++++++ src/LibHac/Fs/Common/Path.cs | 36 +++- src/LibHac/Fs/CommonDirNames.cs | 3 + src/LibHac/FsSrv/BaseFileSystemService.cs | 183 +++++++++++------- src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs | 138 +++++++++++-- .../FsCreator/BaseFileSystemCreatorHolder.cs | 43 ++++ .../BisFsSubDirectoryFileSystemCreator.cs | 59 ++++++ .../IBuiltInStorageFileSystemCreator.cs | 5 + .../ISdCardProxyFileSystemCreator.cs | 5 + .../ImageDirectoryFileSystemCreator.cs | 85 ++++++++ .../FsSrv/Impl/FatFileSystemCacheManager.cs | 152 +++++++++++++++ src/LibHac/Os/SdkMutex.cs | 7 +- 14 files changed, 719 insertions(+), 94 deletions(-) create mode 100644 src/LibHac/Fat/FatFileSystem.cs create mode 100644 src/LibHac/Fat/FatFormatAttribute.cs create mode 100644 src/LibHac/Fat/Impl/FatFileSystemStorageAdapter.cs create mode 100644 src/LibHac/FsSrv/FsCreator/BaseFileSystemCreatorHolder.cs create mode 100644 src/LibHac/FsSrv/FsCreator/BisFsSubDirectoryFileSystemCreator.cs create mode 100644 src/LibHac/FsSrv/FsCreator/ImageDirectoryFileSystemCreator.cs create mode 100644 src/LibHac/FsSrv/Impl/FatFileSystemCacheManager.cs diff --git a/src/LibHac/Fat/FatFileSystem.cs b/src/LibHac/Fat/FatFileSystem.cs new file mode 100644 index 00000000..42a8bdd4 --- /dev/null +++ b/src/LibHac/Fat/FatFileSystem.cs @@ -0,0 +1,6 @@ +namespace LibHac.Fat; + +public class FatFileSystem +{ + public static bool IsExFatSupported() => false; +} \ No newline at end of file diff --git a/src/LibHac/Fat/FatFormatAttribute.cs b/src/LibHac/Fat/FatFormatAttribute.cs new file mode 100644 index 00000000..d2b0be77 --- /dev/null +++ b/src/LibHac/Fat/FatFormatAttribute.cs @@ -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 +} \ No newline at end of file diff --git a/src/LibHac/Fat/Impl/FatFileSystemStorageAdapter.cs b/src/LibHac/Fat/Impl/FatFileSystemStorageAdapter.cs new file mode 100644 index 00000000..8e31c7cb --- /dev/null +++ b/src/LibHac/Fat/Impl/FatFileSystemStorageAdapter.cs @@ -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 attributes, totalSectorCount); + if (res.IsFailure()) return res.Miss(); + + // Call nn::fat::detail::GetPfFormatParams + + return Result.Success; + } + + private static Result GetFormatAttributes(out ReadOnlyRef 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(); + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs index 054d0869..aa59527f 100644 --- a/src/LibHac/Fs/Common/Path.cs +++ b/src/LibHac/Fs/Common/Path.cs @@ -1217,7 +1217,7 @@ public static class PathFunctions /// : The operation was successful.
/// : was too small to contain the built path.
internal static Result SetUpFixedPathSingleEntry(scoped ref Path path, Span pathBuffer, - ReadOnlySpan entryName) + scoped ReadOnlySpan 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 + /// + /// Initializes a using the format string %s/%08x + /// + /// The to be initialized. + /// The buffer that will contain the built string. + /// The first entry in the generated path. + /// The value to insert into the path. + /// : The operation was successful.
+ /// : was too small to contain the built path.
+ /// : The path contains an invalid character.
+ /// : The path is in an invalid format or is not normalized.
+ internal static Result SetUpFixedPathEntryWithInt(scoped ref Path path, Span pathBuffer, scoped ReadOnlySpan 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(); } } \ No newline at end of file diff --git a/src/LibHac/Fs/CommonDirNames.cs b/src/LibHac/Fs/CommonDirNames.cs index 099536a1..99828028 100644 --- a/src/LibHac/Fs/CommonDirNames.cs +++ b/src/LibHac/Fs/CommonDirNames.cs @@ -9,4 +9,7 @@ internal static class CommonDirNames /// "Contents" public static ReadOnlySpan ContentStorageDirectoryName => "Contents"u8; + + /// "Album" + public static ReadOnlySpan ImageDirectoryName => "Album"u8; } \ No newline at end of file diff --git a/src/LibHac/FsSrv/BaseFileSystemService.cs b/src/LibHac/FsSrv/BaseFileSystemService.cs index 18673b1a..b8f44870 100644 --- a/src/LibHac/FsSrv/BaseFileSystemService.cs +++ b/src/LibHac/FsSrv/BaseFileSystemService.cs @@ -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; } +} + +/// +/// Handles managing and opening file systems that aren't NCAs or save data. +/// +/// Based on nnSdk 18.3.0 (FS 18.0.0) +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 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 fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(in fileSystem, false); - + using SharedRef 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 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(); - 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(); @@ -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 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(); - res = _serviceImpl.OpenGameCardFileSystem(ref fileSystem.Ref, handle, partitionId); if (res.IsFailure()) return res.Miss(); - using var asyncFileSystem = - new SharedRef(new AsynchronousAccessFileSystem(in fileSystem)); - - using SharedRef fileSystemAdapter = - FileSystemInterfaceAdapter.CreateShared(in asyncFileSystem, false); + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(in fileSystem)); + using SharedRef 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 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(); @@ -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 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 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 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)) diff --git a/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs b/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs index a35dc8b0..3aa93adc 100644 --- a/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs @@ -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 PaddingDirectoryName => "/Padding"u8; + private const int PaddingFileCountMax = 0x10000000; + public delegate Result BisWiperCreator(ref UniqueRef 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 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 outFileSystem, BisPartitionId partitionId) { - return OpenBisFileSystem(ref outFileSystem, partitionId, false); + return OpenBisFileSystem(ref outFileSystem, partitionId, caseSensitive: false).Ret(); } public Result OpenBisFileSystem(ref SharedRef 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(); + 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 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(); + 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 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 outFileSystem) { - return OpenSdCardProxyFileSystem(ref outFileSystem, false); + return OpenSdCardProxyFileSystem(ref outFileSystem, openCaseSensitive: false).Ret(); } public Result OpenSdCardProxyFileSystem(ref SharedRef 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 scopedLock = _config.FatFileSystemCacheManager.GetScopedLock(); + + FatFileSystemCacheManager.Iterator iter = _config.FatFileSystemCacheManager.GetIterator(); + + while (!iter.IsEnd()) + { + using SharedRef fileSystem = iter.Get(); + if (fileSystem.HasValue) + { + fileSystem.Get.Flush().IgnoreResult(); + } + + iter.Next(); + } } public Result OpenBisWiper(ref UniqueRef 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(); } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/BaseFileSystemCreatorHolder.cs b/src/LibHac/FsSrv/FsCreator/BaseFileSystemCreatorHolder.cs new file mode 100644 index 00000000..7068ddec --- /dev/null +++ b/src/LibHac/FsSrv/FsCreator/BaseFileSystemCreatorHolder.cs @@ -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 outFileSystem, BaseFileSystemId id); + Result Format(BaseFileSystemId id); +} + +public class BaseFileSystemCreatorHolder : IDisposable +{ + private Dictionary _creators; + + public BaseFileSystemCreatorHolder() + { + _creators = new Dictionary(); + } + + 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); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/BisFsSubDirectoryFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/BisFsSubDirectoryFileSystemCreator.cs new file mode 100644 index 00000000..17e74302 --- /dev/null +++ b/src/LibHac/FsSrv/FsCreator/BisFsSubDirectoryFileSystemCreator.cs @@ -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 outFileSystem, BaseFileSystemId id) + { + using var fileSystem = new SharedRef(); + + 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(); + 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(new StorageLayoutTypeSetFileSystem(in fileSystem, StorageLayoutType.Bis)); + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(in typeSetFileSystem)); + + outFileSystem.SetByMove(ref asyncFileSystem.Ref); + + return Result.Success; + } + + public Result Format(BaseFileSystemId id) + { + return ResultFs.NotImplemented.Log(); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/IBuiltInStorageFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/IBuiltInStorageFileSystemCreator.cs index 01238cc1..b359ab61 100644 --- a/src/LibHac/FsSrv/FsCreator/IBuiltInStorageFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/IBuiltInStorageFileSystemCreator.cs @@ -7,4 +7,9 @@ namespace LibHac.FsSrv.FsCreator; public interface IBuiltInStorageFileSystemCreator { Result Create(ref SharedRef outFileSystem, BisPartitionId partitionId, bool caseSensitive); + + public Result Create(ref SharedRef outFileSystem, BisPartitionId partitionId) + { + return Create(ref outFileSystem, partitionId, caseSensitive: false).Ret(); + } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/ISdCardProxyFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ISdCardProxyFileSystemCreator.cs index fc3881b6..fee0cd48 100644 --- a/src/LibHac/FsSrv/FsCreator/ISdCardProxyFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/ISdCardProxyFileSystemCreator.cs @@ -20,4 +20,9 @@ public interface ISdCardProxyFileSystemCreator /// /// The of the operation. Result Format(); + + public Result Create(ref SharedRef outFileSystem) + { + return Create(ref outFileSystem, openCaseSensitive: false).Ret(); + } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/ImageDirectoryFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ImageDirectoryFileSystemCreator.cs new file mode 100644 index 00000000..8a0c0b87 --- /dev/null +++ b/src/LibHac/FsSrv/FsCreator/ImageDirectoryFileSystemCreator.cs @@ -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 outFileSystem, BaseFileSystemId id) + { + Result res; + + using var fileSystem = new SharedRef(); + using scoped var imageDirectoryPath = new Path(); + Span 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(); + 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(new StorageLayoutTypeSetFileSystem(in subDirFs, storageFlag)); + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(in typeSetFileSystem)); + + outFileSystem.SetByMove(ref asyncFileSystem.Ref); + + return Result.Success; + } + + public Result Format(BaseFileSystemId id) + { + return ResultFs.NotImplemented.Log(); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/FatFileSystemCacheManager.cs b/src/LibHac/FsSrv/Impl/FatFileSystemCacheManager.cs new file mode 100644 index 00000000..6f7e5872 --- /dev/null +++ b/src/LibHac/FsSrv/Impl/FatFileSystemCacheManager.cs @@ -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; + +/// +/// Caches opened FatFileSystems. +/// +/// Based on nnSdk 18.3.0 (FS 18.0.0) +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 FileSystem; + + public CacheNode(int id, ref readonly SharedRef fileSystem) + { + Id = id; + FileSystem = SharedRef.CreateCopy(in fileSystem); + } + + public void Dispose() + { + FileSystem.Destroy(); + } + } + + public struct Iterator + { + private LinkedListNode _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 Get() => SharedRef.CreateCopy(in _current.ValueRef.FileSystem); + } + + private SdkRecursiveMutex _mutex; + private int _nextCacheId; + private LinkedList _cache; + + public FatFileSystemCacheManager() + { + _mutex = new SdkRecursiveMutex(); + _nextCacheId = 0; + _cache = new LinkedList(); + } + + public void Dispose() + { + while (_cache.First is not null) + { + LinkedListNode currentNode = _cache.First; + _cache.Remove(currentNode); + currentNode.ValueRef.Dispose(); + } + } + + public UniqueLock GetScopedLock() + { + return new UniqueLock(_mutex); + } + + private SharedRef GetCache(int cacheId) + { + LinkedListNode currentNode = _cache.First; + + while (currentNode is not null) + { + if (currentNode.ValueRef.Id == cacheId) + { + return SharedRef.CreateCopy(in currentNode.ValueRef.FileSystem); + } + + currentNode = currentNode.Next; + } + + return new SharedRef(); + } + + public SharedRef GetCache(CacheId cacheId) + { + return GetCache(cacheId.Value); + } + + public Result SetCache(out CacheId outCacheId, ref readonly SharedRef fileSystem) + { + int originalId = _nextCacheId; + + while (true) + { + bool cacheIdInUse; + using (SharedRef fs = GetCache(_nextCacheId)) + { + cacheIdInUse = fs.HasValue; + } + + if (!cacheIdInUse) + break; + + _nextCacheId++; + if (_nextCacheId == originalId) + Abort.DoAbort(); + } + + int id = _nextCacheId; + var node = new LinkedListNode(new CacheNode(_nextCacheId, in fileSystem)); + _cache.AddLast(node); + + _nextCacheId++; + outCacheId = new CacheId(id); + return Result.Success; + } + + public void UnsetCache(CacheId cacheId) + { + LinkedListNode 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); + } +} \ No newline at end of file diff --git a/src/LibHac/Os/SdkMutex.cs b/src/LibHac/Os/SdkMutex.cs index 3c6a17a2..5f4dc159 100644 --- a/src/LibHac/Os/SdkMutex.cs +++ b/src/LibHac/Os/SdkMutex.cs @@ -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();