Update the FsSrv namespace to use Fs.Path

This commit is contained in:
Alex Barney 2021-08-02 18:31:32 -07:00
parent 9a97e5ef3e
commit 6ba10074a3
24 changed files with 877 additions and 895 deletions

View File

@ -807,5 +807,64 @@ namespace LibHac.Fs
return Result.Success;
}
// Only a small number of format strings are used with these functions, so we can hard code them all easily.
// /%s
internal static Result SetUpFixedPathSingleEntry(ref Path path, Span<byte> pathBuffer,
ReadOnlySpan<byte> entryName)
{
var sb = new U8StringBuilder(pathBuffer);
sb.Append((byte)'/').Append(entryName);
if (sb.Overflowed)
return ResultFs.InvalidArgument.Log();
return SetUpFixedPath(ref path, pathBuffer);
}
// /%016llx
internal static Result SetUpFixedPathSaveId(ref Path path, Span<byte> pathBuffer, ulong saveDataId)
{
var sb = new U8StringBuilder(pathBuffer);
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
if (sb.Overflowed)
return ResultFs.InvalidArgument.Log();
return SetUpFixedPath(ref path, pathBuffer);
}
// /%08x.meta
internal static Result SetUpFixedPathSaveMetaName(ref Path path, Span<byte> pathBuffer, uint metaType)
{
ReadOnlySpan<byte> metaExtension = new[] { (byte)'.', (byte)'m', (byte)'e', (byte)'t', (byte)'a' }; // ".meta"
var sb = new U8StringBuilder(pathBuffer);
sb.Append((byte)'/').AppendFormat(metaType, 'x', 8).Append(metaExtension);
if (sb.Overflowed)
return ResultFs.InvalidArgument.Log();
return SetUpFixedPath(ref path, pathBuffer);
}
// /saveMeta/%016llx
internal static Result SetUpFixedPathSaveMetaDir(ref Path path, Span<byte> pathBuffer, ulong saveDataId)
{
ReadOnlySpan<byte> metaDirectoryName = new[]
{
(byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t',
(byte)'a', (byte)'/'
};
var sb = new U8StringBuilder(pathBuffer);
sb.Append(metaDirectoryName).AppendFormat(saveDataId, 'x', 16);
if (sb.Overflowed)
return ResultFs.InvalidArgument.Log();
return SetUpFixedPath(ref path, pathBuffer);
}
}
}

View File

@ -3,6 +3,7 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Sf;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
@ -73,7 +74,7 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs, false);
return Result.Success;
}
@ -83,10 +84,10 @@ namespace LibHac.FsSrv
}
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath,
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> outFileSystem, in FspPath rootPath,
BisPartitionId partitionId)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
UnsafeHelpers.SkipParamInit(out outFileSystem);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
@ -112,27 +113,44 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
// Normalize the path
using var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty);
if (normalizer.Result.IsFailure()) return normalizer.Result;
const StorageType storageFlag = StorageType.Bis;
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
ReferenceCountedDisposable<IFileSystem> fs = null;
// Normalize the path
var pathNormalized = new Fs.Path();
rc = pathNormalized.Initialize(rootPath.Str);
if (rc.IsFailure()) return rc;
var pathFlags = new PathFlags();
pathFlags.AllowEmptyPath();
rc = pathNormalized.Normalize(pathFlags);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> baseFileSystem = null;
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
// Open the file system
rc = _serviceImpl.OpenBisFileSystem(out fs, normalizer.Path,
partitionId);
rc = _serviceImpl.OpenBisFileSystem(out baseFileSystem, partitionId, false);
if (rc.IsFailure()) return rc;
rc = Impl.Utility.CreateSubDirectoryFileSystem(out fileSystem, ref baseFileSystem, in pathNormalized);
if (rc.IsFailure()) return rc;
// Add all the file system wrappers
fileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref fileSystem, storageFlag);
fileSystem = AsynchronousAccessFileSystem.CreateShared(ref fileSystem);
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
outFileSystem = FileSystemInterfaceAdapter.CreateShared(ref fileSystem, false);
return Result.Success;
}
finally
{
fs?.Dispose();
baseFileSystem?.Dispose();
fileSystem?.Dispose();
}
}
@ -188,7 +206,7 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs, false);
return Result.Success;
}
@ -218,7 +236,7 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs, false);
return Result.Success;
}
@ -291,7 +309,7 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs, false);
return Result.Success;
}

View File

@ -41,21 +41,18 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
BisPartitionId partitionId)
{
return OpenBisFileSystem(out fileSystem, rootPath, partitionId, false);
return OpenBisFileSystem(out fileSystem, partitionId, false);
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
BisPartitionId partitionId, bool caseSensitive)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
Result rc = _config.BisFileSystemCreator.Create(out IFileSystem fs, rootPath.ToString(), partitionId);
Result rc = _config.BisFileSystemCreator.Create(out fileSystem, partitionId, caseSensitive);
if (rc.IsFailure()) return rc;
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
}
@ -95,14 +92,7 @@ namespace LibHac.FsSrv
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
// Todo: Shared
Result rc = _config.SdCardFileSystemCreator.Create(out IFileSystem fs, openCaseSensitive);
if (rc.IsFailure()) return rc;
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
return _config.SdCardFileSystemCreator.Create(out fileSystem, openCaseSensitive);
}
public Result FormatSdCardProxyFileSystem()

View File

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
@ -26,118 +27,119 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenCustomStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenCustomStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> outFileSystem,
CustomStorageId storageId)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
UnsafeHelpers.SkipParamInit(out outFileSystem);
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
ReferenceCountedDisposable<IFileSystem> tempFs = null;
ReferenceCountedDisposable<IFileSystem> encryptedFs = null;
try
{
Span<byte> path = stackalloc byte[0x40];
const int pathBufferLength = 0x40;
switch (storageId)
// Hack around error CS8350.
Span<byte> buffer = stackalloc byte[pathBufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> pathBuffer = MemoryMarshal.CreateSpan(ref bufferRef, pathBufferLength);
if (storageId == CustomStorageId.System)
{
case CustomStorageId.SdCard:
{
Result rc = BaseFileSystemService.OpenSdCardProxyFileSystem(out tempFs);
if (rc.IsFailure()) return rc;
Result rc = BaseFileSystemService.OpenBisFileSystem(out fileSystem, BisPartitionId.User);
if (rc.IsFailure()) return rc;
U8Span customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.SdCard);
var sb = new U8StringBuilder(path);
sb.Append((byte)'/')
.Append(CommonPaths.SdCardNintendoRootDirectoryName)
.Append((byte)'/')
.Append(customStorageDir);
var path = new Path();
var sb = new U8StringBuilder(pathBuffer);
sb.Append((byte)'/')
.Append(CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System));
rc = Utility.WrapSubDirectory(out tempFs, ref tempFs, new U8Span(path), true);
if (rc.IsFailure()) return rc;
rc = PathFunctions.SetUpFixedPath(ref path, pathBuffer);
if (rc.IsFailure()) return rc;
rc = FsCreators.EncryptedFileSystemCreator.Create(out encryptedFs, tempFs,
EncryptedFsKeyId.CustomStorage, SdEncryptionSeed);
if (rc.IsFailure()) return rc;
tempFs = Shared.Move(ref fileSystem);
rc = Utility.WrapSubDirectory(out fileSystem, ref tempFs, in path, true);
if (rc.IsFailure()) return rc;
return Result.Success;
}
case CustomStorageId.System:
{
Result rc = BaseFileSystemService.OpenBisFileSystem(out tempFs, U8Span.Empty,
BisPartitionId.User);
if (rc.IsFailure()) return rc;
U8Span customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System);
var sb = new U8StringBuilder(path);
sb.Append((byte)'/')
.Append(customStorageDir);
rc = Utility.WrapSubDirectory(out tempFs, ref tempFs, new U8Span(path), true);
if (rc.IsFailure()) return rc;
fileSystem = Shared.Move(ref tempFs);
return Result.Success;
}
default:
return ResultFs.InvalidArgument.Log();
path.Dispose();
}
}
finally
{
tempFs?.Dispose();
encryptedFs?.Dispose();
}
}
public Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path,
bool openCaseSensitive)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
Result rc;
if (!path.IsEmpty())
{
rc = Util.VerifyHostPath(path);
if (rc.IsFailure()) return rc;
}
// Todo: Return shared fs from Create
rc = FsCreators.TargetManagerFileSystemCreator.Create(out IFileSystem hostFs, openCaseSensitive);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> sharedHostFs = null;
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
try
{
sharedHostFs = new ReferenceCountedDisposable<IFileSystem>(hostFs);
if (path.IsEmpty())
else if (storageId == CustomStorageId.SdCard)
{
ReadOnlySpan<byte> rootHostPath = new[] { (byte)'C', (byte)':', (byte)'/' };
rc = sharedHostFs.Target.GetEntryType(out _, new U8Span(rootHostPath));
Result rc = BaseFileSystemService.OpenSdCardProxyFileSystem(out fileSystem);
if (rc.IsFailure()) return rc;
// Nintendo ignores all results other than this one
if (ResultFs.TargetNotFound.Includes(rc))
return rc;
var path = new Path();
var sb = new U8StringBuilder(pathBuffer);
sb.Append((byte)'/')
.Append(CommonPaths.SdCardNintendoRootDirectoryName)
.Append((byte)'/')
.Append(CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.SdCard));
Shared.Move(out fileSystem, ref sharedHostFs);
return Result.Success;
rc = PathFunctions.SetUpFixedPath(ref path, pathBuffer);
if (rc.IsFailure()) return rc;
tempFs = Shared.Move(ref fileSystem);
rc = Utility.WrapSubDirectory(out fileSystem, ref tempFs, in path, true);
if (rc.IsFailure()) return rc;
tempFs = Shared.Move(ref fileSystem);
rc = FsCreators.EncryptedFileSystemCreator.Create(out fileSystem, ref tempFs,
IEncryptedFileSystemCreator.KeyId.CustomStorage, SdEncryptionSeed);
if (rc.IsFailure()) return rc;
path.Dispose();
}
else
{
return ResultFs.InvalidArgument.Log();
}
rc = FsCreators.SubDirectoryFileSystemCreator.Create(out subDirFs, ref sharedHostFs, path,
preserveUnc: true);
if (rc.IsFailure()) return rc;
fileSystem = subDirFs;
outFileSystem = Shared.Move(ref fileSystem);
return Result.Success;
}
finally
{
sharedHostFs?.Dispose();
subDirFs?.Dispose();
fileSystem?.Dispose();
tempFs?.Dispose();
}
}
private Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path path)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
var pathHost = new Path();
Result rc = pathHost.Initialize(in path);
if (rc.IsFailure()) return rc;
rc = FsCreators.TargetManagerFileSystemCreator.NormalizeCaseOfPath(out bool isSupported, ref pathHost);
if (rc.IsFailure()) return rc;
rc = FsCreators.TargetManagerFileSystemCreator.Create(out fileSystem, in pathHost, isSupported, false,
Result.Success);
if (rc.IsFailure()) return rc;
pathHost.Dispose();
return Result.Success;
}
public Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path path,
bool openCaseSensitive)
{
if (!path.IsEmpty() && openCaseSensitive)
{
Result rc = OpenHostFileSystem(out fileSystem, in path);
if (rc.IsFailure()) return rc;
}
else
{
Result rc = FsCreators.TargetManagerFileSystemCreator.Create(out fileSystem, in path, openCaseSensitive,
false, Result.Success);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{
SdEncryptionSeed = seed;

View File

@ -11,6 +11,8 @@ using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFileSf = LibHac.FsSrv.Sf.IFile;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
using Path = LibHac.Fs.Path;
using static LibHac.Fs.StringTraits;
namespace LibHac.FsSrv
{
@ -525,25 +527,47 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
ReferenceCountedDisposable<IFileSystem> hostFs = null;
ReferenceCountedDisposable<IFileSystem> hostFileSystem = null;
try
{
rc = FsProxyCore.OpenHostFileSystem(out hostFs, new U8Span(path.Str),
option.Flags.HasFlag(MountHostOptionFlag.PseudoCaseSensitive));
var pathNormalized = new Path();
if (path.Str.At(0) == DirectorySeparator && path.Str.At(1) != DirectorySeparator)
{
rc = pathNormalized.Initialize(path.Str.Slice(1));
if (rc.IsFailure()) return rc;
}
else
{
rc = pathNormalized.InitializeWithReplaceUnc(path.Str);
if (rc.IsFailure()) return rc;
}
var flags = new PathFlags();
flags.AllowWindowsPath();
flags.AllowRelativePath();
flags.AllowEmptyPath();
rc = pathNormalized.Normalize(flags);
if (rc.IsFailure()) return rc;
bool isRootPath = path.Str[0] == 0;
bool isCaseSensitive = option.Flags.HasFlag(MountHostOptionFlag.PseudoCaseSensitive);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref hostFs, isRootPath);
rc = FsProxyCore.OpenHostFileSystem(out hostFileSystem, in pathNormalized, isCaseSensitive);
if (rc.IsFailure()) return rc;
if (fileSystem is null)
return ResultFs.AllocationMemoryFailedCreateShared.Log();
var adapterFlags = new PathFlags();
if (path.Str.At(0) == NullTerminator)
adapterFlags.AllowWindowsPath();
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref hostFileSystem, adapterFlags, false);
pathNormalized.Dispose();
return Result.Success;
}
finally
{
hostFs?.Dispose();
hostFileSystem?.Dispose();
}
}
@ -878,7 +902,7 @@ namespace LibHac.FsSrv
tempFs = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFs, storageFlag);
tempFs = AsynchronousAccessFileSystem.CreateShared(ref tempFs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFs, false);
return Result.Success;
}
@ -915,7 +939,7 @@ namespace LibHac.FsSrv
customFs = StorageLayoutTypeSetFileSystem.CreateShared(ref customFs, storageFlag);
customFs = AsynchronousAccessFileSystem.CreateShared(ref customFs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref customFs);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref customFs, false);
return Result.Success;
}

