Update SaveDataFileSystemService for FS 17

This commit is contained in:
Alex Barney 2024-02-03 22:58:20 -07:00
parent e6d32b96d0
commit 037c5ace9e
7 changed files with 208 additions and 74 deletions

View file

@ -11,21 +11,37 @@ namespace LibHac.Fs;
public struct PathFlags
{
private uint _value;
private PathFormatFlags _formatFlags;
public void AllowWindowsPath() => _value |= 1 << 0;
public void AllowRelativePath() => _value |= 1 << 1;
public void AllowEmptyPath() => _value |= 1 << 2;
public void AllowMountName() => _value |= 1 << 3;
public void AllowBackslash() => _value |= 1 << 4;
public void AllowAllCharacters() => _value |= 1 << 5;
[Flags]
public enum PathFormatFlags
{
AllowWindowsPath = 1 << 0,
AllowRelativePath = 1 << 1,
AllowEmptyPath = 1 << 2,
AllowMountName = 1 << 3,
AllowBackslash = 1 << 4,
AllowInvalidCharacter = 1 << 5
}
public readonly bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0;
public readonly bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0;
public readonly bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0;
public readonly bool IsMountNameAllowed() => (_value & (1 << 3)) != 0;
public readonly bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
public readonly bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0;
public PathFlags(PathFormatFlags formatFlags)
{
_formatFlags = formatFlags;
}
public void AllowWindowsPath() => _formatFlags |= PathFormatFlags.AllowWindowsPath;
public void AllowRelativePath() => _formatFlags |= PathFormatFlags.AllowRelativePath;
public void AllowEmptyPath() => _formatFlags |= PathFormatFlags.AllowEmptyPath;
public void AllowMountName() => _formatFlags |= PathFormatFlags.AllowMountName;
public void AllowBackslash() => _formatFlags |= PathFormatFlags.AllowBackslash;
public void AllowInvalidCharacter() => _formatFlags |= PathFormatFlags.AllowInvalidCharacter;
public readonly bool IsWindowsPathAllowed() => (_formatFlags & PathFormatFlags.AllowWindowsPath) != 0;
public readonly bool IsRelativePathAllowed() => (_formatFlags & PathFormatFlags.AllowRelativePath) != 0;
public readonly bool IsEmptyPathAllowed() => (_formatFlags & PathFormatFlags.AllowEmptyPath) != 0;
public readonly bool IsMountNameAllowed() => (_formatFlags & PathFormatFlags.AllowMountName) != 0;
public readonly bool IsBackslashAllowed() => (_formatFlags & PathFormatFlags.AllowBackslash) != 0;
public readonly bool IsInvalidCharacterAllowed() => (_formatFlags & PathFormatFlags.AllowInvalidCharacter) != 0;
}
/// <summary>

View file

@ -517,7 +517,7 @@ public static class PathFormatter
return Result.Success;
}
res = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer, flags.AreAllCharactersAllowed());
res = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer, flags.IsInvalidCharacterAllowed());
if (res.IsFailure()) return res.Miss();
totalLength += length;
@ -639,7 +639,7 @@ public static class PathFormatter
}
res = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative,
flags.AreAllCharactersAllowed());
flags.IsInvalidCharacterAllowed());
if (res.IsFailure()) return res.Miss();
return Result.Success;

View file

@ -293,6 +293,8 @@ public class FileSystemInterfaceAdapter : IFileSystemSf
_selfReference.Destroy();
}
public static PathFlags GetDefaultPathFlags() => new PathFlags();
private static ReadOnlySpan<byte> RootDir => "/"u8;
private Result SetUpPath(ref Path fsPath, ref readonly PathSf sfPath)

View file

