mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Update the FsSrv namespace to use Fs.Path
This commit is contained in:
parent
9a97e5ef3e
commit
6ba10074a3
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) '/'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user