View File

@ -32,6 +32,7 @@ namespace LibHac.FsSrv
public StorageDeviceManagerFactoryGlobals StorageDeviceManagerFactory;
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
public MultiCommitManagerGlobals MultiCommitManager;
public LocationResolverSetGlobals LocationResolverSet;
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
{
@ -40,6 +41,7 @@ namespace LibHac.FsSrv
SaveDataSharedFileStorage.Initialize(fsServer);
MultiCommitManager.Initialize();
LocationResolverSet.Initialize();
}
}

View File

@ -54,47 +54,13 @@ namespace LibHac.FsSrv.FsCreator
Config = config;
}
public Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
if (!IsValidPartitionId(partitionId)) return ResultFs.InvalidArgument.Log();
if (rootPath == null) return ResultFs.NullptrArgument.Log();
if (Config.TryGetFileSystem(out fileSystem, partitionId))
{
return Result.Success;
}
if (Config.RootFileSystem == null)
{
return ResultFs.PreconditionViolation.Log();
}
string partitionPath = GetPartitionPath(partitionId);
Result rc =
Util.CreateSubFileSystem(out IFileSystem subFileSystem, Config.RootFileSystem, partitionPath, true);
if (rc.IsFailure()) return rc;
if (rootPath == string.Empty)
{
fileSystem = subFileSystem;
return Result.Success;
}
return Util.CreateSubFileSystemImpl(out fileSystem, subFileSystem, rootPath);
}
// Todo: Make case sensitive
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
BisPartitionId partitionId, bool caseSensitive)
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, BisPartitionId partitionId,
bool caseSensitive)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
if (!IsValidPartitionId(partitionId)) return ResultFs.InvalidArgument.Log();
if (rootPath.IsNull()) return ResultFs.NullptrArgument.Log();
if (Config.TryGetFileSystem(out IFileSystem fs, partitionId))
{
@ -107,7 +73,9 @@ namespace LibHac.FsSrv.FsCreator
return ResultFs.PreconditionViolation.Log();
}
var partitionPath = GetPartitionPath(partitionId).ToU8String();
var bisRootPath = new Path();
Result rc = bisRootPath.Initialize(GetPartitionPath(partitionId).ToU8String());
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> partitionFileSystem = null;
ReferenceCountedDisposable<IFileSystem> sharedRootFs = null;
@ -115,17 +83,11 @@ namespace LibHac.FsSrv.FsCreator
{
sharedRootFs = new ReferenceCountedDisposable<IFileSystem>(Config.RootFileSystem);
Result rc = Utility.WrapSubDirectory(out partitionFileSystem, ref sharedRootFs, partitionPath, true);
rc = Utility.WrapSubDirectory(out partitionFileSystem, ref sharedRootFs, in bisRootPath, true);
if (rc.IsFailure()) return rc;
if (rootPath.IsEmpty())
{
Shared.Move(out fileSystem, ref partitionFileSystem);
return Result.Success;
}
return Utility.CreateSubDirectoryFileSystem(out fileSystem, ref partitionFileSystem, rootPath);
Shared.Move(out fileSystem, ref partitionFileSystem);
return Result.Success;
}
finally
{
@ -134,13 +96,7 @@ namespace LibHac.FsSrv.FsCreator
}
}
public Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
return ResultFs.NotImplemented.Log();
}
public Result SetBisRootForHost(BisPartitionId partitionId, string rootPath)
public Result SetBisRoot(BisPartitionId partitionId, string rootPath)
{
return Config.SetPath(rootPath, partitionId);
}

View File

@ -2,64 +2,98 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Impl;
using LibHac.Util;
namespace LibHac.FsSrv.FsCreator
{
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator, IDisposable
{
private const string DefaultPath = "/sdcard";
private EmulatedSdCard SdCard { get; }
private IFileSystem RootFileSystem { get; }
private ReferenceCountedDisposable<IFileSystem> _rootFileSystem;
private string Path { get; }
private IFileSystem SdCardFileSystem { get; set; }
private ReferenceCountedDisposable<IFileSystem> _sdCardFileSystem;
public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
{
SdCard = sdCard;
RootFileSystem = rootFileSystem;
_rootFileSystem = new ReferenceCountedDisposable<IFileSystem>(rootFileSystem);
}
public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
{
SdCard = sdCard;
RootFileSystem = rootFileSystem;
_rootFileSystem = new ReferenceCountedDisposable<IFileSystem>(rootFileSystem);
Path = path;
}
public Result Create(out IFileSystem fileSystem, bool isCaseSensitive)
public void Dispose()
{
UnsafeHelpers.SkipParamInit(out fileSystem);
if (_rootFileSystem is not null)
{
_rootFileSystem.Dispose();
_rootFileSystem = null;
}
if (_sdCardFileSystem is not null)
{
_sdCardFileSystem.Dispose();
_sdCardFileSystem = null;
}
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> outFileSystem, bool isCaseSensitive)
{
UnsafeHelpers.SkipParamInit(out outFileSystem);
if (!SdCard.IsSdCardInserted())
{
return ResultFs.PortSdCardNoDevice.Log();
}
if (SdCardFileSystem != null)
if (_sdCardFileSystem is not null)
{
fileSystem = SdCardFileSystem;
outFileSystem = _sdCardFileSystem.AddReference();
return Result.Success;
}
if (RootFileSystem == null)
if (_rootFileSystem is null)
{
return ResultFs.PreconditionViolation.Log();
}
string path = Path ?? DefaultPath;
// Todo: Add ProxyFileSystem?
Result rc = Util.CreateSubFileSystem(out IFileSystem sdFileSystem, RootFileSystem, path, true);
var sdCardPath = new Path();
Result rc = sdCardPath.Initialize(StringUtils.StringToUtf8(path));
if (rc.IsFailure()) return rc;
SdCardFileSystem = sdFileSystem;
fileSystem = sdFileSystem;
var pathFlags = new PathFlags();
pathFlags.AllowEmptyPath();
rc = sdCardPath.Normalize(pathFlags);
if (rc.IsFailure()) return rc;
return Result.Success;
// Todo: Add ProxyFileSystem?
ReferenceCountedDisposable<IFileSystem> tempFs = null;
try
{
tempFs = _rootFileSystem.AddReference();
rc = Utility.CreateSubDirectoryFileSystem(out _sdCardFileSystem, ref tempFs, in sdCardPath);
if (rc.IsFailure()) return rc;
outFileSystem = _sdCardFileSystem.AddReference();
return Result.Success;
}
finally
{
tempFs?.Dispose();
}
}
public Result Format(bool removeFromFatFsCache)

View File

@ -3,6 +3,7 @@ using LibHac.Common.Keys;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using static LibHac.FsSrv.FsCreator.IEncryptedFileSystemCreator;
namespace LibHac.FsSrv.FsCreator
{
@ -15,12 +16,13 @@ namespace LibHac.FsSrv.FsCreator
KeySet = keySet;
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
EncryptedFsKeyId keyId, in EncryptionSeed encryptionSeed)
public Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, KeyId idIndex,
in EncryptionSeed encryptionSeed)
{
UnsafeHelpers.SkipParamInit(out encryptedFileSystem);
if (keyId < EncryptedFsKeyId.Save || keyId > EncryptedFsKeyId.CustomStorage)
if (idIndex < KeyId.Save || idIndex > KeyId.CustomStorage)
{
return ResultFs.InvalidArgument.Log();
}
@ -29,7 +31,8 @@ namespace LibHac.FsSrv.FsCreator
KeySet.SetSdSeed(encryptionSeed.Value);
// Todo: pass ReferenceCountedDisposable to AesXtsFileSystem
var fs = new AesXtsFileSystem(baseFileSystem, KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
var fs = new AesXtsFileSystem(baseFileSystem, KeySet.SdCardEncryptionKeys[(int)idIndex].DataRo.ToArray(),
0x4000);
encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;

View File

@ -1,15 +1,10 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator
{
public interface IBuiltInStorageFileSystemCreator
{
// Todo: Remove raw IFileSystem overload
Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId, bool caseSensitive);
Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId);
Result SetBisRootForHost(BisPartitionId partitionId, string rootPath);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, BisPartitionId partitionId, bool caseSensitive);
}
}

View File

@ -5,8 +5,15 @@ namespace LibHac.FsSrv.FsCreator
{
public interface IEncryptedFileSystemCreator
{
public enum KeyId
{
Save = 0,
Content = 1,
CustomStorage = 2
}
Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem,
ReferenceCountedDisposable<IFileSystem> baseFileSystem, EncryptedFsKeyId keyId,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, KeyId idIndex,
in EncryptionSeed encryptionSeed);
}

View File

@ -4,7 +4,7 @@ namespace LibHac.FsSrv.FsCreator
{
public interface ISdCardProxyFileSystemCreator
{
Result Create(out IFileSystem fileSystem, bool isCaseSensitive);
Result Create(out ReferenceCountedDisposable<IFileSystem> outFileSystem, bool isCaseSensitive);
/// <summary>
/// Formats the SD card.

View File

@ -1,11 +1,10 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator
{
public interface ISubDirectoryFileSystemCreator
{
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path);
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc);
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path path);
}
}

View File

@ -1,4 +1,5 @@
using System;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator
@ -7,7 +8,7 @@ namespace LibHac.FsSrv.FsCreator
{
// Todo: Remove raw IFilesystem function
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive);
Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult);
Result NormalizeCaseOfPath(out bool isSupported, ref Path path);
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Common.Keys;
using LibHac.Diag;
@ -42,15 +43,20 @@ namespace LibHac.FsSrv.FsCreator
bool isJournalingSupported, bool isMultiCommitSupported, bool openReadOnly, bool openShared,
ISaveDataCommitTimeStampGetter timeStampGetter)
{
Span<byte> saveImageName = stackalloc byte[0x12];
// Hack around error CS8350.
Span<byte> buffer = stackalloc byte[0x12];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveImageNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, 0x12);
UnsafeHelpers.SkipParamInit(out fileSystem, out extraDataAccessor);
Assert.SdkRequiresNotNull(cacheManager);
var sb = new U8StringBuilder(saveImageName);
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
var saveImageName = new Path();
Result rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName, saveImageNameBuffer, saveDataId);
if (rc.IsFailure()) return rc;
Result rc = baseFileSystem.Target.GetEntryType(out DirectoryEntryType type, new U8Span(saveImageName));
rc = baseFileSystem.Target.GetEntryType(out DirectoryEntryType type, in saveImageName);
if (rc.IsFailure())
{
@ -68,7 +74,7 @@ namespace LibHac.FsSrv.FsCreator
{
subDirFs = new SubdirectoryFileSystem(ref baseFileSystem);
rc = subDirFs.Initialize(new U8Span(saveImageName));
rc = subDirFs.Initialize(in saveImageName);
if (rc.IsFailure()) return rc;
saveFs = DirectorySaveDataFileSystem.CreateShared(Shared.Move(ref subDirFs), _fsServer.Hos.Fs);
@ -80,6 +86,7 @@ namespace LibHac.FsSrv.FsCreator
fileSystem = saveFs.AddReference<IFileSystem>();
extraDataAccessor = saveFs.AddReference<ISaveDataExtraDataAccessor>();
saveImageName.Dispose();
return Result.Success;
}
finally

View File

@ -1,4 +1,5 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
@ -7,29 +8,31 @@ namespace LibHac.FsSrv.FsCreator
public class SubDirectoryFileSystemCreator : ISubDirectoryFileSystemCreator
{
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
{
return Create(out subDirFileSystem, ref baseFileSystem, path, false);
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc)
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path path)
{
UnsafeHelpers.SkipParamInit(out subDirFileSystem);
// Verify the sub-path exists
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, in path, OpenDirectoryMode.Directory);
if (rc.IsFailure()) return rc;
// Initialize the SubdirectoryFileSystem
var subDir = new SubdirectoryFileSystem(ref baseFileSystem, preserveUnc);
using var subDirShared = new ReferenceCountedDisposable<SubdirectoryFileSystem>(subDir);
dir.Dispose();
rc = subDirShared.Target.Initialize(path);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<SubdirectoryFileSystem> subFs = null;
try
{
subFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(
new SubdirectoryFileSystem(ref baseFileSystem));
subDirFileSystem = subDirShared.AddReference<IFileSystem>();
return Result.Success;
rc = subFs.Target.Initialize(in path);
if (rc.IsFailure()) return rc;
subDirFileSystem = subFs.AddReference<IFileSystem>();
return Result.Success;
}
finally
{
subFs?.Dispose();
}
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator
@ -10,12 +11,12 @@ namespace LibHac.FsSrv.FsCreator
throw new NotImplementedException();
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult)
{
throw new NotImplementedException();
}
public Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path)
public Result NormalizeCaseOfPath(out bool isSupported, ref Path path)
{
throw new NotImplementedException();
}

View File