@ -115,19 +115,57 @@ public static class SaveDataProperties
public static bool IsObsoleteSystemSaveData(in SaveDataInfo info)
{
foreach (ulong id in ObsoleteSystemSaveDataIdList)
{
if (info.StaticSaveDataId == id)
return true;
}
return false;
throw new NotImplementedException();
}
public static bool IsWipingNeededAtCleanUp(in SaveDataInfo info)
{
return false;
throw new NotImplementedException();
switch (info.Type)
{
case SaveDataType.System:
break;
case SaveDataType.Account:
case SaveDataType.Bcat:
case SaveDataType.Device:
case SaveDataType.Temporary:
case SaveDataType.Cache:
return true;
default:
Abort.UnexpectedDefault();
break;
}
foreach (ulong id in SystemSaveDataIdWipingExceptionList)
{
if (info.StaticSaveDataId == id)
return false;
}
return true;
}
public static bool IsValidSpaceIdForSaveDataMover(SaveDataType type, SaveDataSpaceId spaceId)
{
throw new NotImplementedException();
switch (type)
{
case SaveDataType.System:
case SaveDataType.Account:
case SaveDataType.Bcat:
case SaveDataType.Device:
case SaveDataType.Temporary:
return false;
case SaveDataType.Cache:
return spaceId == SaveDataSpaceId.User || spaceId == SaveDataSpaceId.SdUser;
default:
Abort.UnexpectedDefault();
return default;
}
}
public static bool IsReconstructible(SaveDataType type, SaveDataSpaceId spaceId)
@ -161,4 +199,16 @@ public static class SaveDataProperties
return default;
}
}
private static ReadOnlySpan<ulong> ObsoleteSystemSaveDataIdList => [0x8000000000000060, 0x8000000000000075];
private static ReadOnlySpan<ulong> SystemSaveDataIdWipingExceptionList =>
[
0x8000000000000040, 0x8000000000000041, 0x8000000000000043, 0x8000000000000044, 0x8000000000000045,
0x8000000000000046, 0x8000000000000047, 0x8000000000000048, 0x8000000000000049, 0x800000000000004A,
0x8000000000000070, 0x8000000000000071, 0x8000000000000072, 0x8000000000000074, 0x8000000000000076,
0x8000000000000077, 0x8000000000000090, 0x8000000000000091, 0x8000000000000092, 0x80000000000000B0,
0x80000000000000C1, 0x80000000000000C2, 0x8000000000000120, 0x8000000000000121, 0x8000000000000180,
0x8000000000010003, 0x8000000000010004
];
}

View file