@ -283,8 +283,8 @@ namespace LibHac.FsSrv.Impl
}
}
public static ReferenceCountedDisposable<IFileSystemSf> CreateShared(PathFlags flags,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool allowAllOperations)
public static ReferenceCountedDisposable<IFileSystemSf> CreateShared(
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, PathFlags flags, bool allowAllOperations)
{
ReferenceCountedDisposable<FileSystemInterfaceAdapter> sharedAdapter = null;
try

View File

@ -1,8 +1,8 @@
using System;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSystem;
using LibHac.Util;
@ -10,130 +10,24 @@ namespace LibHac.FsSrv.Impl
{
internal static class Utility
{
public static Result EnsureDirectory(IFileSystem fileSystem, U8Span path)
public static bool IsHostFsMountName(ReadOnlySpan<byte> name)
{
FsPath.FromSpan(out FsPath pathBuffer, path.Value).IgnoreResult();
int pathLength = StringUtils.GetLength(path);
if (pathLength > 0)
{
// Remove any trailing directory separators
while (pathLength > 0 && path[pathLength - 1] == StringTraits.DirectorySeparator)
{
pathLength--;
}
// Copy the path to a mutable buffer
path.Value.Slice(0, pathLength).CopyTo(pathBuffer.Str);
}
pathBuffer.Str[pathLength] = StringTraits.NullTerminator;
return EnsureDirectoryImpl(fileSystem, pathBuffer.Str.Slice(0, pathLength));
}
private static Result EnsureDirectoryImpl(IFileSystem fileSystem, Span<byte> path)
{
// Double check the trailing separators have been trimmed
Assert.SdkRequires(path.Length <= 1 || path[path.Length - 1] != StringTraits.DirectorySeparator);
// Use the root path if the input path is empty
var pathToCheck = new U8Span(path.IsEmpty ? FileSystemRootPath : path);
// Check if the path exists
Result rc = fileSystem.GetEntryType(out DirectoryEntryType entryType, pathToCheck);
if (rc.IsFailure())
{
// Something went wrong if we get a result other than PathNotFound
if (!ResultFs.PathNotFound.Includes(rc))
return rc;
if (path.Length <= 0)
{
// The file system either reported that the root directory doesn't exist,
// or the input path had a negative length
return ResultFs.PathNotFound.Log();
}
// The path does not exist. Ensure its parent directory exists
rc = EnsureParentDirectoryImpl(fileSystem, path);
if (rc.IsFailure()) return rc;
// The parent directory exists, we can now create a directory at the input path
rc = fileSystem.CreateDirectory(new U8Span(path));
if (rc.IsSuccess())
{
// The directory was successfully created
return Result.Success;
}
if (!ResultFs.PathAlreadyExists.Includes(rc))
return rc;
// Someone else created a file system entry at the input path after we checked
// if the path existed. Get the entry type to check if it's a directory.
rc = fileSystem.GetEntryType(out entryType, new U8Span(path));
if (rc.IsFailure()) return rc;
}
// We want the entry that exists at the input path to be a directory
// Return PathAlreadyExists if it's a file
if (entryType == DirectoryEntryType.File)
return ResultFs.PathAlreadyExists.Log();
// A directory exists at the input path. Success
return Result.Success;
}
private static Result EnsureParentDirectoryImpl(IFileSystem fileSystem, Span<byte> path)
{
// The path should not be empty or have a trailing directory separator
Assert.SdkRequiresLess(0, path.Length);
Assert.SdkRequiresNotEqual(path[path.Length - 1], StringTraits.DirectorySeparator);
// Make sure the path's not too long
if (path.Length > PathTool.EntryNameLengthMax)
return ResultFs.TooLongPath.Log();
// Iterate until we run out of path or find the next separator
int length = path.Length - 1;
while (length > 0 && path[length] != StringTraits.DirectorySeparator)
{
length--;
}
if (length == 0)
{
// We hit the beginning of the path. Ensure the root directory exists and return
return EnsureDirectoryImpl(fileSystem, Span<byte>.Empty);
}
// We found the length of the parent directory. Ensure it exists
path[length] = StringTraits.NullTerminator;
Result rc = EnsureDirectoryImpl(fileSystem, path.Slice(0, length));
if (rc.IsFailure()) return rc;
// Restore the separator
path[length] = StringTraits.DirectorySeparator;
return Result.Success;
return StringUtils.Compare(name, CommonMountNames.HostRootFileSystemMountName) == 0;
}
public static Result CreateSubDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path subPath)
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path rootPath)
{
UnsafeHelpers.SkipParamInit(out subDirFileSystem);
if (subPath.IsEmpty())
if (rootPath.IsEmpty())
{
subDirFileSystem = Shared.Move(ref baseFileSystem);
return Result.Success;
}
// Check if the directory exists
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, subPath, OpenDirectoryMode.Directory);
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, rootPath, OpenDirectoryMode.Directory);
if (rc.IsFailure()) return rc;
dir.Dispose();
@ -141,7 +35,7 @@ namespace LibHac.FsSrv.Impl
var fs = new SubdirectoryFileSystem(ref baseFileSystem);
using (var subDirFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(fs))
{
rc = subDirFs.Target.Initialize(in subPath);
rc = subDirFs.Target.Initialize(in rootPath);
if (rc.IsFailure()) return rc;
subDirFileSystem = subDirFs.AddReference<IFileSystem>();
@ -150,22 +44,22 @@ namespace LibHac.FsSrv.Impl
}
public static Result WrapSubDirectory(out ReferenceCountedDisposable<IFileSystem> fileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool createIfMissing)
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path rootPath, bool createIfMissing)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
// The path must already exist if we're not automatically creating it
if (!createIfMissing)
{
Result result = baseFileSystem.Target.GetEntryType(out _, path);
Result result = baseFileSystem.Target.GetEntryType(out _, in rootPath);
if (result.IsFailure()) return result;
}
// Ensure the path exists or check if it's a directory
Result rc = EnsureDirectory(baseFileSystem.Target, path);
Result rc = Utility12.EnsureDirectory(baseFileSystem.Target, in rootPath);
if (rc.IsFailure()) return rc;
return CreateSubDirectoryFileSystem(out fileSystem, ref baseFileSystem, path);
return CreateSubDirectoryFileSystem(out fileSystem, ref baseFileSystem, rootPath);
}
public static long ConvertZeroCommitId(in SaveDataExtraData extraData)
@ -178,11 +72,5 @@ namespace LibHac.FsSrv.Impl
Crypto.Sha256.GenerateSha256Hash(SpanHelpers.AsReadOnlyByteSpan(in extraData), hash);
return BitConverter.ToInt64(hash);
}
private static ReadOnlySpan<byte> FileSystemRootPath => // /
new[]
{
(byte) '/'
};
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
@ -14,8 +13,9 @@ using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IStorage = LibHac.Fs.IStorage;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
using Path = LibHac.Lr.Path;
using Path = LibHac.Fs.Path;
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv
{
@ -72,10 +72,17 @@ namespace LibHac.FsSrv
if (fsType != FileSystemProxyType.Manual)
{
if (fsType == FileSystemProxyType.Logo || fsType == FileSystemProxyType.Control)
return ResultFs.NotImplemented.Log();
else
return ResultFs.InvalidArgument.Log();
switch (fsType)
{
case FileSystemProxyType.Logo:
case FileSystemProxyType.Control:
case FileSystemProxyType.Meta:
case FileSystemProxyType.Data:
case FileSystemProxyType.Package:
return ResultFs.NotImplemented.Log();
default:
return ResultFs.InvalidArgument.Log();
}
}
Accessibility accessibility =
@ -89,28 +96,17 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc;
// Try to find the path to the original version of the file system
Result originalResult = ServiceImpl.ResolveApplicationHtmlDocumentPath(out Path originalPath,
new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId);
var originalPath = new Fs.Path();
Result originalResult = ServiceImpl.ResolveApplicationHtmlDocumentPath(out bool isDirectory,
ref originalPath, new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId);
// The file system might have a patch version with no original version, so continue if not found
if (originalResult.IsFailure() && !ResultLr.HtmlDocumentNotFound.Includes(originalResult))
return originalResult;
// Use a separate bool because ref structs can't be used as type parameters
bool originalPathNormalizerHasValue = false;
PathNormalizer originalPathNormalizer = default;
// Normalize the original version path if found
if (originalResult.IsSuccess())
{
originalPathNormalizer = new PathNormalizer(originalPath, GetPathNormalizerOptions(originalPath));
if (originalPathNormalizer.Result.IsFailure()) return originalPathNormalizer.Result;
originalPathNormalizerHasValue = true;
}
// Try to find the path to the patch file system
Result patchResult = ServiceImpl.ResolveRegisteredHtmlDocumentPath(out Path patchPath, programId.Value);
var patchPath = new Fs.Path();
Result patchResult = ServiceImpl.ResolveRegisteredHtmlDocumentPath(ref patchPath, programId.Value);
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager = null;
@ -122,37 +118,25 @@ namespace LibHac.FsSrv
if (originalResult.IsFailure())
return originalResult;
Assert.SdkAssert(originalPathNormalizerHasValue);
// There is an original version and no patch version. Open the original directly
rc = ServiceImpl.OpenFileSystem(out tempFileSystem, originalPathNormalizer.Path, fsType, programId.Value);
rc = ServiceImpl.OpenFileSystem(out tempFileSystem, in originalPath, fsType, programId.Value,
isDirectory);
if (rc.IsFailure()) return rc;
}
else
{
// Get the normalized path to the original file system
U8Span normalizedOriginalPath;
if (originalPathNormalizerHasValue)
{
normalizedOriginalPath = originalPathNormalizer.Path;
}
else
{
normalizedOriginalPath = U8Span.Empty;
}
// Normalize the path to the patch file system
using var patchPathNormalizer = new PathNormalizer(patchPath, GetPathNormalizerOptions(patchPath));
if (patchPathNormalizer.Result.IsFailure()) return patchPathNormalizer.Result;
if (patchResult.IsFailure())
return patchResult;
U8Span normalizedPatchPath = patchPathNormalizer.Path;
var emptyPath = new Fs.Path();
rc = emptyPath.InitializeAsEmpty();
if (rc.IsFailure()) return rc;
ref Fs.Path originalNcaPath = ref originalResult.IsSuccess() ? ref originalPath : ref emptyPath;
// Open the file system using both the original and patch versions
rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, normalizedOriginalPath,
normalizedPatchPath, fsType, programId.Value);
rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, in originalNcaPath, in patchPath,
fsType, programId.Value);
if (rc.IsFailure()) return rc;
}
@ -163,7 +147,7 @@ namespace LibHac.FsSrv
accessFailureManager = SelfReference.AddReference<IRomFileSystemAccessFailureManager>();
tempFileSystem = DeepRetryFileSystem.CreateShared(ref tempFileSystem, ref accessFailureManager);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem, false);
return Result.Success;
}
finally
@ -200,10 +184,10 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path,
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> outFileSystem, in FspPath path,
ulong id, FileSystemProxyType fsType)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
UnsafeHelpers.SkipParamInit(out outFileSystem);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
@ -251,25 +235,34 @@ namespace LibHac.FsSrv
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
using var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
if (normalizer.Result.IsFailure()) return normalizer.Result;
var pathNormalized = new Fs.Path();
rc = pathNormalized.InitializeWithReplaceUnc(path.Str);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> fs = null;
var pathFlags = new PathFlags();
pathFlags.AllowWindowsPath();
pathFlags.AllowMountName();
rc = pathNormalized.Normalize(pathFlags);
if (rc.IsFailure()) return rc;
bool isDirectory = PathUtility12.IsDirectoryPath(in path);
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
rc = ServiceImpl.OpenFileSystem(out fs, out _, path, fsType,
canMountSystemDataPrivate, id);
rc = ServiceImpl.OpenFileSystem(out fileSystem, in pathNormalized, fsType, canMountSystemDataPrivate,
id, isDirectory);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
outFileSystem = FileSystemInterfaceAdapter.CreateShared(ref fileSystem, false);
return Result.Success;
}
finally
{
fs?.Dispose();
fileSystem?.Dispose();
}
}
@ -316,7 +309,7 @@ namespace LibHac.FsSrv
if (tempFileSystem is null)
return ResultFs.AllocationMemoryFailedAllocateShared.Log();
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem, false);
if (fileSystem is null)
return ResultFs.AllocationMemoryFailedCreateShared.Log();
@ -334,11 +327,11 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId)
public Result GetRightsId(out RightsId outRightsId, ProgramId programId, StorageId storageId)
{
UnsafeHelpers.SkipParamInit(out rightsId);
UnsafeHelpers.SkipParamInit(out outRightsId);
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.All);
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageType.All);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
@ -346,24 +339,27 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.GetRightsId))
return ResultFs.PermissionDenied.Log();
rc = ServiceImpl.ResolveProgramPath(out Path programPath, programId, storageId);
var programPath = new Path();
rc = ServiceImpl.ResolveProgramPath(out bool isDirectory, ref programPath, programId, storageId);
if (rc.IsFailure()) return rc;
using var normalizer = new PathNormalizer(programPath, GetPathNormalizerOptions(programPath));
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (isDirectory)
return ResultFs.TargetNotFound.Log();
rc = ServiceImpl.GetRightsId(out RightsId tempRightsId, out _, normalizer.Path, programId);
rc = ServiceImpl.GetRightsId(out RightsId rightsId, out _, in programPath, programId);
if (rc.IsFailure()) return rc;
rightsId = tempRightsId;
outRightsId = rightsId;
programPath.Dispose();
return Result.Success;
}
public Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path)
public Result GetRightsIdAndKeyGenerationByPath(out RightsId outRightsId, out byte outKeyGeneration, in FspPath path)
{
UnsafeHelpers.SkipParamInit(out rightsId, out keyGeneration);
UnsafeHelpers.SkipParamInit(out outRightsId, out outKeyGeneration);
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.All);
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageType.All);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
@ -371,52 +367,61 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.GetRightsId))
return ResultFs.PermissionDenied.Log();
using var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
if (normalizer.Result.IsFailure()) return normalizer.Result;
var pathNormalized = new Path();
rc = pathNormalized.Initialize(path.Str);
if (rc.IsFailure()) return rc;
rc = ServiceImpl.GetRightsId(out RightsId tempRightsId, out byte tempKeyGeneration, normalizer.Path,
var pathFlags = new PathFlags();
pathFlags.AllowWindowsPath();
pathFlags.AllowMountName();
rc = pathNormalized.Normalize(pathFlags);
if (rc.IsFailure()) return rc;
if (PathUtility12.IsDirectoryPath(in path))
return ResultFs.TargetNotFound.Log();
rc = ServiceImpl.GetRightsId(out RightsId rightsId, out byte keyGeneration, in pathNormalized,
new ProgramId(ulong.MaxValue));
if (rc.IsFailure()) return rc;
rightsId = tempRightsId;
keyGeneration = tempKeyGeneration;
outRightsId = rightsId;
outKeyGeneration = keyGeneration;
pathNormalized.Dispose();
return Result.Success;
}
private Result OpenDataFileSystemCore(out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool isHostFs,
private Result OpenDataFileSystemCore(out ReferenceCountedDisposable<IFileSystem> outFileSystem, out bool isHostFs,
ulong programId, StorageId storageId)
{
UnsafeHelpers.SkipParamInit(out fileSystem, out isHostFs);
UnsafeHelpers.SkipParamInit(out outFileSystem, out isHostFs);
if (Unsafe.IsNullRef(ref isHostFs))
return ResultFs.NullptrArgument.Log();
StorageType storageFlag = ServiceImpl.GetStorageFlag(programId);
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result rc = ServiceImpl.ResolveRomPath(out Path romPath, programId, storageId);
var programPath = new Path();
Result rc = ServiceImpl.ResolveRomPath(out bool isDirectory, ref programPath, programId, storageId);
if (rc.IsFailure()) return rc;
using var normalizer = new PathNormalizer(romPath, GetPathNormalizerOptions(romPath));
if (normalizer.Result.IsFailure()) return normalizer.Result;
isHostFs = Utility.IsHostFsMountName(programPath.GetString());
isHostFs = IsHostFs(romPath);
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
rc = ServiceImpl.OpenDataFileSystem(out tempFileSystem, normalizer.Path, FileSystemProxyType.Rom,
programId);
rc = ServiceImpl.OpenDataFileSystem(out fileSystem, in programPath, FileSystemProxyType.Rom, programId, isDirectory);
if (rc.IsFailure()) return rc;
tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag);
fileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref fileSystem, storageFlag);
Shared.Move(out fileSystem, ref tempFileSystem);
outFileSystem = Shared.Move(ref fileSystem);
return Result.Success;
}
finally
{
tempFileSystem?.Dispose();
fileSystem?.Dispose();
}
}
@ -448,7 +453,7 @@ namespace LibHac.FsSrv
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem, false);
return Result.Success;
}
@ -499,13 +504,11 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.RegisterUpdatePartition))
return ResultFs.PermissionDenied.Log();
rc = ServiceImpl.ResolveRomPath(out Path romPath, programInfo.ProgramIdValue, programInfo.StorageId);
var programPath = new Path();
rc = ServiceImpl.ResolveRomPath(out _, ref programPath, programInfo.ProgramIdValue, programInfo.StorageId);
if (rc.IsFailure()) return rc;
using var normalizer = new PathNormalizer(romPath, GetPathNormalizerOptions(romPath));
if (normalizer.Result.IsFailure()) return normalizer.Result;
return ServiceImpl.RegisterUpdatePartition(programInfo.ProgramIdValue, normalizer.Path);
return ServiceImpl.RegisterUpdatePartition(programInfo.ProgramIdValue, in programPath);
}
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
@ -537,7 +540,7 @@ namespace LibHac.FsSrv
if (tempFileSystem is null)
return ResultFs.AllocationMemoryFailedAllocateShared.Log();
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem, false);
if (fileSystem is null)
return ResultFs.AllocationMemoryFailedCreateShared.Log();
@ -620,21 +623,6 @@ namespace LibHac.FsSrv
return ServiceImpl.GetProgramInfoByProgramId(out programInfo, programId);
}
private PathNormalizer.Option GetPathNormalizerOptions(U8Span path)
{
// Set the PreserveUnc flag if the path is on the host file system
PathNormalizer.Option hostOption = IsHostFs(path) ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
return PathNormalizer.Option.HasMountName | PathNormalizer.Option.PreserveTrailingSeparator | hostOption;
}
private bool IsHostFs(U8Span path)
{
int hostMountLength = StringUtils.GetLength(CommonPaths.HostRootFileSystemMountName,
PathTools.MountNameLengthMax);
return StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountLength) == 0;
}
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage,
out Hash ncaHeaderDigest, ulong id, StorageId storageId)
{

View File

@ -1,6 +1,7 @@
using System;
using System.Buffers.Text;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
@ -13,8 +14,6 @@ using LibHac.Os;
using LibHac.Spl;
using LibHac.Util;
using RightsId = LibHac.Fs.RightsId;
using Utility = LibHac.FsSrv.Impl.Utility;
using PathLr = LibHac.Lr.Path;
namespace LibHac.FsSrv
{
@ -65,15 +64,23 @@ namespace LibHac.FsSrv
public bool CanMountNca;
}
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path,
FileSystemProxyType type, ulong id)
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path path,
FileSystemProxyType type, ulong id, bool isDirectory)
{
return OpenFileSystem(out fileSystem, out Unsafe.NullRef<CodeVerificationData>(), path, type, false, id);
return OpenFileSystem(out fileSystem, out Unsafe.NullRef<CodeVerificationData>(), in path, type, false, id,
isDirectory);
}
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path path,
FileSystemProxyType type, bool canMountSystemDataPrivate, ulong id, bool isDirectory)
{
return OpenFileSystem(out fileSystem, out Unsafe.NullRef<CodeVerificationData>(), in path, type,
canMountSystemDataPrivate, id, isDirectory);
}
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
out CodeVerificationData verificationData, U8Span path, FileSystemProxyType type,
bool canMountSystemDataPrivate, ulong id)
out CodeVerificationData verificationData, in Path path, FileSystemProxyType type,
bool canMountSystemDataPrivate, ulong id, bool isDirectory)
{
UnsafeHelpers.SkipParamInit(out fileSystem, out verificationData);
@ -81,7 +88,7 @@ namespace LibHac.FsSrv
verificationData.IsValid = false;
// Get a reference to the path that will be advanced as each part of the path is parsed
U8Span currentPath = path.Slice(0, StringUtils.GetLength(path));
var currentPath = new U8Span(path.GetString());
// Open the root filesystem based on the path's mount name
Result rc = ParseMountName(ref currentPath,
@ -106,7 +113,7 @@ namespace LibHac.FsSrv
return rc;
}
rc = CheckDirOrNcaOrNsp(ref currentPath, out bool isDirectory);
rc = CheckDirOrNcaOrNsp(ref currentPath, out isDirectory);
if (rc.IsFailure()) return rc;
if (isDirectory)
@ -114,17 +121,21 @@ namespace LibHac.FsSrv
if (!mountNameInfo.IsHostFs)
return ResultFs.PermissionDenied.Log();
var directoryPath = new Path();
rc = directoryPath.InitializeWithNormalization(currentPath.Value);
if (rc.IsFailure()) return rc;
if (type == FileSystemProxyType.Manual)
{
ReferenceCountedDisposable<IFileSystem> manualFileSystem = null;
ReferenceCountedDisposable<IFileSystem> hostFileSystem = null;
ReferenceCountedDisposable<IFileSystem> readOnlyFileSystem = null;
try
{
rc = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(out manualFileSystem, ref baseFileSystem,
currentPath);
rc = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(out hostFileSystem,
in directoryPath);
if (rc.IsFailure()) return rc;
readOnlyFileSystem = ReadOnlyFileSystem.CreateShared(ref manualFileSystem);
readOnlyFileSystem = ReadOnlyFileSystem.CreateShared(ref hostFileSystem);
if (readOnlyFileSystem?.Target is null)
return ResultFs.AllocationMemoryFailedAllocateShared.Log();
@ -133,12 +144,12 @@ namespace LibHac.FsSrv
}
finally
{
manualFileSystem?.Dispose();
hostFileSystem?.Dispose();
readOnlyFileSystem?.Dispose();
}
}
return ParseDir(currentPath, out fileSystem, ref baseFileSystem, type, true);
return ParseDir(in directoryPath, out fileSystem, ref baseFileSystem, type, true);
}
rc = ParseNsp(ref currentPath, out ReferenceCountedDisposable<IFileSystem> nspFileSystem, baseFileSystem);
@ -146,7 +157,7 @@ namespace LibHac.FsSrv
if (rc.IsSuccess())
{
// Must be the end of the path to open Application Package FS type
if (currentPath.Length == 0 || currentPath[0] == 0)
if (currentPath.Value.At(0) == 0)
{
if (type == FileSystemProxyType.Package)
{
@ -185,28 +196,28 @@ namespace LibHac.FsSrv
}
}
public Result OpenDataFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path,
FileSystemProxyType fsType, ulong programId)
public Result OpenDataFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path path,
FileSystemProxyType fsType, ulong programId, bool isDirectory)
{
throw new NotImplementedException();
}
public Result OpenStorageWithPatch(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
U8Span originalNcaPath, U8Span currentNcaPath, FileSystemProxyType fsType, ulong id)
in Path originalNcaPath, in Path currentNcaPath, FileSystemProxyType fsType, ulong id)
{
throw new NotImplementedException();
}
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
U8Span originalNcaPath, U8Span currentNcaPath, FileSystemProxyType fsType, ulong id)
in Path originalNcaPath, in Path currentNcaPath, FileSystemProxyType fsType, ulong id)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
ReferenceCountedDisposable<IStorage> romFsStorage = null;
try
{
Result rc = OpenStorageWithPatch(out romFsStorage, out Unsafe.NullRef<Hash>(), originalNcaPath,
currentNcaPath, fsType, id);
Result rc = OpenStorageWithPatch(out romFsStorage, out Unsafe.NullRef<Hash>(), in originalNcaPath,
in currentNcaPath, fsType, id);
if (rc.IsFailure()) return rc;
return _config.RomFsCreator.Create(out fileSystem, romFsStorage);
@ -220,13 +231,14 @@ namespace LibHac.FsSrv
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
ContentStorageId contentStorageId)
{
const int storagePathMaxLen = 0x40;
const int pathBufferLength = 0x40;
UnsafeHelpers.SkipParamInit(out fileSystem);
ReferenceCountedDisposable<IFileSystem> baseFileSystem = null;
ReferenceCountedDisposable<IFileSystem> subDirFileSystem = null;
ReferenceCountedDisposable<IFileSystem> encryptedFileSystem = null;
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
Path contentStoragePath = default;
try
{
Result rc;
@ -235,62 +247,62 @@ namespace LibHac.FsSrv
switch (contentStorageId)
{
case ContentStorageId.System:
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
BisPartitionId.System);
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, BisPartitionId.System);
if (rc.IsFailure()) return rc;
break;
case ContentStorageId.User:
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
BisPartitionId.User);
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, BisPartitionId.User);
if (rc.IsFailure()) return rc;
break;
case ContentStorageId.SdCard:
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out baseFileSystem);
if (rc.IsFailure()) return rc;
break;
default:
rc = ResultFs.InvalidArgument.Log();
break;
return ResultFs.InvalidArgument.Log();
}
if (rc.IsFailure()) return rc;
// Hack around error CS8350.
Span<byte> buffer = stackalloc byte[pathBufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> contentStoragePathBuffer = MemoryMarshal.CreateSpan(ref bufferRef, pathBufferLength);
// Build the appropriate path for the content storage ID
Span<byte> contentStoragePath = stackalloc byte[storagePathMaxLen];
if (contentStorageId == ContentStorageId.SdCard)
{
var sb = new U8StringBuilder(contentStoragePath);
var sb = new U8StringBuilder(contentStoragePathBuffer);
sb.Append(StringTraits.DirectorySeparator).Append(SdCardNintendoRootDirectoryName);
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
}
else
{
var sb = new U8StringBuilder(contentStoragePath);
var sb = new U8StringBuilder(contentStoragePathBuffer);
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
}
contentStoragePath = new Path();
rc = PathFunctions.SetUpFixedPath(ref contentStoragePath, contentStoragePathBuffer);
if (rc.IsFailure()) return rc;
// Make sure the content storage path exists
// ReSharper disable once PossibleNullReferenceException
rc = Utility.EnsureDirectory(baseFileSystem.Target, new U8Span(contentStoragePath));
rc = Utility12.EnsureDirectory(baseFileSystem.Target, in contentStoragePath);
if (rc.IsFailure()) return rc;
rc = _config.SubDirectoryFsCreator.Create(out subDirFileSystem, ref baseFileSystem,
new U8Span(contentStoragePath));
in contentStoragePath);
if (rc.IsFailure()) return rc;
// Only content on the SD card is encrypted
if (contentStorageId != ContentStorageId.SdCard)
if (contentStorageId == ContentStorageId.SdCard)
{
// Move the shared reference to the out variable
Shared.Move(out fileSystem, ref subDirFileSystem);
tempFileSystem = Shared.Move(ref subDirFileSystem);
return Result.Success;
rc = _config.EncryptedFsCreator.Create(out subDirFileSystem, ref tempFileSystem,
IEncryptedFileSystemCreator.KeyId.Content, in _encryptionSeed);
if (rc.IsFailure()) return rc;
}
// Create an encrypted file system for SD card content storage
rc = _config.EncryptedFsCreator.Create(out encryptedFileSystem, subDirFileSystem,
EncryptedFsKeyId.Content, in _encryptionSeed);
if (rc.IsFailure()) return rc;
Shared.Move(out fileSystem, ref encryptedFileSystem);
Shared.Move(out fileSystem, ref subDirFileSystem);
return Result.Success;
}
@ -298,11 +310,12 @@ namespace LibHac.FsSrv
{
baseFileSystem?.Dispose();
subDirFileSystem?.Dispose();
encryptedFileSystem?.Dispose();
tempFileSystem?.Dispose();
contentStoragePath.Dispose();
}
}
public Result GetRightsId(out RightsId rightsId, out byte keyGeneration, U8Span path, ProgramId programId)
public Result GetRightsId(out RightsId rightsId, out byte keyGeneration, in Path path, ProgramId programId)
{
throw new NotImplementedException();
}
@ -326,7 +339,7 @@ namespace LibHac.FsSrv
return Result.Success;
}
public Result RegisterUpdatePartition(ulong programId, U8Span path)
public Result RegisterUpdatePartition(ulong programId, in Path path)
{
throw new NotImplementedException();
}
@ -423,8 +436,7 @@ namespace LibHac.FsSrv
{
path = path.Slice(CommonPaths.BisCalibrationFilePartitionMountName.Length);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
BisPartitionId.CalibrationFile);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, BisPartitionId.CalibrationFile);
if (rc.IsFailure()) return rc;
}
@ -433,8 +445,7 @@ namespace LibHac.FsSrv
{
path = path.Slice(CommonPaths.BisSafeModePartitionMountName.Length);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
BisPartitionId.SafeMode);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, BisPartitionId.SafeMode);
if (rc.IsFailure()) return rc;
}
@ -443,7 +454,7 @@ namespace LibHac.FsSrv
{
path = path.Slice(CommonPaths.BisUserPartitionMountName.Length);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty, BisPartitionId.User);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, BisPartitionId.User);
if (rc.IsFailure()) return rc;
}
@ -452,8 +463,7 @@ namespace LibHac.FsSrv
{
path = path.Slice(CommonPaths.BisSystemPartitionMountName.Length);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
BisPartitionId.System);
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, BisPartitionId.System);
if (rc.IsFailure()) return rc;
}
@ -471,10 +481,14 @@ namespace LibHac.FsSrv
{
path = path.Slice(CommonPaths.HostRootFileSystemMountName.Length);
var rootPathEmpty = new Path();
Result rc = rootPathEmpty.InitializeAsEmpty();
if (rc.IsFailure()) return rc;
info.IsHostFs = true;
info.CanMountNca = true;
Result rc = OpenHostFileSystem(out fileSystem, U8Span.Empty, openCaseSensitive: false);
rc = OpenHostFileSystem(out fileSystem, in rootPathEmpty, openCaseSensitive: false);
if (rc.IsFailure()) return rc;
}
@ -540,7 +554,7 @@ namespace LibHac.FsSrv
return ResultFs.PathNotFound.Log();
}
private Result ParseDir(U8Span path,
private Result ParseDir(in Path path,
out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType, bool preserveUnc)
{
@ -549,7 +563,7 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
try
{
Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, path, preserveUnc);
Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, in path);
if (rc.IsFailure()) return rc;
return ParseContentTypeForDirectory(out contentFileSystem, ref subDirFs, fsType);
@ -561,32 +575,29 @@ namespace LibHac.FsSrv
}
private Result ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(
out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path path)
{
UnsafeHelpers.SkipParamInit(out contentFileSystem);
Unsafe.SkipInit(out FsPath fullPath);
UnsafeHelpers.SkipParamInit(out fileSystem);
var sb = new U8StringBuilder(fullPath.Str);
sb.Append(path)
.Append(new[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' });
var pathRoot = new Path();
var pathData = new Path();
if (sb.Overflowed)
return ResultFs.TooLongPath.Log();
Result rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool success, fullPath.Str);
Result rc = PathFunctions.SetUpFixedPath(ref pathData,
new[] { (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a' });
if (rc.IsFailure()) return rc;
// Reopen the host filesystem as case sensitive
if (success)
{
baseFileSystem.Dispose();
rc = pathRoot.Combine(in path, in pathData);
if (rc.IsFailure()) return rc;
rc = OpenHostFileSystem(out baseFileSystem, U8Span.Empty, openCaseSensitive: true);
if (rc.IsFailure()) return rc;
}
rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isSupported, ref pathRoot);
if (rc.IsFailure()) return rc;
return _config.SubDirectoryFsCreator.Create(out contentFileSystem, ref baseFileSystem, fullPath);
rc = _config.TargetManagerFsCreator.Create(out fileSystem, in pathRoot, isSupported, false, Result.Success);
if (rc.IsFailure()) return rc;
pathData.Dispose();
pathRoot.Dispose();
return Result.Success;
}
private Result ParseNsp(ref U8Span path, out ReferenceCountedDisposable<IFileSystem> fileSystem,
@ -710,17 +721,18 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
try
{
// Open the subdirectory filesystem
Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, new U8Span(dirName));
var directoryPath = new Path();
Result rc = PathFunctions.SetUpFixedPath(ref directoryPath, dirName);
if (rc.IsFailure()) return rc;
if (fsType == FileSystemProxyType.Code)
{
rc = _config.StorageOnNcaCreator.VerifyAcidSignature(subDirFs.Target, null);
if (rc.IsFailure()) return rc;
}
if (directoryPath.IsEmpty())
return ResultFs.InvalidArgument.Log();
Shared.Move(out fileSystem, ref subDirFs);
// Open the subdirectory filesystem
rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, in directoryPath);
if (rc.IsFailure()) return rc;
fileSystem = Shared.Move(ref subDirFs);
return Result.Success;
}
finally
@ -828,35 +840,36 @@ namespace LibHac.FsSrv
return Result.Success;
}
public Result ResolveProgramPath(out PathLr path, ProgramId programId, StorageId storageId)
public Result ResolveProgramPath(out bool isDirectory, ref Path path, ProgramId programId, StorageId storageId)
{
Result rc = _locationResolverSet.ResolveProgramPath(out path, programId.Value, storageId);
Result rc = _locationResolverSet.ResolveProgramPath(out isDirectory, ref path, programId, storageId);
if (rc.IsSuccess())
return Result.Success;
var dataId = new DataId(programId.Value);
isDirectory = false;
rc = _locationResolverSet.ResolveDataPath(out path, dataId, storageId);
rc = _locationResolverSet.ResolveDataPath(ref path, new DataId(programId.Value), storageId);
if (rc.IsSuccess())
return Result.Success;
return ResultFs.TargetNotFound.Log();
}
public Result ResolveRomPath(out PathLr path, ulong id, StorageId storageId)
public Result ResolveRomPath(out bool isDirectory, ref Path path, ulong id, StorageId storageId)
{
return _locationResolverSet.ResolveRomPath(out path, id, storageId);
return _locationResolverSet.ResolveRomPath(out isDirectory, ref path, new ProgramId(id), storageId);
}
public Result ResolveApplicationHtmlDocumentPath(out PathLr path, Ncm.ApplicationId applicationId,
StorageId storageId)
public Result ResolveApplicationHtmlDocumentPath(out bool isDirectory, ref Path path,
Ncm.ApplicationId applicationId, StorageId storageId)
{
return _locationResolverSet.ResolveApplicationHtmlDocumentPath(out path, applicationId, storageId);
return _locationResolverSet.ResolveApplicationHtmlDocumentPath(out isDirectory, ref path, applicationId,
storageId);
}
public Result ResolveRegisteredHtmlDocumentPath(out PathLr path, ulong id)
public Result ResolveRegisteredHtmlDocumentPath(ref Path path, ulong id)
{
return _locationResolverSet.ResolveRegisteredHtmlDocumentPath(out path, id);
return _locationResolverSet.ResolveRegisteredHtmlDocumentPath(ref path, id);
}
internal StorageType GetStorageFlag(ulong programId)
@ -909,48 +922,12 @@ namespace LibHac.FsSrv
_romFsRecoveredByInvalidateCacheCount = 0;
}
public Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path, bool openCaseSensitive)
public Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in Path rootPath, bool openCaseSensitive)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
Result rc;
if (!path.IsEmpty())
{
rc = Util.VerifyHostPath(path);
if (rc.IsFailure()) return rc;
}
ReferenceCountedDisposable<IFileSystem> hostFs = null;
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
try
{
rc = _config.TargetManagerFsCreator.Create(out hostFs, openCaseSensitive);
if (rc.IsFailure()) return rc;
if (path.IsEmpty())
{
ReadOnlySpan<byte> rootHostPath = new[] { (byte)'C', (byte)':', (byte)'/' };
rc = hostFs.Target.GetEntryType(out _, new U8Span(rootHostPath));
// Nintendo ignores all results other than this one
if (ResultFs.TargetNotFound.Includes(rc))
return rc;
Shared.Move(out fileSystem, ref hostFs);
return Result.Success;
}
rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref hostFs, path, preserveUnc: true);
if (rc.IsFailure()) return rc;
Shared.Move(out fileSystem, ref subDirFs);
return Result.Success;
}
finally
{
hostFs?.Dispose();
subDirFs?.Dispose();
}
return _config.TargetManagerFsCreator.Create(out fileSystem, in rootPath, openCaseSensitive, false,
Result.Success);
}
internal Result GetProgramInfoByProcessId(out ProgramInfo programInfo, ulong processId)

View File

@ -15,9 +15,10 @@ using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFile = LibHac.Fs.Fsa.IFile;
using IFileSf = LibHac.FsSrv.Sf.IFile;
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
using Path = LibHac.Fs.Path;
using SaveData = LibHac.Fs.SaveData;
using Utility = LibHac.FsSystem.Utility;
using static LibHac.Fs.StringTraits;
namespace LibHac.FsSrv
{
@ -34,21 +35,21 @@ namespace LibHac.FsSrv
private const int SaveDataBlockSize = 0x4000;
private ReferenceCountedDisposable<SaveDataFileSystemService>.WeakReference SelfReference { get; set; }
private SaveDataFileSystemServiceImpl ServiceImpl { get; }
private ulong ProcessId { get; }
private FsPath SaveDataRootPath { get; set; }
private SemaphoreAdapter OpenEntryCountSemaphore { get; }
private SemaphoreAdapter SaveDataMountCountSemaphore { get; }
private ReferenceCountedDisposable<SaveDataFileSystemService>.WeakReference _selfReference;
private SaveDataFileSystemServiceImpl _serviceImpl;
private ulong _processId;
private Path.Stored _saveDataRootPath;
private SemaphoreAdapter _openEntryCountSemaphore;
private SemaphoreAdapter _saveDataMountCountSemaphore;
private HorizonClient Hos => ServiceImpl.Hos;
private HorizonClient Hos => _serviceImpl.Hos;
public SaveDataFileSystemService(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
{
ServiceImpl = serviceImpl;
ProcessId = processId;
OpenEntryCountSemaphore = new SemaphoreAdapter(OpenEntrySemaphoreCount, OpenEntrySemaphoreCount);
SaveDataMountCountSemaphore = new SemaphoreAdapter(SaveMountSemaphoreCount, SaveMountSemaphoreCount);
_serviceImpl = serviceImpl;
_processId = processId;
_openEntryCountSemaphore = new SemaphoreAdapter(OpenEntrySemaphoreCount, OpenEntrySemaphoreCount);
_saveDataMountCountSemaphore = new SemaphoreAdapter(SaveMountSemaphoreCount, SaveMountSemaphoreCount);
}
public static ReferenceCountedDisposable<SaveDataFileSystemService> CreateShared(
@ -59,7 +60,7 @@ namespace LibHac.FsSrv
// Wrap the service in a ref-counter and give the service a weak self-reference
var sharedService = new ReferenceCountedDisposable<SaveDataFileSystemService>(saveService);
saveService.SelfReference =
saveService._selfReference =
new ReferenceCountedDisposable<SaveDataFileSystemService>.WeakReference(sharedService);
return sharedService;
@ -457,12 +458,13 @@ namespace LibHac.FsSrv
private Result DeleteSaveDataFileSystemCore(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile)
{
// Delete the save data's meta files
Result rc = ServiceImpl.DeleteAllSaveDataMetas(saveDataId, spaceId);
Result rc = _serviceImpl.DeleteAllSaveDataMetas(saveDataId, spaceId);
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
return rc;
// Delete the actual save data.
rc = ServiceImpl.DeleteSaveDataFileSystem(spaceId, saveDataId, wipeSaveFile, SaveDataRootPath);
Path saveDataRootPath = _saveDataRootPath.GetPath();
rc = _serviceImpl.DeleteSaveDataFileSystem(spaceId, saveDataId, wipeSaveFile, in saveDataRootPath);
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
return rc;
@ -526,7 +528,7 @@ namespace LibHac.FsSrv
// Only the FS process may delete the save indexer's save data.
if (saveDataId == SaveData.SaveIndexerId)
{
if (!IsCurrentProcess(ProcessId))
if (!IsCurrentProcess(_processId))
return ResultFs.PermissionDenied.Log();
actualSpaceId = spaceId;
@ -554,8 +556,8 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc;
Result GetExtraData(out SaveDataExtraData data) =>
ServiceImpl.ReadSaveDataFileSystemExtraData(out data, actualSpaceId, saveDataId, key.Type,
SaveDataRootPath);
_serviceImpl.ReadSaveDataFileSystemExtraData(out data, actualSpaceId, saveDataId, key.Type,
_saveDataRootPath.GetPath());
rc = SaveDataAccessibilityChecker.CheckDelete(in key, programInfo, GetExtraData);
if (rc.IsFailure()) return rc;
@ -625,28 +627,30 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.DebugSaveData))
return ResultFs.PermissionDenied.Log();
if (StringUtils.GetLength(path.Str, PathTool.EntryNameLengthMax + 1) > PathTool.EntryNameLengthMax)
return ResultFs.TooLongPath.Log();
var saveDataRootPath = new Path();
if (path.Str[0] == 0)
SaveDataRootPath.Str[0] = 0;
var sb = new U8StringBuilder(SaveDataRootPath.Str);
sb.Append((byte)'/').Append(path.Str);
if (StringUtils.Compare(SaveDataRootPath.Str.Slice(1), new[] { (byte)'/', (byte)'/' }, 2) == 0)
if (path.Str[0] == NullTerminator)
{
for (int i = 0; SaveDataRootPath.Str[i] == '/'; i++)
{
SaveDataRootPath.Str[i] = (byte)'\\';
}
rc = saveDataRootPath.Initialize(new[] { (byte)'/' });
if (rc.IsFailure()) return rc;
}
else
{
rc = saveDataRootPath.InitializeWithReplaceUnc(path.Str);
if (rc.IsFailure()) return rc;
}
using var normalizer = new PathNormalizer(SaveDataRootPath, PathNormalizer.Option.PreserveUnc);
if (normalizer.Result.IsFailure())
return normalizer.Result;
var pathFlags = new PathFlags();
pathFlags.AllowWindowsPath();
pathFlags.AllowRelativePath();
pathFlags.AllowEmptyPath();
rc = saveDataRootPath.Normalize(pathFlags);
if (rc.IsFailure()) return rc;
_saveDataRootPath.Initialize(in saveDataRootPath);
saveDataRootPath.Dispose();
StringUtils.Copy(SaveDataRootPath.Str, normalizer.Path);
return Result.Success;
}
@ -658,7 +662,13 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.DebugSaveData))
return ResultFs.PermissionDenied.Log();
SaveDataRootPath.Str.Clear();
var saveDataRootPath = new Path();
rc = saveDataRootPath.InitializeAsEmpty();
if (rc.IsFailure()) return rc;
_saveDataRootPath.Initialize(in saveDataRootPath);
saveDataRootPath.Dispose();
return Result.Success;
}
@ -709,7 +719,7 @@ namespace LibHac.FsSrv
{
// The save indexer doesn't index itself
saveDataId = SaveData.SaveIndexerId;
rc = ServiceImpl.DoesSaveDataEntityExist(out bool saveExists, creationInfo.SpaceId, saveDataId);
rc = _serviceImpl.DoesSaveDataEntityExist(out bool saveExists, creationInfo.SpaceId, saveDataId);
if (rc.IsSuccess() && saveExists)
{
@ -780,8 +790,9 @@ namespace LibHac.FsSrv
}
// After the new save was added to the save indexer, create the save data file or directory.
rc = ServiceImpl.CreateSaveDataFileSystem(saveDataId, in attribute, in creationInfo, SaveDataRootPath,
in hashSalt, false);
Path saveDataRootPath = _saveDataRootPath.GetPath();
rc = _serviceImpl.CreateSaveDataFileSystem(saveDataId, in attribute, in creationInfo,
in saveDataRootPath, in hashSalt, false);
if (rc.IsFailure())
{
@ -792,21 +803,21 @@ namespace LibHac.FsSrv
rc = DeleteSaveDataFileSystemCore(creationInfo.SpaceId, saveDataId, false);
if (rc.IsFailure()) return rc;
rc = ServiceImpl.CreateSaveDataFileSystem(saveDataId, in attribute, in creationInfo,
SaveDataRootPath, in hashSalt, false);
rc = _serviceImpl.CreateSaveDataFileSystem(saveDataId, in attribute, in creationInfo,
in saveDataRootPath, in hashSalt, false);
if (rc.IsFailure()) return rc;
}
if (metaInfo.Type != SaveDataMetaType.None)
{
// Create the requested save data meta file.
rc = ServiceImpl.CreateSaveDataMeta(saveDataId, creationInfo.SpaceId, metaInfo.Type,
rc = _serviceImpl.CreateSaveDataMeta(saveDataId, creationInfo.SpaceId, metaInfo.Type,
metaInfo.Size);
if (rc.IsFailure()) return rc;
if (metaInfo.Type == SaveDataMetaType.Thumbnail)
{
rc = ServiceImpl.OpenSaveDataMeta(out IFile metaFile, saveDataId, creationInfo.SpaceId,
rc = _serviceImpl.OpenSaveDataMeta(out IFile metaFile, saveDataId, creationInfo.SpaceId,
metaInfo.Type);
using (metaFile)
@ -866,7 +877,6 @@ namespace LibHac.FsSrv
}
accessor?.Dispose();
}
}
@ -896,7 +906,7 @@ namespace LibHac.FsSrv
if (dataSize < 0 || journalSize < 0)
return ResultFs.InvalidSize.Log();
return ServiceImpl.QuerySaveDataTotalSize(out totalSize, SaveDataBlockSize, dataSize, journalSize);
return _serviceImpl.QuerySaveDataTotalSize(out totalSize, SaveDataBlockSize, dataSize, journalSize);
}
public Result CreateSaveDataFileSystem(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo,
@ -1037,8 +1047,9 @@ namespace LibHac.FsSrv
}
// Open the save data using its ID
Result saveFsResult = ServiceImpl.OpenSaveDataFileSystem(out fileSystem, spaceId, tempSaveDataId,
SaveDataRootPath, openReadOnly, attribute.Type, cacheExtraData);
Path saveDataRootPath = _saveDataRootPath.GetPath();
Result saveFsResult = _serviceImpl.OpenSaveDataFileSystem(out fileSystem, spaceId, tempSaveDataId,
in saveDataRootPath, openReadOnly, attribute.Type, cacheExtraData);
if (saveFsResult.IsSuccess())
{
@ -1117,7 +1128,8 @@ namespace LibHac.FsSrv
Result rc = TryAcquireSaveDataMountCountSemaphore(out mountCountSemaphore);
if (rc.IsFailure()) return rc;
bool useAsyncFileSystem = !ServiceImpl.IsAllowedDirectorySaveData(spaceId, SaveDataRootPath);
Path saveDataRootPath = _saveDataRootPath.GetPath();
bool useAsyncFileSystem = !_serviceImpl.IsAllowedDirectorySaveData(spaceId, in saveDataRootPath);
// Open the file system
rc = OpenSaveDataFileSystemCore(out tempFileSystem, out ulong saveDataId, spaceId, in attribute,
@ -1127,8 +1139,12 @@ namespace LibHac.FsSrv
// Can't use attribute in a closure, so copy the needed field
SaveDataType type = attribute.Type;
Result ReadExtraData(out SaveDataExtraData data) =>
ServiceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, type, SaveDataRootPath);
Result ReadExtraData(out SaveDataExtraData data)
{
Path savePath = _saveDataRootPath.GetPath();
return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, type,
in savePath);
}
// Check if we have permissions to open this save data
rc = SaveDataAccessibilityChecker.CheckOpen(in attribute, programInfo, ReadExtraData);
@ -1142,13 +1158,15 @@ namespace LibHac.FsSrv
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
}
saveService = SelfReference.AddReference();
saveService = _selfReference.AddReference();
openEntryCountAdapter = SaveDataOpenCountAdapter.CreateShared(ref saveService);
tempFileSystem = OpenCountFileSystem.CreateShared(ref tempFileSystem, ref openEntryCountAdapter,
ref mountCountSemaphore);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
var pathFlags = new PathFlags();
pathFlags.AllowBackslash();
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem, pathFlags, false);
return Result.Success;
}
finally
@ -1223,7 +1241,8 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
bool useAsyncFileSystem = !ServiceImpl.IsAllowedDirectorySaveData(spaceId, SaveDataRootPath);
Path saveDataRootPath = _saveDataRootPath.GetPath();
bool useAsyncFileSystem = !_serviceImpl.IsAllowedDirectorySaveData(spaceId, in saveDataRootPath);
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
ReferenceCountedDisposable<SaveDataFileSystemService> saveService = null;
@ -1240,7 +1259,8 @@ namespace LibHac.FsSrv
SaveDataType type = attribute.Type;
Result ReadExtraData(out SaveDataExtraData data) =>
ServiceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, type, SaveDataRootPath);
_serviceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, type,
_saveDataRootPath.GetPath());
// Check if we have permissions to open this save data
rc = SaveDataAccessibilityChecker.CheckOpen(in attribute, programInfo, ReadExtraData);
@ -1254,12 +1274,14 @@ namespace LibHac.FsSrv
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
}
saveService = SelfReference.AddReference();
saveService = _selfReference.AddReference();
openEntryCountAdapter = SaveDataOpenCountAdapter.CreateShared(ref saveService);
tempFileSystem = OpenCountFileSystem.CreateShared(ref tempFileSystem, ref openEntryCountAdapter);
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
var pathFlags = new PathFlags();
pathFlags.AllowBackslash();
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem, pathFlags, false);
return Result.Success;
}
finally
@ -1288,8 +1310,9 @@ namespace LibHac.FsSrv
rc = accessor.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
if (rc.IsFailure()) return rc;
return ServiceImpl.ReadSaveDataFileSystemExtraData(out extraData, spaceId, saveDataId, key.Type,
SaveDataRootPath);
Path saveDataRootPath = _saveDataRootPath.GetPath();
return _serviceImpl.ReadSaveDataFileSystemExtraData(out extraData, spaceId, saveDataId, key.Type,
in saveDataRootPath);
}
finally
{
@ -1361,15 +1384,16 @@ namespace LibHac.FsSrv
}
}
Result ReadExtraData(out SaveDataExtraData data) => ServiceImpl.ReadSaveDataFileSystemExtraData(out data,
resolvedSpaceId, saveDataId, key.Type, new U8Span(SaveDataRootPath.Str));
Result ReadExtraData(out SaveDataExtraData data) => _serviceImpl.ReadSaveDataFileSystemExtraData(out data,
resolvedSpaceId, saveDataId, key.Type, _saveDataRootPath.GetPath());
rc = SaveDataAccessibilityChecker.CheckReadExtraData(in key, in extraDataMask, programInfo,
ReadExtraData);
if (rc.IsFailure()) return rc;
rc = ServiceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData tempExtraData, resolvedSpaceId,
saveDataId, key.Type, new U8Span(SaveDataRootPath.Str));
Path saveDataRootPath = _saveDataRootPath.GetPath();
rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData tempExtraData, resolvedSpaceId,
saveDataId, key.Type, in saveDataRootPath);
if (rc.IsFailure()) return rc;
MaskExtraData(ref tempExtraData, in extraDataMask);
@ -1469,7 +1493,8 @@ namespace LibHac.FsSrv
{
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.NonGameCard);
return ServiceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, SaveDataRootPath,
Path saveDataRootPath = _saveDataRootPath.GetPath();
return _serviceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in saveDataRootPath,
saveType, updateTimeStamp);
}
@ -1490,21 +1515,22 @@ namespace LibHac.FsSrv
rc = accessor.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
if (rc.IsFailure()) return rc;
Result ReadExtraData(out SaveDataExtraData data) => ServiceImpl.ReadSaveDataFileSystemExtraData(out data,
spaceId, saveDataId, key.Type, new U8Span(SaveDataRootPath.Str));
Result ReadExtraData(out SaveDataExtraData data) => _serviceImpl.ReadSaveDataFileSystemExtraData(out data,
spaceId, saveDataId, key.Type, _saveDataRootPath.GetPath());
rc = SaveDataAccessibilityChecker.CheckWriteExtraData(in key, in extraDataMask, programInfo,
ReadExtraData);
if (rc.IsFailure()) return rc;
rc = ServiceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraDataModify, spaceId,
saveDataId, key.Type, SaveDataRootPath);
Path saveDataRootPath = _saveDataRootPath.GetPath();
rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraDataModify, spaceId,
saveDataId, key.Type, in saveDataRootPath);
if (rc.IsFailure()) return rc;
ModifySaveDataExtraData(ref extraDataModify, in extraData, in extraDataMask);
return ServiceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraDataModify,
SaveDataRootPath, key.Type, false);
return _serviceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraDataModify,
in saveDataRootPath, key.Type, false);
}
finally
{
@ -1872,7 +1898,7 @@ namespace LibHac.FsSrv
Result rc;
// Cache storage on the SD card will always take priority over case storage in NAND
if (ServiceImpl.IsSdCardAccessible())
if (_serviceImpl.IsSdCardAccessible())
{
rc = SaveExists(out bool existsOnSdCard, SaveDataSpaceId.SdCache);
if (rc.IsFailure()) return rc;
@ -1957,8 +1983,9 @@ namespace LibHac.FsSrv
Result rc = FindCacheStorage(out SaveDataInfo saveInfo, out SaveDataSpaceId spaceId, index);
if (rc.IsFailure()) return rc;
rc = ServiceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId,
saveInfo.SaveDataId, saveInfo.Type, new U8Span(SaveDataRootPath.Str));
Path saveDataRootPath = _saveDataRootPath.GetPath();
rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId,
saveInfo.SaveDataId, saveInfo.Type, in saveDataRootPath);
if (rc.IsFailure()) return rc;
usableDataSize = extraData.DataSize;
@ -2005,7 +2032,7 @@ namespace LibHac.FsSrv
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{
return ServiceImpl.SetSdCardEncryptionSeed(in seed);
return _serviceImpl.SetSdCardEncryptionSeed(in seed);
}
public Result ListAccessibleSaveDataOwnerId(out int readCount, OutBuffer idBuffer, ProgramId programId,
@ -2016,7 +2043,7 @@ namespace LibHac.FsSrv
private ProgramId ResolveDefaultSaveDataReferenceProgramId(ProgramId programId)
{
return ServiceImpl.ResolveDefaultSaveDataReferenceProgramId(programId);
return _serviceImpl.ResolveDefaultSaveDataReferenceProgramId(programId);
}
public Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId,
@ -2085,15 +2112,17 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
Result rc = ServiceImpl.OpenSaveDataDirectoryFileSystem(out fileSystem, SaveDataSpaceId.Temporary,
U8Span.Empty, false);
Result rc = _serviceImpl.OpenSaveDataDirectoryFileSystem(out fileSystem, SaveDataSpaceId.Temporary);
if (rc.IsFailure()) return rc;
var rootPath = new U8Span(new[] { (byte)'/' });
rc = fileSystem.Target.CleanDirectoryRecursively(rootPath);
var pathRoot = new Path();
rc = PathFunctions.SetUpFixedPath(ref pathRoot, new[] { (byte)'/' });
if (rc.IsFailure()) return rc;
ServiceImpl.ResetTemporaryStorageIndexer();
rc = fileSystem.Target.CleanDirectoryRecursively(in pathRoot);
if (rc.IsFailure()) return rc;
_serviceImpl.ResetTemporaryStorageIndexer();
return Result.Success;
}
finally
@ -2116,10 +2145,10 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> commitInterface = null;
try
{
saveService = SelfReference.AddReference();
saveService = _selfReference.AddReference();
commitInterface = saveService.AddReference<ISaveDataMultiCommitCoreInterface>();
commitManager = MultiCommitManager.CreateShared(ServiceImpl.FsServer, ref commitInterface);
commitManager = MultiCommitManager.CreateShared(_serviceImpl.FsServer, ref commitInterface);
return Result.Success;
}
finally
@ -2146,12 +2175,12 @@ namespace LibHac.FsSrv
public Result RecoverMultiCommit()
{
return MultiCommitManager.Recover(ServiceImpl.FsServer, this, ServiceImpl);
return MultiCommitManager.Recover(_serviceImpl.FsServer, this, _serviceImpl);
}
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
{
return ServiceImpl.IsProvisionallyCommittedSaveData(out isProvisionallyCommitted, in saveInfo);
return _serviceImpl.IsProvisionallyCommittedSaveData(out isProvisionallyCommitted, in saveInfo);
}
public Result RecoverProvisionallyCommittedSaveData(in SaveDataInfo saveInfo, bool doRollback)
@ -2197,9 +2226,9 @@ namespace LibHac.FsSrv
IUniqueLock uniqueLock = null;
try
{
saveService = SelfReference.AddReference();
saveService = _selfReference.AddReference();
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, OpenEntryCountSemaphore,
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, _openEntryCountSemaphore,
ref saveService);
if (rc.IsFailure()) return rc;
@ -2221,9 +2250,9 @@ namespace LibHac.FsSrv
IUniqueLock uniqueLock = null;
try
{
saveService = SelfReference.AddReference();
saveService = _selfReference.AddReference();
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, SaveDataMountCountSemaphore,
Result rc = Utility.MakeUniqueLockWithPin(out uniqueLock, _saveDataMountCountSemaphore,
ref saveService);
if (rc.IsFailure()) return rc;
@ -2250,13 +2279,13 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.SetSdCardAccessibility))
return ResultFs.PermissionDenied.Log();
ServiceImpl.SetSdCardAccessibility(isAccessible);
_serviceImpl.SetSdCardAccessibility(isAccessible);
return Result.Success;
}
public Result IsSdCardAccessible(out bool isAccessible)
{
isAccessible = ServiceImpl.IsSdCardAccessible();
isAccessible = _serviceImpl.IsSdCardAccessible();
return Result.Success;
}
@ -2267,7 +2296,7 @@ namespace LibHac.FsSrv
SaveDataIndexerAccessor accessorTemp = null;
try
{
Result rc = ServiceImpl.OpenSaveDataIndexerAccessor(out accessorTemp, out bool neededInit, spaceId);
Result rc = _serviceImpl.OpenSaveDataIndexerAccessor(out accessorTemp, out bool neededInit, spaceId);
if (rc.IsFailure()) return rc;
if (neededInit)
@ -2287,7 +2316,7 @@ namespace LibHac.FsSrv
private Result GetProgramInfo(out ProgramInfo programInfo)
{
return ServiceImpl.GetProgramInfo(out programInfo, ProcessId);
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
}
private bool IsCurrentProcess(ulong processId)
@ -2387,8 +2416,8 @@ namespace LibHac.FsSrv
public void Dispose()
{
OpenEntryCountSemaphore.Dispose();
SaveDataMountCountSemaphore.Dispose();
_openEntryCountSemaphore.Dispose();
_saveDataMountCountSemaphore.Dispose();
}
}
}