@ -32,6 +32,12 @@ using Utility = LibHac.FsSystem.Utility;
namespace LibHac.FsSrv;
/// <summary>
/// Creates locks for incrementing and decrementing the save data open-count semaphore
/// from a <see cref="SaveDataFileSystemService"/> to keep track of how many save data files are currently open.
/// </summary>
/// <remarks>Used by objects such as <see cref="IFileSystem"/>s that open save data files.
/// <br/>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
file class SaveDataOpenCountAdapter : IEntryOpenCountSemaphoreManager
{
private SharedRef<SaveDataFileSystemService> _saveService;
@ -115,7 +121,7 @@ file static class Anonymous
: SaveDataFormatType.NoJournal;
}
public static Result CheckOpenSaveDataInfoReaderAccessControl(ProgramInfo programInfo, SaveDataSpaceId spaceId)
public static Result CheckOpenSaveDataInfoReaderAccessControl(ProgramInfo programInfo, ulong processId, SaveDataSpaceId spaceId)
{
Assert.SdkNotNull(programInfo);
@ -142,6 +148,10 @@ file static class Anonymous
}
}
/// <summary>
/// Determines if a specified program has access to perform various functions on a specified save data.
/// </summary>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
file static class SaveDataAccessibilityChecker
{
public delegate Result ExtraDataReader(out SaveDataExtraData extraData);
@ -565,7 +575,7 @@ file static class SaveDataAccessibilityChecker
/// </summary>
/// <remarks>FS will have one instance of this class for every connected process.
/// The FS permissions of the calling process are checked on every function call.
/// <br/>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
/// <br/>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISaveDataMultiCommitCoreInterface
{
private const int OpenEntrySemaphoreCount = 256;
@ -761,8 +771,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return DeleteSaveDataFileSystemCommon(spaceId, saveDataId).Ret();
}
public Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId,
in SaveDataAttribute attribute)
public Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
@ -1725,8 +1734,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var fileSystem = new SharedRef<IFileSystem>();
// Open the file system
res = OpenSaveDataFileSystemCore(ref fileSystem.Ref, out ulong saveDataId, spaceId, in attribute,
openReadOnly, cacheExtraData: true);
res = OpenSaveDataFileSystemCore(ref fileSystem.Ref, out ulong saveDataId, spaceId, in attribute, openReadOnly,
cacheExtraData: true);
if (res.IsFailure()) return res.Miss();
// Can't use attribute in a closure, so copy the needed field
@ -1764,9 +1773,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var openCountFileSystem = new SharedRef<IFileSystem>(new OpenCountFileSystem(ref asyncFileSystem.Ref,
ref openEntryCountAdapter.Ref, ref mountCountSemaphore.Ref));
var pathFlags = new PathFlags();
PathFlags pathFlags = FileSystemInterfaceAdapter.GetDefaultPathFlags();
pathFlags.AllowBackslash();
pathFlags.AllowAllCharacters();
pathFlags.AllowInvalidCharacter();
using SharedRef<IFileSystemSf> fileSystemAdapter =
FileSystemInterfaceAdapter.CreateShared(ref openCountFileSystem.Ref, pathFlags, false);
@ -1862,7 +1871,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result ReadExtraData(out SaveDataExtraData data)
{
using Path savePath = _saveDataRootPath.DangerousGetPath();
return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, type, in savePath);
return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, type, in savePath).Ret();
}
// Check if we have permissions to open this save data
@ -1891,25 +1900,23 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var openCountFileSystem = new SharedRef<IFileSystem>(
new OpenCountFileSystem(ref asyncFileSystem.Ref, ref openEntryCountAdapter.Ref));
var pathFlags = new PathFlags();
PathFlags pathFlags = FileSystemInterfaceAdapter.GetDefaultPathFlags();
pathFlags.AllowBackslash();
pathFlags.AllowAllCharacters();
pathFlags.AllowInvalidCharacter();
using SharedRef<IFileSystemSf> fileSystemAdapter =
FileSystemInterfaceAdapter.CreateShared(ref openCountFileSystem.Ref, pathFlags,
allowAllOperations: false);
FileSystemInterfaceAdapter.CreateShared(ref openCountFileSystem.Ref, pathFlags, allowAllOperations: false);
outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
return Result.Success;
}
// ReSharper disable once UnusedParameter.Local
// Nintendo used isTemporarySaveData in older FS versions, but never removed the parameter.
private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData, SaveDataSpaceId spaceId,
// The unused parameter hasn't been removed because this method is part of an interface. It was used in older FS versions.
private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData outExtraData, SaveDataSpaceId spaceId,
ulong saveDataId, bool isTemporarySaveData)
{
UnsafeHelpers.SkipParamInit(out extraData);
UnsafeHelpers.SkipParamInit(out outExtraData);
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
@ -1921,14 +1928,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (res.IsFailure()) return res.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
return _serviceImpl.ReadSaveDataFileSystemExtraData(out extraData, spaceId, saveDataId, key.Type,
return _serviceImpl.ReadSaveDataFileSystemExtraData(out outExtraData, spaceId, saveDataId, key.Type,
in saveDataRootPath).Ret();
}
private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData,
private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData outExtraData,
Optional<SaveDataSpaceId> spaceId, ulong saveDataId, in SaveDataExtraData extraDataMask)
{
UnsafeHelpers.SkipParamInit(out extraData);
UnsafeHelpers.SkipParamInit(out outExtraData);
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
@ -1971,7 +1978,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
res = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue _, saveDataId);
if (res.IsFailure()) return res.Miss();
resolvedSpaceId = spaceId.ValueRo;
resolvedSpaceId = spaceId.Value;
res = accessor.Get.GetInterface().GetKey(out key, saveDataId);
if (res.IsFailure()) return res.Miss();
@ -1984,7 +1991,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
in savePath);
}
res = SaveDataAccessibilityChecker.CheckReadExtraData(resolvedSpaceId, in key, in extraDataMask, programInfo, _processId, ReadExtraData);
res = SaveDataAccessibilityChecker.CheckReadExtraData(resolvedSpaceId, in key, in extraDataMask, programInfo,
_processId, ReadExtraData);
if (res.IsFailure()) return res.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
@ -1993,7 +2001,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (res.IsFailure()) return res.Miss();
MaskExtraData(ref tempExtraData, in extraDataMask);
extraData = tempExtraData;
outExtraData = tempExtraData;
return Result.Success;
}
@ -2003,6 +2011,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraData.IsNull)
return ResultFs.NullptrArgument.Log();
// Make a mask for reading the entire extra data
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
@ -2017,6 +2028,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraData.IsNull)
return ResultFs.NullptrArgument.Log();
ref SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
Result res = GetProgramInfo(out ProgramInfo programInfo);
@ -2045,6 +2059,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraData.IsNull)
return ResultFs.NullptrArgument.Log();
ref SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
// Make a mask for reading the entire extra data
@ -2060,9 +2077,15 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraDataMask.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraDataMask.IsNull)
return ResultFs.NullptrArgument.Log();
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraData.IsNull)
return ResultFs.NullptrArgument.Log();
ref readonly SaveDataExtraData maskRef = ref extraDataMask.As<SaveDataExtraData>();
ref SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
@ -2101,13 +2124,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (res.IsFailure()) return res.Miss();
using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
res = OpenSaveDataIndexerAccessor(ref accessor.Ref, spaceId);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
if (res.IsFailure()) return res.Miss();
SaveDataType saveDataType = key.Type;
Result ReadExtraData(out SaveDataExtraData data)
{
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
@ -2120,13 +2144,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
res = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraDataModify, spaceId, saveDataId,
key.Type, in saveDataRootPath);
saveDataType, in saveDataRootPath);
if (res.IsFailure()) return res.Miss();
ModifySaveDataExtraData(ref extraDataModify, in extraData, in extraDataMask);
return _serviceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraDataModify,
in saveDataRootPath, key.Type, updateTimeStamp: false).Ret();
in saveDataRootPath, saveDataType, updateTimeStamp: false).Ret();
}
public Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraData)
@ -2134,6 +2158,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraData.IsNull)
return ResultFs.NullptrArgument.Log();
ref readonly SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
SaveDataExtraData extraDataMask = default;
@ -2167,9 +2194,15 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraData.IsNull)
return ResultFs.NullptrArgument.Log();
if (extraDataMask.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
if (extraDataMask.IsNull)
return ResultFs.NullptrArgument.Log();
ref readonly SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
ref readonly SaveDataExtraData maskRef = ref extraDataMask.As<SaveDataExtraData>();
@ -2213,7 +2246,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
res = CheckOpenSaveDataInfoReaderAccessControl(programInfo, spaceId);
res = CheckOpenSaveDataInfoReaderAccessControl(programInfo, _processId, spaceId);
if (res.IsFailure()) return res.Miss();
using var filterReader = new UniqueRef<SaveDataInfoFilterReader>();
@ -2250,7 +2283,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (!programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReaderForInternal))
return ResultFs.PermissionDenied.Log();
res = CheckOpenSaveDataInfoReaderAccessControl(programInfo, spaceId);
res = CheckOpenSaveDataInfoReaderAccessControl(programInfo, _processId, spaceId);
if (res.IsFailure()) return res.Miss();
using var filterReader = new UniqueRef<SaveDataInfoFilterReader>();
@ -2303,17 +2336,20 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (saveDataInfoBuffer.Size != Unsafe.SizeOf<SaveDataInfo>())
return ResultFs.InvalidArgument.Log();
if (saveDataInfoBuffer.IsNull)
return ResultFs.NullptrArgument.Log();
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
res = CheckOpenSaveDataInfoReaderAccessControl(programInfo, spaceId);
res = CheckOpenSaveDataInfoReaderAccessControl(programInfo, _processId, spaceId);
if (res.IsFailure())
{
if (!ResultFs.PermissionDenied.Includes(res))
return res;
return res.Miss();
// Don't have full info reader permissions. Check if we have find permissions.
res = SaveDataAccessibilityChecker.CheckFind(spaceId, in filter, programInfo, _processId);
@ -2328,7 +2364,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in tempFilter);
return FindSaveDataWithFilterImpl(out count, out saveDataInfoBuffer.As<SaveDataInfo>(), spaceId, in infoFilter).Ret();
res = FindSaveDataWithFilterImpl(out var outCount, out saveDataInfoBuffer.As<SaveDataInfo>(), spaceId, in infoFilter);
if (res.IsFailure()) return res.Miss();
count = outCount;
return Result.Success;
}
private Result CreateEmptyThumbnailFile(SaveDataSpaceId spaceId, ulong saveDataId)
@ -2519,10 +2559,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (res.IsFailure())
{
spaceId = SaveDataSpaceId.User;
if (!ResultFs.TargetNotFound.Includes(res))
return res;
if (ResultFs.TargetNotFound.Includes(res))
{
spaceId = SaveDataSpaceId.User;
}
else
{
return res.Miss();
}
}
return OpenSaveDataInfoReaderOnlyCacheStorage(ref outInfoReader, spaceId).Ret();
@ -2536,7 +2580,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
if (spaceId != SaveDataSpaceId.SdUser && spaceId != SaveDataSpaceId.User)
if (spaceId != SaveDataSpaceId.User && spaceId != SaveDataSpaceId.SdUser)
return ResultFs.InvalidSaveDataSpaceId.Log();
using var filterReader = new UniqueRef<SaveDataInfoFilterReader>();
@ -2686,7 +2730,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (res.IsFailure()) return res.Miss();
ulong resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId).Value;
return GetCacheStorageSpaceId(out spaceId, resolvedProgramId);
return GetCacheStorageSpaceId(out spaceId, resolvedProgramId).Ret();
}
private Result GetCacheStorageSpaceId(out SaveDataSpaceId spaceId, ulong programId)
@ -2694,7 +2738,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
UnsafeHelpers.SkipParamInit(out spaceId);
Result res;
// Cache storage on the SD card will always take priority over case storage in NAND
// Cache storage on the SD card will always take priority over cache storage in NAND
if (_serviceImpl.IsSdCardAccessible())
{
res = DoesCacheStorageExist(out bool existsOnSdCard, SaveDataSpaceId.SdUser);
@ -2722,8 +2766,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
UnsafeHelpers.SkipParamInit(out exists);
var filter = new SaveDataInfoFilter(saveSpaceId, new ProgramId(programId), SaveDataType.Cache,
userId: default, saveDataId: default, index: default, (int)SaveDataRank.Primary);
var filter = new SaveDataInfoFilter(ConvertToRealSpaceId(saveSpaceId), new ProgramId(programId),
SaveDataType.Cache, userId: default, saveDataId: default, index: default, (int)SaveDataRank.Primary);
Result result = FindSaveDataWithFilterImpl(out long count, out _, saveSpaceId, in filter);
if (result.IsFailure()) return result;
@ -2954,6 +2998,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (bufferIdCount > 0)
{
if (idBuffer.IsNull)
return ResultFs.NullptrArgument.Log();
ids = idBuffer.AsSpan<Ncm.ApplicationId>();
if (ids.Length < bufferIdCount)
@ -3015,8 +3062,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
res = _serviceImpl.OpenSaveDataFileSystem(ref fileSystem.Ref, value.SpaceId, saveDataId,
in saveDataRootPath,
openReadOnly: false, saveDataType, cacheExtraData: true);
in saveDataRootPath, openReadOnly: false, saveDataType, cacheExtraData: true);
if (res.IsFailure()) return res.Miss();
// Verify the file system.
@ -3027,7 +3073,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
finally
{
// Make sure we don't leak any invalid data.
// Make sure we don't leak any data.
workBuffer.Buffer.Clear();
}
}
@ -3305,8 +3351,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result RecoverProvisionallyCommittedSaveData(in SaveDataInfo saveInfo, bool doRollback)
{
ulong saveDataId = IsStaticSaveDataIdValueRange(saveInfo.SaveDataId)
? saveInfo.SaveDataId
: InvalidSystemSaveDataId;
Result res = SaveDataAttribute.Make(out SaveDataAttribute attribute, saveInfo.ProgramId, saveInfo.Type,
saveInfo.UserId, saveInfo.SaveDataId, saveInfo.Index);
saveInfo.UserId, saveDataId, saveInfo.Index);
if (res.IsFailure()) return res.Miss();
using var fileSystem = new SharedRef<IFileSystem>();
@ -3333,8 +3383,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
using SharedRef<SaveDataFileSystemService> saveService = GetSharedFromThis();
Result res = Utility.MakeUniqueLockWithPin(ref outSemaphoreLock, _openEntryCountSemaphore,
ref saveService.Ref);
Result res = Utility.MakeUniqueLockWithPin(ref outSemaphoreLock, _openEntryCountSemaphore, ref saveService.Ref);
if (res.IsFailure()) return res.Miss();
return Result.Success;
@ -3344,8 +3393,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
using SharedRef<SaveDataFileSystemService> saveService = GetSharedFromThis();
Result res = Utility.MakeUniqueLockWithPin(ref outSemaphoreLock, _saveDataMountCountSemaphore,
ref saveService.Ref);
Result res = Utility.MakeUniqueLockWithPin(ref outSemaphoreLock, _saveDataMountCountSemaphore, ref saveService.Ref);
if (res.IsFailure()) return res.Miss();
return Result.Success;
@ -3407,8 +3455,23 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (isInitialOpen)
{
CleanUpSaveData(accessor.Get).IgnoreResult();
CompleteSaveDataExtension(accessor.Get).IgnoreResult();
Unsafe.SkipInit(out Array80<byte> stringBuffer);
Result result = CleanUpSaveData(accessor.Get);
if (result.IsFailure())
{
var sb = new U8StringBuilder(stringBuffer, true);
sb.Append("[fs] Failed to clean up save data ("u8).AppendFormat(result.Value, 'x').Append(")\n"u8);
Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info, sb.Buffer);
}
result = CompleteSaveDataExtension(accessor.Get);
if (result.IsFailure())
{
var sb = new U8StringBuilder(stringBuffer, true);
sb.Append("[fs] Failed to complete save data extension ("u8).AppendFormat(result.Value, 'x').Append(")\n"u8);
Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info, sb.Buffer);
}
}
outAccessor.Set(ref accessor.Ref);
@ -3419,14 +3482,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt,
bool leaveUnfinalized)
{
return CreateSaveDataFileSystemCore(in attribute, in creationInfo, in metaInfo, in hashSalt,
leaveUnfinalized);
return CreateSaveDataFileSystemCore(in attribute, in creationInfo, in metaInfo, in hashSalt, leaveUnfinalized);
}
Result ISaveDataTransferCoreInterface.ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData,
Result ISaveDataTransferCoreInterface.ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData outExtraData,
SaveDataSpaceId spaceId, ulong saveDataId, bool isTemporarySaveData)
{
return ReadSaveDataFileSystemExtraDataCore(out extraData, spaceId, saveDataId, isTemporarySaveData);
return ReadSaveDataFileSystemExtraDataCore(out outExtraData, spaceId, saveDataId, isTemporarySaveData);
}
Result ISaveDataTransferCoreInterface.WriteSaveDataFileSystemExtraDataCore(SaveDataSpaceId spaceId,

View file

@ -11,6 +11,7 @@ public readonly ref struct InBuffer
public int Size => _buffer.Length;
public ReadOnlySpan<byte> Buffer => _buffer;
public bool IsNull => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
public InBuffer(ReadOnlySpan<byte> buffer)
{
@ -46,6 +47,7 @@ public readonly ref struct OutBuffer
public int Size => _buffer.Length;
public Span<byte> Buffer => _buffer;
public bool IsNull => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
public OutBuffer(Span<byte> buffer)
{
@ -81,6 +83,7 @@ public readonly ref struct InArray<T> where T : unmanaged
public int Size => _array.Length;
public ReadOnlySpan<T> Array => _array;
public bool IsNull => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_array));
public InArray(ReadOnlySpan<T> array)
{
@ -96,6 +99,7 @@ public readonly ref struct OutArray<T> where T : unmanaged
public int Size => _array.Length;
public Span<T> Array => _array;
public bool IsNull => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_array));
public OutArray(Span<T> array)
{

View file

@ -455,7 +455,7 @@ public class PathFormatterTests
flags.AllowWindowsPath();
break;
case 'C':
flags.AllowAllCharacters();
flags.AllowInvalidCharacter();
break;
default:
throw new NotSupportedException();