View File

@ -1,5 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
@ -88,15 +88,21 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId, U8Span.Empty, true);
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId);
if (rc.IsFailure()) return rc;
// Get the path of the save data
Span<byte> saveDataPath = stackalloc byte[0x12];
var sb = new U8StringBuilder(saveDataPath);
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
// Hack around error CS8350.
const int bufferLength = 0x12;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveImageNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
rc = fileSystem.Target.GetEntryType(out _, new U8Span(saveDataPath));
var saveImageName = new Path();
rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName, saveImageNameBuffer, saveDataId);
if (rc.IsFailure()) return rc;
rc = fileSystem.Target.GetEntryType(out _, in saveImageName);
if (rc.IsSuccess())
{
@ -120,7 +126,7 @@ namespace LibHac.FsSrv
}
public Result OpenSaveDataFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, ulong saveDataId, U8Span saveDataRootPath, bool openReadOnly, SaveDataType type,
SaveDataSpaceId spaceId, ulong saveDataId, in Path saveDataRootPath, bool openReadOnly, SaveDataType type,
bool cacheExtraData)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
@ -130,10 +136,10 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> saveDataFs = null;
try
{
Result rc = OpenSaveDataDirectoryFileSystem(out saveDirectoryFs, spaceId, saveDataRootPath, true);
Result rc = OpenSaveDataDirectoryFileSystem(out saveDirectoryFs, spaceId, in saveDataRootPath, true);
if (rc.IsFailure()) return rc;
bool allowDirectorySaveData = IsAllowedDirectorySaveData2(spaceId, saveDataRootPath);
bool allowDirectorySaveData = IsAllowedDirectorySaveData2(spaceId, in saveDataRootPath);
// Note: When directory save data is allowed, Nintendo creates the save directory if it doesn't exist.
// This bypasses normal save data creation, leaving the save with empty extra data.
@ -209,22 +215,24 @@ namespace LibHac.FsSrv
public Result OpenSaveDataMetaDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, ulong saveDataId)
{
var metaDirPath = // /saveMeta/
new U8Span(new[]
{
(byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t',
(byte)'a', (byte)'/'
});
UnsafeHelpers.SkipParamInit(out fileSystem);
Span<byte> directoryName = stackalloc byte[0x1B];
var sb = new U8StringBuilder(directoryName);
sb.Append(metaDirPath).AppendFormat(saveDataId, 'x', 16);
// Hack around error CS8350.
const int bufferLength = 0xF;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveDataMetaIdDirectoryNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, new U8Span(directoryName));
var saveDataMetaIdDirectoryName = new Path();
Result rc = PathFunctions.SetUpFixedPathSaveMetaDir(ref saveDataMetaIdDirectoryName,
saveDataMetaIdDirectoryNameBuffer, saveDataId);
if (rc.IsFailure()) return rc;
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, in saveDataMetaIdDirectoryName);
}
public Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, ulong saveDataId, U8Span saveDataRootPath, bool useSecondMacKey)
SaveDataSpaceId spaceId, ulong saveDataId, in Path saveDataRootPath, bool useSecondMacKey)
{
throw new NotImplementedException();
}
@ -245,11 +253,18 @@ namespace LibHac.FsSrv
Result rc = OpenSaveDataMetaDirectoryFileSystem(out metaDirFs, spaceId, saveDataId);
if (rc.IsFailure()) return rc;
Span<byte> saveMetaPathBuffer = stackalloc byte[0xF];
var sb = new U8StringBuilder(saveMetaPathBuffer);
sb.Append((byte)'/').AppendFormat((int)metaType, 'x', 8);
// Hack around error CS8350.
const int bufferLength = 0xF;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveDataMetaNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
return metaDirFs.Target.CreateFile(new U8Span(saveMetaPathBuffer), metaSize);
var saveDataMetaName = new Path();
rc = PathFunctions.SetUpFixedPathSaveMetaName(ref saveDataMetaName, saveDataMetaNameBuffer,
(uint)metaType);
if (rc.IsFailure()) return rc;
return metaDirFs.Target.CreateFile(in saveDataMetaName, metaSize);
}
finally
{
@ -265,11 +280,18 @@ namespace LibHac.FsSrv
Result rc = OpenSaveDataMetaDirectoryFileSystem(out metaDirFs, spaceId, saveDataId);
if (rc.IsFailure()) return rc;
Span<byte> saveMetaPathBuffer = stackalloc byte[0xF];
var sb = new U8StringBuilder(saveMetaPathBuffer);
sb.Append((byte)'/').AppendFormat((int)metaType, 'x', 8);
// Hack around error CS8350.
const int bufferLength = 0xF;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveDataMetaNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
return metaDirFs.Target.DeleteFile(new U8Span(saveMetaPathBuffer));
var saveDataMetaName = new Path();
rc = PathFunctions.SetUpFixedPathSaveMetaName(ref saveDataMetaName, saveDataMetaNameBuffer,
(uint)metaType);
if (rc.IsFailure()) return rc;
return metaDirFs.Target.DeleteFile(in saveDataMetaName);
}
finally
{
@ -279,30 +301,43 @@ namespace LibHac.FsSrv
public Result DeleteAllSaveDataMetas(ulong saveDataId, SaveDataSpaceId spaceId)
{
var metaDirPath = // /saveMeta
new U8Span(new[]
ReadOnlySpan<byte> metaDirName = // /saveMeta
new[]
{
(byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t',
(byte)'a'
});
(byte) '/', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'M', (byte) 'e', (byte) 't',
(byte) 'a'
};
// Hack around error CS8350.
const int bufferLength = 0x12;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveDataIdDirectoryNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
Result rc = OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, metaDirPath, false);
var saveDataMetaDirectoryName = new Path();
Result rc = PathFunctions.SetUpFixedPath(ref saveDataMetaDirectoryName, metaDirName);
if (rc.IsFailure()) return rc;
// Get the path of the save data meta
Span<byte> saveDataPathBuffer = stackalloc byte[0x12];
var sb = new U8StringBuilder(saveDataPathBuffer);
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
rc = OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, in saveDataMetaDirectoryName, false);
if (rc.IsFailure()) return rc;
var saveDataIdDirectoryName = new Path();
PathFunctions.SetUpFixedPathSaveId(ref saveDataIdDirectoryName, saveDataIdDirectoryNameBuffer,
saveDataId);
if (rc.IsFailure()) return rc;
// Delete the save data's meta directory, ignoring the error if the directory is already gone
rc = fileSystem.Target.DeleteDirectoryRecursively(new U8Span(saveDataPathBuffer));
rc = fileSystem.Target.DeleteDirectoryRecursively(in saveDataIdDirectoryName);
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
return rc;
saveDataMetaDirectoryName.Dispose();
saveDataIdDirectoryName.Dispose();
return Result.Success;
}
finally
@ -322,11 +357,18 @@ namespace LibHac.FsSrv
Result rc = OpenSaveDataMetaDirectoryFileSystem(out metaDirFs, spaceId, saveDataId);
if (rc.IsFailure()) return rc;
Span<byte> saveMetaPathBuffer = stackalloc byte[0xF];
var sb = new U8StringBuilder(saveMetaPathBuffer);
sb.Append((byte)'/').AppendFormat((int)metaType, 'x', 8);
// Hack around error CS8350.
const int bufferLength = 0xF;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveDataMetaNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
return metaDirFs.Target.OpenFile(out metaFile, new U8Span(saveMetaPathBuffer), OpenMode.ReadWrite);
var saveDataMetaName = new Path();
rc = PathFunctions.SetUpFixedPathSaveMetaName(ref saveDataMetaName, saveDataMetaNameBuffer,
(uint)metaType);
if (rc.IsFailure()) return rc;
return metaDirFs.Target.OpenFile(out metaFile, in saveDataMetaName, OpenMode.ReadWrite);
}
finally
{
@ -335,25 +377,31 @@ namespace LibHac.FsSrv
}
public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataAttribute attribute,
in SaveDataCreationInfo creationInfo, U8Span saveDataRootPath, in Optional<HashSalt> hashSalt,
in SaveDataCreationInfo creationInfo, in Path saveDataRootPath, in Optional<HashSalt> hashSalt,
bool skipFormat)
{
// Use directory save data for now
// Hack around error CS8350.
const int bufferLength = 0x12;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveImageNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
ReferenceCountedDisposable<IFileSystem> saveDirectoryFs = null;
try
{
Result rc = OpenSaveDataDirectoryFileSystem(out saveDirectoryFs, creationInfo.SpaceId, saveDataRootPath,
false);
Result rc = OpenSaveDataDirectoryFileSystem(out saveDirectoryFs, creationInfo.SpaceId,
in saveDataRootPath, false);
if (rc.IsFailure()) return rc;
Span<byte> saveDataPathBuffer = stackalloc byte[0x12];
var sb = new U8StringBuilder(saveDataPathBuffer);
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
var saveImageName = new Path();
rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName, saveImageNameBuffer, saveDataId);
if (rc.IsFailure()) return rc;
if (_config.IsPseudoSaveData())
{
rc = Utility.EnsureDirectory(saveDirectoryFs.Target, new U8Span(saveDataPathBuffer));
rc = Utility12.EnsureDirectory(saveDirectoryFs.Target, in saveImageName);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> saveFs = null;
@ -405,51 +453,55 @@ namespace LibHac.FsSrv
}
}
private Result WipeData(IFileSystem fileSystem, U8Span filePath, RandomDataGenerator random)
private Result WipeData(IFileSystem fileSystem, in Path filePath, RandomDataGenerator random)
{
throw new NotImplementedException();
}
public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile,
U8Span saveDataRootPath)
in Path saveDataRootPath)
{
// Hack around error CS8350.
const int bufferLength = 0x12;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveImageNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
try
{
_saveDataFsCacheManager.Unregister(spaceId, saveDataId);
// Open the directory containing the save data
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId, saveDataRootPath, false);
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId, in saveDataRootPath, false);
if (rc.IsFailure()) return rc;
// Get the path of the save data
Span<byte> saveDataPathBuffer = stackalloc byte[0x12];
var saveDataPath = new U8Span(saveDataPathBuffer);
var sb = new U8StringBuilder(saveDataPathBuffer);
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
var saveImageName = new Path();
rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName, saveImageNameBuffer, saveDataId);
if (rc.IsFailure()) return rc;
// Check if the save data is a file or a directory
rc = fileSystem.Target.GetEntryType(out DirectoryEntryType entryType, saveDataPath);
rc = fileSystem.Target.GetEntryType(out DirectoryEntryType entryType, in saveImageName);
if (rc.IsFailure()) return rc;
// Delete the save data, wiping the file if needed
if (entryType == DirectoryEntryType.Directory)
{
rc = fileSystem.Target.DeleteDirectoryRecursively(saveDataPath);
rc = fileSystem.Target.DeleteDirectoryRecursively(in saveImageName);
if (rc.IsFailure()) return rc;
}
else
{
if (wipeSaveFile)
{
WipeData(fileSystem.Target, saveDataPath, _config.GenerateRandomData).IgnoreResult();
WipeData(fileSystem.Target, in saveImageName, _config.GenerateRandomData).IgnoreResult();
}
rc = fileSystem.Target.DeleteDirectoryRecursively(saveDataPath);
rc = fileSystem.Target.DeleteFile(in saveImageName);
if (rc.IsFailure()) return rc;
}
saveImageName.Dispose();
return Result.Success;
}
finally
@ -459,7 +511,7 @@ namespace LibHac.FsSrv
}
public Result ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId spaceId,
ulong saveDataId, SaveDataType type, U8Span saveDataRootPath)
ulong saveDataId, SaveDataType type, in Path saveDataRootPath)
{
UnsafeHelpers.SkipParamInit(out extraData);
@ -515,7 +567,7 @@ namespace LibHac.FsSrv
}
public Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId spaceId, ulong saveDataId,
in SaveDataExtraData extraData, U8Span saveDataRootPath, SaveDataType type, bool updateTimeStamp)
in SaveDataExtraData extraData, in Path saveDataRootPath, SaveDataType type, bool updateTimeStamp)
{
// Nintendo does nothing when writing directory save data extra data.
// We've extended directory save data to store extra data so we don't return early.
@ -573,7 +625,7 @@ namespace LibHac.FsSrv
}
public Result CorruptSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long offset,
U8Span saveDataRootPath)
in Path saveDataRootPath)
{
throw new NotImplementedException();
}
@ -583,43 +635,51 @@ namespace LibHac.FsSrv
return _config.TimeService.GetCurrentPosixTime(out timeStamp);
}
private bool IsSaveEmulated(U8Span saveDataRootPath)
private bool IsSaveEmulated(in Path saveDataRootPath)
{
return !saveDataRootPath.IsEmpty();
}
public Result OpenSaveDataDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, U8Span saveDataRootPath, bool allowEmulatedSave)
SaveDataSpaceId spaceId)
{
if (allowEmulatedSave && IsAllowedDirectorySaveData(spaceId, saveDataRootPath))
var rootPath = new Path();
return OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId, in rootPath, true);
}
public Result OpenSaveDataDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, in Path saveDataRootPath, bool allowEmulatedSave)
{
Result rc;
UnsafeHelpers.SkipParamInit(out fileSystem);
if (allowEmulatedSave && IsAllowedDirectorySaveData(spaceId, in saveDataRootPath))
{
UnsafeHelpers.SkipParamInit(out fileSystem);
ReferenceCountedDisposable<IFileSystem> tmFs = null;
try
{
// Ensure the target save data directory exists
Result rc = _config.TargetManagerFsCreator.Create(out tmFs, false);
rc = _config.TargetManagerFsCreator.Create(out tmFs, in saveDataRootPath, false, true,
ResultFs.SaveDataRootPathUnavailable.Value);
if (rc.IsFailure()) return rc;
rc = Utility.EnsureDirectory(tmFs.Target, saveDataRootPath);
if (rc.IsFailure())
return ResultFs.SaveDataRootPathUnavailable.LogConverted(rc);
tmFs.Dispose();
// Nintendo does a straight copy here without checking the input path length,
// stopping before the null terminator.
// FsPath used instead of stackalloc for convenience
FsPath.FromSpan(out FsPath path, saveDataRootPath).IgnoreResult();
rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isTargetFsCaseSensitive, path.Str);
var path = new Path();
rc = path.Initialize(in saveDataRootPath);
if (rc.IsFailure()) return rc;
rc = _config.TargetManagerFsCreator.Create(out tmFs, isTargetFsCaseSensitive);
rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isTargetFsCaseSensitive, ref path);
if (rc.IsFailure()) return rc;
return Utility.CreateSubDirectoryFileSystem(out fileSystem, ref tmFs, new U8Span(path.Str), true);
rc = _config.TargetManagerFsCreator.Create(out tmFs, in path, isTargetFsCaseSensitive, false,
ResultFs.SaveDataRootPathUnavailable.Value);
if (rc.IsFailure()) return rc;
path.Dispose();
return Result.Success;
}
finally
{
@ -627,6 +687,7 @@ namespace LibHac.FsSrv
}
}
var saveDataDirPath = new Path();
ReadOnlySpan<byte> saveDirName;
if (spaceId == SaveDataSpaceId.Temporary)
@ -638,22 +699,29 @@ namespace LibHac.FsSrv
saveDirName = new[] { (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e' }; // /save
}
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, new U8Span(saveDirName), true);
rc = PathFunctions.SetUpFixedPath(ref saveDataDirPath, saveDirName);
if (rc.IsFailure()) return rc;
rc = OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, in saveDataDirPath, true);
if (rc.IsFailure()) return rc;
saveDataDirPath.Dispose();
return Result.Success;
}
public Result OpenSaveDataDirectoryFileSystemImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, U8Span basePath)
SaveDataSpaceId spaceId, in Path basePath)
{
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, basePath, true);
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, in basePath, true);
}
public Result OpenSaveDataDirectoryFileSystemImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, U8Span basePath, bool createIfMissing)
SaveDataSpaceId spaceId, in Path basePath, bool createIfMissing)
{
UnsafeHelpers.SkipParamInit(out fileSystem);
ReferenceCountedDisposable<IFileSystem> tempFs = null;
ReferenceCountedDisposable<IFileSystem> tempSubFs = null;
ReferenceCountedDisposable<IFileSystem> baseFileSystem = null;
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
try
{
Result rc;
@ -661,51 +729,63 @@ namespace LibHac.FsSrv
switch (spaceId)
{
case SaveDataSpaceId.System:
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.System,
true);
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, BisPartitionId.System, true);
if (rc.IsFailure()) return rc;
return Utility.WrapSubDirectory(out fileSystem, ref tempFs, basePath, createIfMissing);
return Utility.WrapSubDirectory(out fileSystem, ref baseFileSystem, in basePath, createIfMissing);
case SaveDataSpaceId.User:
case SaveDataSpaceId.Temporary:
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.User,
true);
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, BisPartitionId.User, true);
if (rc.IsFailure()) return rc;
return Utility.WrapSubDirectory(out fileSystem, ref tempFs, basePath, createIfMissing);
return Utility.WrapSubDirectory(out fileSystem, ref baseFileSystem, in basePath, createIfMissing);
case SaveDataSpaceId.SdSystem:
case SaveDataSpaceId.SdCache:
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out tempFs, true);
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out baseFileSystem, true);
if (rc.IsFailure()) return rc;
Unsafe.SkipInit(out FsPath path);
var sb = new U8StringBuilder(path.Str);
sb.Append((byte)'/')
.Append(basePath.Value)
.Append((byte)'/')
.Append(CommonPaths.SdCardNintendoRootDirectoryName);
// Hack around error CS8350.
const int bufferLength = 0x40;
Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> pathParentBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
rc = Utility.WrapSubDirectory(out tempSubFs, ref tempFs, path, createIfMissing);
var parentPath = new Path();
rc = PathFunctions.SetUpFixedPathSingleEntry(ref parentPath, pathParentBuffer,
CommonPaths.SdCardNintendoRootDirectoryName);
if (rc.IsFailure()) return rc;
return _config.EncryptedFsCreator.Create(out fileSystem, tempSubFs, EncryptedFsKeyId.Save,
in _encryptionSeed);
var pathSdRoot = new Path();
rc = pathSdRoot.Combine(in parentPath, in basePath);
if (rc.IsFailure()) return rc;
tempFileSystem = Shared.Move(ref baseFileSystem);
rc = Utility.WrapSubDirectory(out baseFileSystem, ref tempFileSystem, in pathSdRoot,
createIfMissing);
if (rc.IsFailure()) return rc;
rc = _config.EncryptedFsCreator.Create(out fileSystem, ref baseFileSystem,
IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed);
if (rc.IsFailure()) return rc;
parentPath.Dispose();
pathSdRoot.Dispose();
return Result.Success;
case SaveDataSpaceId.ProperSystem:
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty,
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem,
BisPartitionId.SystemProperPartition, true);
if (rc.IsFailure()) return rc;
return Utility.WrapSubDirectory(out fileSystem, ref tempFs, basePath, createIfMissing);
return Utility.WrapSubDirectory(out fileSystem, ref baseFileSystem, in basePath, createIfMissing);
case SaveDataSpaceId.SafeMode:
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.SafeMode,
true);
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, BisPartitionId.SafeMode, true);
if (rc.IsFailure()) return rc;
return Utility.WrapSubDirectory(out fileSystem, ref tempFs, basePath, createIfMissing);
return Utility.WrapSubDirectory(out fileSystem, ref baseFileSystem, in basePath, createIfMissing);
default:
return ResultFs.InvalidArgument.Log();
@ -713,8 +793,8 @@ namespace LibHac.FsSrv
}
finally
{
tempFs?.Dispose();
tempSubFs?.Dispose();
baseFileSystem?.Dispose();
tempFileSystem?.Dispose();
}
}
@ -734,18 +814,18 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public bool IsAllowedDirectorySaveData(SaveDataSpaceId spaceId, U8Span saveDataRootPath)
public bool IsAllowedDirectorySaveData(SaveDataSpaceId spaceId, in Path saveDataRootPath)
{
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(saveDataRootPath);
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath);
}
// Todo: remove once file save data is supported
// Used to always allow directory save data in OpenSaveDataFileSystem
public bool IsAllowedDirectorySaveData2(SaveDataSpaceId spaceId, U8Span saveDataRootPath)
public bool IsAllowedDirectorySaveData2(SaveDataSpaceId spaceId, in Path saveDataRootPath)
{
// Todo: remove "|| true" once file save data is supported
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(saveDataRootPath) || true;
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath) || true;
}
public bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)

View File

@ -1,81 +0,0 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using PathNormalizer = LibHac.FsSrv.Impl.PathNormalizer;
namespace LibHac.FsSrv
{
public static class Util
{
public static Result CreateSubFileSystem(out IFileSystem subFileSystem, IFileSystem baseFileSystem, string path,
bool createPathIfMissing)
{
UnsafeHelpers.SkipParamInit(out subFileSystem);
Result rc;
if (!createPathIfMissing)
{
if (path == null) return ResultFs.NullptrArgument.Log();
rc = baseFileSystem.GetEntryType(out DirectoryEntryType entryType, path.ToU8Span());
if (rc.IsFailure() || entryType != DirectoryEntryType.Directory)
{
return ResultFs.PathNotFound.Log();
}
}
rc = baseFileSystem.EnsureDirectoryExists(path);
if (rc.IsFailure()) return rc;
return CreateSubFileSystemImpl(out subFileSystem, baseFileSystem, path);
}
public static Result CreateSubFileSystemImpl(out IFileSystem subFileSystem, IFileSystem baseFileSystem, string path)
{
UnsafeHelpers.SkipParamInit(out subFileSystem);
if (path == null) return ResultFs.NullptrArgument.Log();
Result rc = baseFileSystem.OpenDirectory(out IDirectory _, path.ToU8Span(), OpenDirectoryMode.Directory);
if (rc.IsFailure()) return rc;
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem fs, baseFileSystem, path.ToU8String());
subFileSystem = fs;
return rc;
}
public static Result VerifyHostPath(U8Span path)
{
if (path.IsEmpty())
return Result.Success;
if (path[0] != StringTraits.DirectorySeparator)
return ResultFs.InvalidPathFormat.Log();
U8Span path2 = path.Slice(1);
if (path2.IsEmpty())
return Result.Success;
int skipLength = WindowsPath.GetWindowsPathSkipLength(path2);
int remainingLength = PathTools.MaxPathLength - skipLength;
Result rc = PathUtility.VerifyPath(null, path2.Slice(skipLength), remainingLength, remainingLength);
if (rc.IsFailure()) return rc;
using var normalizer = new PathNormalizer(path, PathNormalizer.Option.PreserveUnc);
return normalizer.Result;
}
public static bool UseDeviceUniqueSaveMac(SaveDataSpaceId spaceId)
{
return spaceId == SaveDataSpaceId.System ||
spaceId == SaveDataSpaceId.User ||
spaceId == SaveDataSpaceId.Temporary ||
spaceId == SaveDataSpaceId.ProperSystem ||
spaceId == SaveDataSpaceId.SafeMode;
}
}
}