diff --git a/src/LibHac/Diag/Log.cs b/src/LibHac/Diag/Log.cs
index e452a426..df172d06 100644
--- a/src/LibHac/Diag/Log.cs
+++ b/src/LibHac/Diag/Log.cs
@@ -43,4 +43,7 @@ public static class Log
diag.LogImpl(in metaData, message);
}
+
+ /// "$"
+ public static ReadOnlySpan EmptyModuleName => new[] { (byte)'$' }; // "$"
}
diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs
index 89dd8230..aaa52a1b 100644
--- a/src/LibHac/Fs/Common/Path.cs
+++ b/src/LibHac/Fs/Common/Path.cs
@@ -94,9 +94,17 @@ public ref struct Path
[DebuggerDisplay("{" + nameof(ToString) + "(),nq}")]
public struct Stored : IDisposable
{
+ private static readonly byte[] EmptyBuffer = { 0 };
+
private byte[] _buffer;
private int _length;
+ public Stored()
+ {
+ _buffer = EmptyBuffer;
+ _length = 0;
+ }
+
public void Dispose()
{
byte[] buffer = Shared.Move(ref _buffer);
@@ -160,7 +168,8 @@ public ref struct Path
byte[] oldBuffer = _buffer;
_buffer = buffer;
- if (oldBuffer is not null)
+ // Check if the buffer is longer than 1 so we don't try to return EmptyBuffer to the pool.
+ if (oldBuffer?.Length > 1)
ArrayPool.Shared.Return(oldBuffer);
return Result.Success;
diff --git a/src/LibHac/Fs/Common/SaveDataTypes.cs b/src/LibHac/Fs/Common/SaveDataTypes.cs
index d5db41c8..e7f98453 100644
--- a/src/LibHac/Fs/Common/SaveDataTypes.cs
+++ b/src/LibHac/Fs/Common/SaveDataTypes.cs
@@ -16,8 +16,7 @@ public enum SaveDataSpaceId : byte
Temporary = 3,
SdUser = 4,
ProperSystem = 100,
- SafeMode = 101,
- BisAuto = 127
+ SafeMode = 101
}
public enum SaveDataType : byte
@@ -99,6 +98,7 @@ public struct SaveDataExtraData
public ulong OwnerId;
public long TimeStamp;
public SaveDataFlags Flags;
+ public SaveDataFormatType FormatType;
public long DataSize;
public long JournalSize;
public long CommitId;
@@ -371,7 +371,7 @@ internal static class SaveDataTypesValidity
{
public static bool IsValid(in SaveDataAttribute attribute)
{
- return IsValid(in attribute.Type)&& IsValid(in attribute.Rank);
+ return IsValid(in attribute.Type) && IsValid(in attribute.Rank);
}
public static bool IsValid(in SaveDataCreationInfo creationInfo)
diff --git a/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs b/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs
index 09433d0d..6492747a 100644
--- a/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs
+++ b/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs
@@ -4,8 +4,9 @@ namespace LibHac.Fs.Impl;
internal readonly struct SaveDataMetaPolicy
{
+ internal const int ThumbnailFileSize = 0x40060;
+
private readonly SaveDataType _type;
- private const int ThumbnailFileSize = 0x40060;
public SaveDataMetaPolicy(SaveDataType saveType)
{
@@ -39,4 +40,4 @@ internal readonly struct SaveDataMetaPolicy
GenerateMetaInfo(out SaveDataMetaInfo metaInfo);
return metaInfo.Type;
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FileSystemProxyImpl.cs b/src/LibHac/FsSrv/FileSystemProxyImpl.cs
index 60e783a4..71ae758f 100644
--- a/src/LibHac/FsSrv/FileSystemProxyImpl.cs
+++ b/src/LibHac/FsSrv/FileSystemProxyImpl.cs
@@ -352,6 +352,14 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
return saveFsService.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in creationInfo);
}
+ public Result CreateSaveDataFileSystemWithCreationInfo2(in SaveDataCreationInfo2 creationInfo)
+ {
+ Result rc = GetSaveDataFileSystemService(out SaveDataFileSystemService saveFsService);
+ if (rc.IsFailure()) return rc;
+
+ return saveFsService.CreateSaveDataFileSystemWithCreationInfo2(in creationInfo);
+ }
+
public Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize,
long journalSize)
{
diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
index 0f02dcf8..0e131516 100644
--- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs
+++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
@@ -136,7 +136,7 @@ public static class FileSystemServerInitializer
saveFsServiceConfig.BufferManager = bufferManager;
saveFsServiceConfig.GenerateRandomData = config.RandomGenerator;
saveFsServiceConfig.IsPseudoSaveData = () => true;
- saveFsServiceConfig.MaxSaveFsCacheCount = 1;
+ saveFsServiceConfig.SaveDataFileSystemCacheCount = 1;
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
saveFsServiceConfig.FsServer = server;
diff --git a/src/LibHac/FsSrv/ISaveDataIndexerManager.cs b/src/LibHac/FsSrv/ISaveDataIndexerManager.cs
index 7a4fb4f4..62bfe637 100644
--- a/src/LibHac/FsSrv/ISaveDataIndexerManager.cs
+++ b/src/LibHac/FsSrv/ISaveDataIndexerManager.cs
@@ -5,7 +5,7 @@ namespace LibHac.FsSrv;
public interface ISaveDataIndexerManager
{
- Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor, out bool neededInit, SaveDataSpaceId spaceId);
+ Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor, out bool isInitialOpen, SaveDataSpaceId spaceId);
void ResetIndexer(SaveDataSpaceId spaceId);
void InvalidateIndexer(SaveDataSpaceId spaceId);
}
diff --git a/src/LibHac/FsSrv/Impl/ISaveDataTransferCoreInterface.cs b/src/LibHac/FsSrv/Impl/ISaveDataTransferCoreInterface.cs
index 2e0a35a0..6b73228e 100644
--- a/src/LibHac/FsSrv/Impl/ISaveDataTransferCoreInterface.cs
+++ b/src/LibHac/FsSrv/Impl/ISaveDataTransferCoreInterface.cs
@@ -2,15 +2,15 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
+using LibHac.Os;
using LibHac.Util;
-using IFileSf = LibHac.FsSrv.Sf.IFile;
namespace LibHac.FsSrv.Impl;
public interface ISaveDataTransferCoreInterface : IDisposable
{
- Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId);
- Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize);
+ Result GetFreeSpaceSizeForSaveData(out long outFreeSpaceSize, SaveDataSpaceId spaceId);
+ Result QuerySaveDataTotalSize(out long outTotalSize, long dataSize, long journalSize);
Result CheckSaveDataFile(long saveDataId, SaveDataSpaceId spaceId);
Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional hashSalt, bool leaveUnfinalized);
Result GetSaveDataInfo(out SaveDataInfo saveInfo, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
@@ -18,13 +18,15 @@ public interface ISaveDataTransferCoreInterface : IDisposable
Result WriteSaveDataFileSystemExtraDataCore(SaveDataSpaceId spaceId, ulong saveDataId, in SaveDataExtraData extraData, SaveDataType type, bool updateTimeStamp);
Result FinalizeSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
Result CancelSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
- Result OpenSaveDataFile(ref SharedRef file, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, SaveDataMetaType metaType);
- Result OpenSaveDataMetaFileRaw(ref SharedRef file, SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode);
+ Result OpenSaveDataFile(ref SharedRef oufFile, SaveDataSpaceId spaceId, ulong saveDataId, OpenMode mode);
+ Result OpenSaveDataMetaFileRaw(ref SharedRef outFile, SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode);
Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey);
+ Result OpenSaveDataFileSystemCore(ref SharedRef outFileSystem, out ulong outSaveDataId, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData);
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2);
Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state);
Result SetSaveDataRank(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataRank rank);
Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor, SaveDataSpaceId spaceId);
-}
+ bool IsProhibited(ref UniqueLock outLock, ApplicationId applicationId);
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs
index ce2c856f..af60437d 100644
--- a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs
+++ b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs
@@ -46,7 +46,7 @@ internal class MultiCommitManager : IMultiCommitManager
{
private const int MaxFileSystemCount = 10;
- public const ulong ProgramId = 0x0100000000000000;
+ public const ulong ProgramId = 0;
public const ulong SaveDataId = 0x8000000000000001;
private const long SaveDataSize = 0xC000;
private const long SaveJournalSize = 0xC000;
diff --git a/src/LibHac/FsSrv/SaveDataFileSystemService.cs b/src/LibHac/FsSrv/SaveDataFileSystemService.cs
index bfa316c2..b9bd2f43 100644
--- a/src/LibHac/FsSrv/SaveDataFileSystemService.cs
+++ b/src/LibHac/FsSrv/SaveDataFileSystemService.cs
@@ -1,13 +1,17 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using LibHac.Common;
+using LibHac.Diag;
using LibHac.Fs;
+using LibHac.Fs.Impl;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Kvdb;
using LibHac.Ncm;
+using LibHac.Os;
using LibHac.Sf;
using LibHac.Util;
@@ -28,7 +32,7 @@ namespace LibHac.FsSrv;
///
/// 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.
-///
Based on FS 10.2.0 (nnSdk 10.6.0)
+///
Based on FS 14.1.0 (nnSdk 14.3.0)
internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISaveDataMultiCommitCoreInterface
{
private const int OpenEntrySemaphoreCount = 256;
@@ -51,14 +55,6 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
private SharedRef GetSharedMultiCommitInterfaceFromThis() =>
SharedRef.Create(in _selfReference);
- public SaveDataFileSystemService(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
- {
- _serviceImpl = serviceImpl;
- _processId = processId;
- _openEntryCountSemaphore = new SemaphoreAdapter(OpenEntrySemaphoreCount, OpenEntrySemaphoreCount);
- _saveDataMountCountSemaphore = new SemaphoreAdapter(SaveMountSemaphoreCount, SaveMountSemaphoreCount);
- }
-
public static SharedRef CreateShared(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
{
// Create the service
@@ -80,19 +76,21 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
_saveService = SharedRef.CreateMove(ref saveService);
}
- public Result TryAcquireEntryOpenCountSemaphore(ref UniqueRef outSemaphore)
- {
- return _saveService.Get.TryAcquireSaveDataEntryOpenCountSemaphore(ref outSemaphore);
- }
-
public void Dispose()
{
_saveService.Destroy();
}
+
+ public Result TryAcquireEntryOpenCountSemaphore(ref UniqueRef outSemaphore)
+ {
+ return _saveService.Get.TryAcquireSaveDataEntryOpenCountSemaphore(ref outSemaphore).Ret();
+ }
}
- private Result CheckOpenSaveDataInfoReaderAccessControl(ProgramInfo programInfo, SaveDataSpaceId spaceId)
+ private static Result CheckOpenSaveDataInfoReaderAccessControl(ProgramInfo programInfo, SaveDataSpaceId spaceId)
{
+ Assert.SdkNotNull(programInfo);
+
switch (spaceId)
{
case SaveDataSpaceId.System:
@@ -119,30 +117,29 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
public delegate Result ExtraDataGetter(out SaveDataExtraData extraData);
- public static Result CheckCreate(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo,
- ProgramInfo programInfo, ProgramId programId)
+ public static Result CheckCreate(in SaveDataAttribute attribute, ulong ownerId, ProgramInfo programInfo,
+ ProgramId programId)
{
AccessControl accessControl = programInfo.AccessControl;
if (SaveDataProperties.IsSystemSaveData(attribute.Type))
{
- if (creationInfo.OwnerId == programInfo.ProgramIdValue)
+ if (ownerId != programInfo.ProgramIdValue)
{
- bool canAccess = accessControl.CanCall(OperationType.CreateSystemSaveData);
+ // If the program doesn't own the created save data it needs either the permission to create
+ // any system save data or it needs explicit access to the owner's save data.
+ Accessibility accessibility = accessControl.GetAccessibilitySaveDataOwnedBy(ownerId);
+
+ bool canAccess =
+ accessControl.CanCall(OperationType.CreateSystemSaveData) &&
+ accessControl.CanCall(OperationType.CreateOthersSystemSaveData) || accessibility.CanWrite;
if (!canAccess)
return ResultFs.PermissionDenied.Log();
}
else
{
- // If the program doesn't own the created save data it needs either the permission to create
- // any system save data or it needs explicit access to the owner's save data.
- Accessibility accessibility =
- accessControl.GetAccessibilitySaveDataOwnedBy(creationInfo.OwnerId);
-
- bool canAccess =
- accessControl.CanCall(OperationType.CreateSystemSaveData) &&
- accessControl.CanCall(OperationType.CreateOthersSystemSaveData) || accessibility.CanWrite;
+ bool canAccess = accessControl.CanCall(OperationType.CreateSystemSaveData);
if (!canAccess)
return ResultFs.PermissionDenied.Log();
@@ -159,9 +156,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
else
{
- Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
- creationInfo.OwnerId);
- if (rc.IsFailure()) return rc;
+ Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, ownerId);
+ if (rc.IsFailure()) return rc.Miss();
// If none of the above conditions apply, the program needs write access to the owner's save data.
// The program also needs either permission to create any save data, or it must be creating its own
@@ -169,7 +165,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
bool canAccess = accessControl.CanCall(OperationType.CreateSaveData);
if (accessibility.CanWrite &&
- attribute.ProgramId == programId || attribute.ProgramId.Value == creationInfo.OwnerId)
+ attribute.ProgramId == programId || attribute.ProgramId.Value == ownerId)
{
canAccess |= accessControl.CanCall(OperationType.CreateOwnSaveData);
}
@@ -213,10 +209,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
AccessControl accessControl = programInfo.AccessControl;
Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, extraDataGetter);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- // Note: This is correct. Even if a program only has read accessibility to another program's save data,
- // Nintendo gives it full read/write accessibility as of FS 12.0.0
+ // Note: This is correct. Even if a program only has read accessibility to a save data,
+ // Nintendo allows opening it with read/write accessibility as of FS 14.0.0
if (accessibility.CanRead || accessibility.CanWrite)
return Result.Success;
@@ -257,7 +253,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Otherwise the program needs the DeleteOwnSaveData permission and write access to the save
Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, extraDataGetter);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (accessControl.CanCall(OperationType.DeleteOwnSaveData) && accessibility.CanWrite)
{
@@ -267,6 +263,65 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return ResultFs.PermissionDenied.Log();
}
+ public static Result CheckExtend(SaveDataSpaceId spaceId, in SaveDataAttribute attribute,
+ ProgramInfo programInfo, ExtraDataGetter extraDataGetter)
+ {
+ AccessControl accessControl = programInfo.AccessControl;
+
+ switch (spaceId)
+ {
+ case SaveDataSpaceId.System:
+ case SaveDataSpaceId.SdSystem:
+ case SaveDataSpaceId.ProperSystem:
+ case SaveDataSpaceId.SafeMode:
+ {
+ Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
+ extraDataGetter);
+ if (rc.IsFailure()) return rc.Miss();
+
+ // The program needs the ExtendSystemSaveData permission and either one of
+ // read/write access to the save or the ExtendOthersSystemSaveData permission
+ bool canAccess = accessControl.CanCall(OperationType.ExtendSystemSaveData) &&
+ (accessibility.CanRead && accessibility.CanWrite ||
+ accessControl.CanCall(OperationType.ExtendOthersSystemSaveData));
+
+ if (!canAccess)
+ return ResultFs.PermissionDenied.Log();
+
+ break;
+ }
+ case SaveDataSpaceId.User:
+ case SaveDataSpaceId.SdUser:
+ {
+ bool canAccess = accessControl.CanCall(OperationType.ExtendSaveData);
+
+ Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
+ extraDataGetter);
+ if (rc.IsFailure()) return rc.Miss();
+
+ if (attribute.ProgramId == programInfo.ProgramId || accessibility.CanRead)
+ {
+ canAccess |= accessControl.CanCall(OperationType.ExtendOwnSaveData);
+
+ bool hasDebugAccess = accessControl.CanCall(OperationType.DebugSaveData)
+ && attribute.Type == SaveDataType.Account
+ && attribute.UserId == UserId.InvalidId;
+
+ canAccess |= hasDebugAccess;
+
+ if (!canAccess)
+ return ResultFs.PermissionDenied.Log();
+ }
+
+ break;
+ }
+ default:
+ return ResultFs.InvalidSaveDataSpaceId.Log();
+ }
+
+ return Result.Success;
+ }
+
public static Result CheckReadExtraData(in SaveDataAttribute attribute, in SaveDataExtraData mask,
ProgramInfo programInfo, ExtraDataGetter extraDataGetter)
{
@@ -274,9 +329,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
bool canAccess = accessControl.CanCall(OperationType.ReadSaveDataFileSystemExtraData);
- Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
- extraDataGetter);
- if (rc.IsFailure()) return rc;
+ Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, extraDataGetter);
+ if (rc.IsFailure()) return rc.Miss();
SaveDataExtraData emptyMask = default;
SaveDataExtraData maskWithoutRestoreFlag = mask;
@@ -293,9 +347,15 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
canAccess |= accessibility.CanRead;
}
- else if (attribute.ProgramId == programInfo.ProgramId)
+ else if (attribute.ProgramId == programInfo.ProgramId || accessibility.CanRead)
{
canAccess |= accessControl.CanCall(OperationType.ReadOwnSaveDataFileSystemExtraData);
+
+ bool hasDebugAccess = accessControl.CanCall(OperationType.DebugSaveData)
+ && attribute.Type == SaveDataType.Account
+ && attribute.UserId == UserId.InvalidId;
+
+ canAccess |= hasDebugAccess;
}
if (!canAccess)
@@ -318,7 +378,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
extraDataGetter);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
canAccess |= accessibility.CanWrite;
}
@@ -327,7 +387,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
extraDataGetter);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
canAccess |= accessibility.CanWrite;
}
@@ -379,7 +439,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (programInfo.ProgramId == filter.Attribute.ProgramId)
{
- canAccess = programInfo.AccessControl.CanCall(OperationType.FindOwnSaveDataWithFilter);
+ AccessControl accessControl = programInfo.AccessControl;
+ canAccess = accessControl.CanCall(OperationType.FindOwnSaveDataWithFilter);
+
+ bool hasDebugAccess = accessControl.CanCall(OperationType.DebugSaveData)
+ && filter.Attribute.Type == SaveDataType.Account
+ && filter.Attribute.UserId == UserId.InvalidId;
+
+ canAccess |= hasDebugAccess;
}
else
{
@@ -392,6 +459,39 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
+ public static Result CheckOpenProhibiter(ProgramId programId, ProgramInfo programInfo)
+ {
+ Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, programId.Value);
+ if (rc.IsFailure()) return rc.Miss();
+
+ bool canAccess = programInfo.AccessControl.CanCall(OperationType.OpenSaveDataTransferProhibiter);
+
+ if (programInfo.ProgramId == programId || accessibility.CanRead)
+ {
+ canAccess |= programInfo.AccessControl.CanCall(OperationType.OpenOwnSaveDataTransferProhibiter);
+ }
+
+ if (!canAccess)
+ return ResultFs.PermissionDenied.Log();
+
+ return Result.Success;
+ }
+
+ private static Result GetAccessibilityForSaveData(out Accessibility accessibility, ProgramInfo programInfo,
+ ulong ownerId)
+ {
+ if (ownerId == programInfo.ProgramIdValue)
+ {
+ // A program always has full access to its own save data
+ accessibility = new Accessibility(true, true);
+ }
+ else
+ {
+ accessibility = programInfo.AccessControl.GetAccessibilitySaveDataOwnedBy(ownerId);
+ }
+
+ return Result.Success;
+ }
private static Result GetAccessibilityForSaveData(out Accessibility accessibility, ProgramInfo programInfo,
ExtraDataGetter extraDataGetter)
@@ -411,48 +511,186 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
// Allow access when opening a directory save FS on a dev console
- if (extraData.OwnerId == 0 && extraData.DataSize == 0 && extraData.JournalSize == 0 &&
+ if (IsDirectorySaveDataExtraData(in extraData) &&
programInfo.AccessControl.CanCall(OperationType.DebugSaveData))
{
accessibility = new Accessibility(true, true);
return Result.Success;
}
- return GetAccessibilityForSaveData(out accessibility, programInfo, extraData.OwnerId);
- }
-
- private static Result GetAccessibilityForSaveData(out Accessibility accessibility, ProgramInfo programInfo,
- ulong ownerId)
- {
- if (ownerId == programInfo.ProgramIdValue)
- {
- // A program always has full access to its own save data
- accessibility = new Accessibility(true, true);
- }
- else
- {
- accessibility = programInfo.AccessControl.GetAccessibilitySaveDataOwnedBy(ownerId);
- }
-
- return Result.Success;
+ return GetAccessibilityForSaveData(out accessibility, programInfo, extraData.OwnerId).Ret();
}
}
- public Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId)
+ public SaveDataFileSystemService(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
{
- throw new NotImplementedException();
+ _serviceImpl = serviceImpl;
+ _processId = processId;
+ using var path = new Path();
+ _openEntryCountSemaphore = new SemaphoreAdapter(OpenEntrySemaphoreCount, OpenEntrySemaphoreCount);
+ _saveDataMountCountSemaphore = new SemaphoreAdapter(SaveMountSemaphoreCount, SaveMountSemaphoreCount);
+ }
+
+ public void Dispose()
+ {
+ _openEntryCountSemaphore.Dispose();
+ _saveDataMountCountSemaphore.Dispose();
+ _selfReference.Destroy();
+ }
+
+ private Result GetProgramInfo(out ProgramInfo programInfo)
+ {
+ var registry = new ProgramRegistryImpl(_serviceImpl.FsServer);
+ return registry.GetProgramInfo(out programInfo, _processId);
+ }
+
+ private Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
+ {
+ var registry = new ProgramRegistryImpl(_serviceImpl.FsServer);
+ return registry.GetProgramInfoByProgramId(out programInfo, programId);
+ }
+
+ private static SaveDataSpaceId ConvertToRealSpaceId(SaveDataSpaceId spaceId)
+ {
+ return spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode
+ ? SaveDataSpaceId.System
+ : spaceId;
+ }
+
+ private static bool IsStaticSaveDataIdValueRange(ulong id)
+ {
+ return unchecked((long)id) < 0;
+ }
+
+ private static bool IsDirectorySaveDataExtraData(in SaveDataExtraData extraData)
+ {
+ return extraData.OwnerId == 0 && extraData.DataSize == 0 && extraData.JournalSize == 0;
+ }
+
+ private static void ModifySaveDataExtraData(ref SaveDataExtraData currentExtraData, in SaveDataExtraData extraData,
+ in SaveDataExtraData extraDataMask)
+ {
+ Span currentExtraDataBytes = SpanHelpers.AsByteSpan(ref currentExtraData);
+ ReadOnlySpan extraDataBytes = SpanHelpers.AsReadOnlyByteSpan(in extraData);
+ ReadOnlySpan extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
+
+ for (int i = 0; i < Unsafe.SizeOf(); i++)
+ {
+ currentExtraDataBytes[i] = (byte)(extraDataBytes[i] & extraDataMaskBytes[i] |
+ currentExtraDataBytes[i] & ~extraDataMaskBytes[i]);
+ }
+ }
+
+ private static void MaskExtraData(ref SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
+ {
+ Span extraDataBytes = SpanHelpers.AsByteSpan(ref extraData);
+ ReadOnlySpan extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
+
+ for (int i = 0; i < Unsafe.SizeOf(); i++)
+ {
+ extraDataBytes[i] &= extraDataMaskBytes[i];
+ }
+ }
+
+ private static StorageLayoutType DecidePossibleStorageFlag(SaveDataType type, SaveDataSpaceId spaceId)
+ {
+ if (type == SaveDataType.Cache || type == SaveDataType.Bcat)
+ return StorageLayoutType.Bis | StorageLayoutType.SdCard | StorageLayoutType.Usb;
+
+ if (type == SaveDataType.System && (spaceId == SaveDataSpaceId.SdSystem || spaceId == SaveDataSpaceId.SdUser))
+ return StorageLayoutType.SdCard | StorageLayoutType.Usb;
+
+ return StorageLayoutType.Bis;
+ }
+
+ private static SaveDataFormatType GetSaveDataFormatType(in SaveDataAttribute attribute)
+ {
+ return SaveDataProperties.IsJournalingSupported(attribute.Type)
+ ? SaveDataFormatType.Normal
+ : SaveDataFormatType.NoJournal;
+ }
+
+ public Result GetFreeSpaceSizeForSaveData(out long outFreeSpaceSize, SaveDataSpaceId spaceId)
+ {
+ UnsafeHelpers.SkipParamInit(out outFreeSpaceSize);
+
+ using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
+ using var fileSystem = new SharedRef();
+
+ Result rc = _serviceImpl.OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var pathRoot = new Path();
+ rc = PathFunctions.SetUpFixedPath(ref pathRoot.Ref(), new[] { (byte)'/' });
+ if (rc.IsFailure()) return rc.Miss();
+
+ fileSystem.Get.GetFreeSpaceSize(out long freeSpaceSize, in pathRoot);
+ if (rc.IsFailure()) return rc.Miss();
+
+ outFreeSpaceSize = freeSpaceSize;
+ return Result.Success;
}
public Result RegisterSaveDataFileSystemAtomicDeletion(InBuffer saveDataIds)
{
- throw new NotImplementedException();
+ using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
+
+ Result rc = GetProgramInfo(out ProgramInfo programInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ // This function only operates on the system save data space, so the caller
+ // must have permissions to delete system save data
+ AccessControl accessControl = programInfo.AccessControl;
+
+ bool canAccess = accessControl.CanCall(OperationType.DeleteSaveData) &&
+ accessControl.CanCall(OperationType.DeleteSystemSaveData);
+
+ if (!canAccess)
+ return ResultFs.PermissionDenied.Log();
+
+ bool success = false;
+
+ using var accessor = new UniqueRef();
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System);
+ if (rc.IsFailure()) return rc.Miss();
+
+ ReadOnlySpan ids = MemoryMarshal.Cast(saveDataIds.Buffer);
+
+ try
+ {
+ // Try to set the state of all the save IDs as being marked for deletion.
+ for (int i = 0; i < ids.Length; i++)
+ {
+ rc = accessor.Get.GetInterface().SetState(ids[i], SaveDataState.MarkedForDeletion);
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ rc = accessor.Get.GetInterface().Commit();
+ if (rc.IsFailure()) return rc.Miss();
+
+ success = true;
+ return Result.Success;
+ }
+ finally
+ {
+ // Rollback the operation if something goes wrong.
+ if (!success)
+ {
+ rc = accessor.Get.GetInterface().Rollback();
+ if (rc.IsFailure())
+ {
+ Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info,
+ "[fs] Error: Failed to rollback save data indexer.\n".ToU8Span());
+ }
+ }
+ }
}
public Result DeleteSaveDataFileSystem(ulong saveDataId)
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
- return DeleteSaveDataFileSystemCommon(SaveDataSpaceId.System, saveDataId);
+ return DeleteSaveDataFileSystemCommon(SaveDataSpaceId.System, saveDataId).Ret();
}
private Result DeleteSaveDataFileSystemCore(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile)
@@ -460,13 +698,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Delete the save data's meta files
Result rc = _serviceImpl.DeleteAllSaveDataMetas(saveDataId, spaceId);
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
- return rc;
+ return rc.Miss();
// Delete the actual save data.
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
rc = _serviceImpl.DeleteSaveDataFileSystem(spaceId, saveDataId, wipeSaveFile, in saveDataRootPath);
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
- return rc;
+ return rc.Miss();
return Result.Success;
}
@@ -475,7 +713,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
- return DeleteSaveDataFileSystemBySaveDataSpaceIdCore(spaceId, saveDataId);
+ return DeleteSaveDataFileSystemBySaveDataSpaceIdCore(spaceId, saveDataId).Ret();
}
private Result DeleteSaveDataFileSystemBySaveDataSpaceIdCore(SaveDataSpaceId spaceId, ulong saveDataId)
@@ -484,16 +722,16 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
{
using var accessor = new UniqueRef();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (value.SpaceId != ConvertToRealSpaceId(spaceId))
return ResultFs.TargetNotFound.Log();
}
- return DeleteSaveDataFileSystemCommon(spaceId, saveDataId);
+ return DeleteSaveDataFileSystemCommon(spaceId, saveDataId).Ret();
}
public Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId,
@@ -504,76 +742,76 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rs = GetSaveDataInfo(out SaveDataInfo info, spaceId, in attribute);
if (rs.IsFailure()) return rs;
- return DeleteSaveDataFileSystemBySaveDataSpaceIdCore(spaceId, info.SaveDataId);
+ return DeleteSaveDataFileSystemBySaveDataSpaceIdCore(spaceId, info.SaveDataId).Ret();
}
private Result DeleteSaveDataFileSystemCommon(SaveDataSpaceId spaceId, ulong saveDataId)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var accessor = new UniqueRef();
- SaveDataSpaceId actualSpaceId;
+ SaveDataSpaceId targetSpaceId;
- // Only the FS process may delete the save indexer's save data.
- if (saveDataId == SaveIndexerId)
- {
- if (!_serviceImpl.FsServer.IsCurrentProcess(_processId))
- return ResultFs.PermissionDenied.Log();
-
- actualSpaceId = spaceId;
- }
- else
+ if (saveDataId != SaveIndexerId)
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
// Get the actual space ID of this save.
if (spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode)
{
- actualSpaceId = spaceId;
+ targetSpaceId = spaceId;
}
else
{
rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- actualSpaceId = value.SpaceId;
+ targetSpaceId = value.SpaceId;
}
// Check if the caller has permission to delete this save.
rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
Result GetExtraData(out SaveDataExtraData data) =>
- _serviceImpl.ReadSaveDataFileSystemExtraData(out data, actualSpaceId, saveDataId, key.Type,
+ _serviceImpl.ReadSaveDataFileSystemExtraData(out data, targetSpaceId, saveDataId, key.Type,
_saveDataRootPath.DangerousGetPath());
rc = SaveDataAccessibilityChecker.CheckDelete(in key, programInfo, GetExtraData);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
// Pre-delete checks successful. Put the save in the Processing state until deletion is finished.
rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Processing);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().Commit();
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
+ }
+ else
+ {
+ // Only the FS process may delete the save indexer's save data.
+ if (!_serviceImpl.FsServer.IsCurrentProcess(_processId))
+ return ResultFs.PermissionDenied.Log();
+
+ targetSpaceId = spaceId;
}
// Do the actual deletion.
- rc = DeleteSaveDataFileSystemCore(actualSpaceId, saveDataId, false);
- if (rc.IsFailure()) return rc;
+ rc = DeleteSaveDataFileSystemCore(targetSpaceId, saveDataId, wipeSaveFile: false);
+ if (rc.IsFailure()) return rc.Miss();
// Remove the save data from the indexer.
// The indexer doesn't track itself, so skip if deleting its save data.
if (saveDataId != SaveIndexerId)
{
rc = accessor.Get.GetInterface().Delete(saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().Commit();
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
return Result.Success;
@@ -607,7 +845,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result SetSaveDataRootPath(in FspPath path)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (!programInfo.AccessControl.CanCall(OperationType.DebugSaveData))
return ResultFs.PermissionDenied.Log();
@@ -616,13 +854,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (path.Str[0] == NullTerminator)
{
- rc = saveDataRootPath.Initialize(new[] { (byte)'/' });
- if (rc.IsFailure()) return rc;
+ rc = saveDataRootPath.Initialize(new[] { (byte)'.' });
+ if (rc.IsFailure()) return rc.Miss();
}
else
{
rc = saveDataRootPath.InitializeWithReplaceUnc(path.Str);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
var pathFlags = new PathFlags();
@@ -631,7 +869,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
pathFlags.AllowEmptyPath();
rc = saveDataRootPath.Normalize(pathFlags);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
_saveDataRootPath.Initialize(in saveDataRootPath);
@@ -641,14 +879,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result UnsetSaveDataRootPath()
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (!programInfo.AccessControl.CanCall(OperationType.DebugSaveData))
return ResultFs.PermissionDenied.Log();
using var saveDataRootPath = new Path();
rc = saveDataRootPath.InitializeAsEmpty();
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
_saveDataRootPath.Initialize(in saveDataRootPath);
@@ -664,10 +902,20 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return ResultFs.NotImplemented.Log();
}
- public Result OpenSaveDataFile(ref SharedRef file, SaveDataSpaceId spaceId,
- in SaveDataAttribute attribute, SaveDataMetaType metaType)
+ public Result OpenSaveDataFile(ref SharedRef outFile, SaveDataSpaceId spaceId, ulong saveDataId,
+ OpenMode openMode)
{
- throw new NotImplementedException();
+ var storageFlag = StorageLayoutType.NonGameCard;
+ using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
+
+ using var file = new SharedRef();
+ Result rc = _serviceImpl.OpenSaveDataFile(ref file.Ref(), spaceId, saveDataId, openMode);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var typeSetFile = new SharedRef(new StorageLayoutTypeSetFile(ref file.Ref(), storageFlag));
+
+ outFile.SetByMove(ref typeSetFile.Ref());
+ return Result.Success;
}
public Result CheckSaveDataFile(long saveDataId, SaveDataSpaceId spaceId)
@@ -675,23 +923,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
throw new NotImplementedException();
}
- private Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute,
- in SaveDataCreationInfo creationInfo,
- in SaveDataMetaInfo metaInfo, in Optional hashSalt)
- {
- return CreateSaveDataFileSystemCore(in attribute, in creationInfo, in metaInfo, in hashSalt, false);
- }
-
- private Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute,
- in SaveDataCreationInfo creationInfo,
- in SaveDataMetaInfo metaInfo, in Optional hashSalt, bool leaveUnfinalized)
+ private Result CreateSaveDataFileSystemCore(in SaveDataCreationInfo2 creationInfo, bool leaveUnfinalized)
{
ulong saveDataId = 0;
bool creating = false;
bool accessorInitialized = false;
Result rc;
- StorageLayoutType storageFlag = DecidePossibleStorageFlag(attribute.Type, creationInfo.SpaceId);
+ StorageLayoutType storageFlag = DecidePossibleStorageFlag(creationInfo.Attribute.Type, creationInfo.SpaceId);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
using var accessor = new UniqueRef();
@@ -699,7 +938,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
try
{
// Add the new save data to the save indexer
- if (attribute.StaticSaveDataId == SaveIndexerId)
+ if (creationInfo.Attribute.StaticSaveDataId == SaveIndexerId)
{
// The save indexer doesn't index itself
saveDataId = SaveIndexerId;
@@ -715,17 +954,17 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
else
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), creationInfo.SpaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
accessorInitialized = true;
- SaveDataAttribute indexerKey = attribute;
+ SaveDataAttribute indexerKey = creationInfo.Attribute;
// Add the new value to the indexer
- if (attribute.StaticSaveDataId != 0 && attribute.UserId == InvalidUserId)
+ if (creationInfo.Attribute.StaticSaveDataId != 0 && creationInfo.Attribute.UserId == InvalidUserId)
{
// If a static save data ID is specified that ID is always used
- saveDataId = attribute.StaticSaveDataId;
+ saveDataId = creationInfo.Attribute.StaticSaveDataId;
rc = accessor.Get.GetInterface().PutStaticSaveDataIdIndex(in indexerKey);
}
@@ -734,7 +973,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// The save indexer has an upper limit on the number of entries it can hold.
// A few of those entries are reserved for system saves so the system doesn't
// end up in a situation where it can't create a required system save.
- if (!SaveDataProperties.CanUseIndexerReservedArea(attribute.Type))
+ if (!SaveDataProperties.CanUseIndexerReservedArea(creationInfo.Attribute.Type))
{
if (accessor.Get.GetInterface().IsRemainedReservedOnly())
{
@@ -746,6 +985,49 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
rc = accessor.Get.GetInterface().Publish(out saveDataId, in indexerKey);
}
+ if (rc.IsSuccess())
+ {
+ creating = true;
+
+ // Set the state, space ID and size on the new save indexer entry.
+ rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Processing);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().SetSpaceId(saveDataId, ConvertToRealSpaceId(creationInfo.SpaceId));
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = QuerySaveDataTotalSize(out long saveDataSize, creationInfo.Size, creationInfo.JournalSize);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().SetSize(saveDataId, saveDataSize);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().Commit();
+ if (rc.IsFailure()) return rc.Miss();
+ }
+ else
+ {
+ if (ResultFs.AlreadyExists.Includes(rc))
+ {
+ // The save already exists. Ensure the thumbnail meta file exists if needed.
+ if (creationInfo.MetaType == SaveDataMetaType.Thumbnail)
+ {
+ Result rcMeta = accessor.Get.GetInterface().Get(out SaveDataIndexerValue value, in indexerKey);
+ if (rcMeta.IsFailure()) return rcMeta.Miss();
+
+ rcMeta = CreateEmptyThumbnailFile(creationInfo.SpaceId, value.SaveDataId);
+
+ // Allow the thumbnail file to already exist
+ if (rcMeta.IsFailure() && !ResultFs.PathAlreadyExists.Includes(rcMeta))
+ return rcMeta.Miss();
+ }
+
+ return ResultFs.PathAlreadyExists.LogConverted(rc);
+ }
+
+ return rc.Miss();
+ }
+
if (rc.IsFailure())
{
if (ResultFs.AlreadyExists.Includes(rc))
@@ -755,66 +1037,47 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return rc;
}
-
- creating = true;
-
- // Set the state, space ID and size on the new save indexer entry.
- rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Processing);
- if (rc.IsFailure()) return rc;
-
- rc = accessor.Get.GetInterface().SetSpaceId(saveDataId, ConvertToRealSpaceId(creationInfo.SpaceId));
- if (rc.IsFailure()) return rc;
-
- rc = QuerySaveDataTotalSize(out long saveDataSize, creationInfo.Size, creationInfo.JournalSize);
- if (rc.IsFailure()) return rc;
-
- rc = accessor.Get.GetInterface().SetSize(saveDataId, saveDataSize);
- if (rc.IsFailure()) return rc;
-
- rc = accessor.Get.GetInterface().Commit();
- if (rc.IsFailure()) return rc;
}
- // After the new save was added to the save indexer, create the save data file or directory.
+ // After the new save is added to the save indexer, create the save data file or directory.
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
- rc = _serviceImpl.CreateSaveDataFileSystem(saveDataId, in attribute, in creationInfo,
- in saveDataRootPath, in hashSalt, false);
+ rc = _serviceImpl.CreateSaveDataFileSystem(saveDataId, in creationInfo, in saveDataRootPath, skipFormat: false);
if (rc.IsFailure())
{
- if (!ResultFs.PathAlreadyExists.Includes(rc)) return rc;
+ if (!ResultFs.PathAlreadyExists.Includes(rc))
+ return rc.Miss();
- // Handle the situation where a save exists on disk but not in the save indexer.
+ // The save exists on the file system but not in the save indexer.
// Delete the save data and try creating it again.
rc = DeleteSaveDataFileSystemCore(creationInfo.SpaceId, saveDataId, false);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- rc = _serviceImpl.CreateSaveDataFileSystem(saveDataId, in attribute, in creationInfo,
- in saveDataRootPath, in hashSalt, false);
- if (rc.IsFailure()) return rc;
+ rc = _serviceImpl.CreateSaveDataFileSystem(saveDataId, in creationInfo, in saveDataRootPath,
+ skipFormat: false);
+ if (rc.IsFailure()) return rc.Miss();
}
- if (metaInfo.Type != SaveDataMetaType.None)
+ if (creationInfo.MetaType != SaveDataMetaType.None)
{
// Create the requested save data meta file.
- rc = _serviceImpl.CreateSaveDataMeta(saveDataId, creationInfo.SpaceId, metaInfo.Type,
- metaInfo.Size);
- if (rc.IsFailure()) return rc;
+ rc = _serviceImpl.CreateSaveDataMeta(saveDataId, creationInfo.SpaceId, creationInfo.MetaType,
+ creationInfo.MetaSize);
+ if (rc.IsFailure()) return rc.Miss();
- if (metaInfo.Type == SaveDataMetaType.Thumbnail)
+ if (creationInfo.MetaType == SaveDataMetaType.Thumbnail)
{
using var metaFile = new UniqueRef();
rc = _serviceImpl.OpenSaveDataMeta(ref metaFile.Ref(), saveDataId, creationInfo.SpaceId,
- metaInfo.Type);
-
- if (rc.IsFailure()) return rc;
+ creationInfo.MetaType);
+ if (rc.IsFailure()) return rc.Miss();
// The first 0x20 bytes of thumbnail meta files is an SHA-256 hash.
// Zero the hash to indicate that it's currently unused.
ReadOnlySpan metaFileHash = stackalloc byte[0x20];
rc = metaFile.Get.Write(0, metaFileHash, WriteOption.Flush);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
}
@@ -825,14 +1088,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
// The indexer's save data isn't tracked, so we don't need to update its state.
- if (attribute.StaticSaveDataId != SaveIndexerId)
+ if (creationInfo.Attribute.StaticSaveDataId != SaveIndexerId)
{
// Mark the save data as being successfully created
rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Normal);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().Commit();
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
creating = false;
@@ -843,7 +1106,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Revert changes if an error happened in the middle of creation
if (creating)
{
- DeleteSaveDataFileSystemCore(creationInfo.SpaceId, saveDataId, false).IgnoreResult();
+ rc = DeleteSaveDataFileSystemCore(creationInfo.SpaceId, saveDataId, false);
+ if (rc.IsFailure())
+ {
+ Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info, GetRollbackErrorMessage(rc));
+ }
if (accessorInitialized && saveDataId != SaveIndexerId)
{
@@ -851,14 +1118,53 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (rc.IsSuccess() && value.SpaceId == creationInfo.SpaceId)
{
- accessor.Get.GetInterface().Delete(saveDataId).IgnoreResult();
- accessor.Get.GetInterface().Commit().IgnoreResult();
+ accessor.Get.GetInterface().Delete(saveDataId);
+ if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc))
+ {
+ Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info, GetRollbackErrorMessage(rc));
+ }
+
+ accessor.Get.GetInterface().Commit();
+ if (rc.IsFailure())
+ {
+ Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info, GetRollbackErrorMessage(rc));
+ }
}
}
}
+
+ static byte[] GetRollbackErrorMessage(Result result)
+ {
+ string message = $"[fs] Error : Failed to rollback save data creation. ({result.Value:x})\n";
+ return System.Text.Encoding.UTF8.GetBytes(message);
+ }
}
}
+ private Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo,
+ in SaveDataMetaInfo metaInfo, in Optional hashSalt, bool leaveUnfinalized)
+ {
+ // Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
+ Result rc = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 newCreationInfo, in attribute,
+ creationInfo.Size, creationInfo.JournalSize, creationInfo.BlockSize, creationInfo.OwnerId,
+ creationInfo.Flags, creationInfo.SpaceId, GetSaveDataFormatType(in attribute));
+ if (rc.IsFailure()) return rc.Miss();
+
+ newCreationInfo.IsHashSaltEnabled = hashSalt.HasValue;
+ if (hashSalt.HasValue)
+ {
+ newCreationInfo.HashSalt = hashSalt.ValueRo;
+ }
+
+ newCreationInfo.MetaType = metaInfo.Type;
+ newCreationInfo.MetaSize = metaInfo.Size;
+
+ rc = CreateSaveDataFileSystemCore(in newCreationInfo, leaveUnfinalized);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
+ }
+
public Result GetSaveDataInfo(out SaveDataInfo info, SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
{
UnsafeHelpers.SkipParamInit(out info);
@@ -867,77 +1173,112 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var accessor = new UniqueRef();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().Get(out SaveDataIndexerValue value, in attribute);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
SaveDataIndexer.GenerateSaveDataInfo(out info, in attribute, in value);
return Result.Success;
}
- public Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize)
+ public Result QuerySaveDataTotalSize(out long outTotalSize, long dataSize, long journalSize)
{
- UnsafeHelpers.SkipParamInit(out totalSize);
+ UnsafeHelpers.SkipParamInit(out outTotalSize);
if (dataSize < 0 || journalSize < 0)
return ResultFs.InvalidSize.Log();
- return _serviceImpl.QuerySaveDataTotalSize(out totalSize, SaveDataBlockSize, dataSize, journalSize);
+ Result rc = _serviceImpl.QuerySaveDataTotalSize(out long totalSize, SaveDataBlockSize, dataSize, journalSize);
+ if (rc.IsFailure()) return rc.Miss();
+
+ outTotalSize = totalSize;
+ return Result.Success;
}
public Result CreateSaveDataFileSystem(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo,
in SaveDataMetaInfo metaInfo)
{
- var hashSalt = new Optional();
+ // Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
+ Result rc = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 newCreationInfo, in attribute,
+ creationInfo.Size, creationInfo.JournalSize, creationInfo.BlockSize, creationInfo.OwnerId,
+ creationInfo.Flags, creationInfo.SpaceId, GetSaveDataFormatType(in attribute));
+ if (rc.IsFailure()) return rc.Miss();
- return CreateSaveDataFileSystemWithHashSaltImpl(in attribute, in creationInfo, in metaInfo, in hashSalt);
+ newCreationInfo.IsHashSaltEnabled = false;
+ newCreationInfo.MetaType = metaInfo.Type;
+ newCreationInfo.MetaSize = metaInfo.Size;
+
+ rc = CreateSaveDataFileSystemWithCreationInfo2(in newCreationInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
}
public Result CreateSaveDataFileSystemWithHashSalt(in SaveDataAttribute attribute,
in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt)
{
- var optionalHashSalt = new Optional(in hashSalt);
+ // Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
+ Result rc = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 newCreationInfo, in attribute,
+ creationInfo.Size, creationInfo.JournalSize, creationInfo.BlockSize, creationInfo.OwnerId,
+ creationInfo.Flags, creationInfo.SpaceId, GetSaveDataFormatType(in attribute));
+ if (rc.IsFailure()) return rc.Miss();
- return CreateSaveDataFileSystemWithHashSaltImpl(in attribute, in creationInfo, in metaInfo,
- in optionalHashSalt);
+ newCreationInfo.IsHashSaltEnabled = true;
+ newCreationInfo.HashSalt = hashSalt;
+ newCreationInfo.MetaType = metaInfo.Type;
+ newCreationInfo.MetaSize = metaInfo.Size;
+
+ rc = CreateSaveDataFileSystemWithCreationInfo2(in newCreationInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
}
- private Result CreateSaveDataFileSystemWithHashSaltImpl(in SaveDataAttribute attribute,
- in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional hashSalt)
+ public Result CreateSaveDataFileSystemWithCreationInfo2(in SaveDataCreationInfo2 creationInfo)
{
- StorageLayoutType storageFlag = DecidePossibleStorageFlag(attribute.Type, creationInfo.SpaceId);
+ StorageLayoutType storageFlag = DecidePossibleStorageFlag(creationInfo.Attribute.Type, creationInfo.SpaceId);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- SaveDataAttribute tempAttribute = attribute;
- SaveDataCreationInfo tempCreationInfo = creationInfo;
-
- if (hashSalt.HasValue && !programInfo.AccessControl.CanCall(OperationType.CreateSaveDataWithHashSalt))
+ if (creationInfo.IsHashSaltEnabled &&
+ !programInfo.AccessControl.CanCall(OperationType.CreateSaveDataWithHashSalt))
{
return ResultFs.PermissionDenied.Log();
}
- ProgramId programId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
- rc = SaveDataAccessibilityChecker.CheckCreate(in attribute, in creationInfo, programInfo, programId);
- if (rc.IsFailure()) return rc;
+ ProgramId resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
+ rc = SaveDataAccessibilityChecker.CheckCreate(in creationInfo.Attribute, creationInfo.OwnerId, programInfo,
+ resolvedProgramId);
+ if (rc.IsFailure()) return rc.Miss();
- if (tempAttribute.Type == SaveDataType.Account && tempAttribute.UserId == InvalidUserId)
+ if (creationInfo.Attribute.Type == SaveDataType.Account && creationInfo.Attribute.UserId == InvalidUserId)
{
- if (tempAttribute.ProgramId == ProgramId.InvalidId)
+ // Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
+ SaveDataCreationInfo2 newCreationInfo = creationInfo;
+
+ if (newCreationInfo.Attribute.ProgramId == ProgramId.InvalidId)
{
- tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
+ newCreationInfo.Attribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
}
- if (tempCreationInfo.OwnerId == 0)
+ if (newCreationInfo.OwnerId == 0)
{
- tempCreationInfo.OwnerId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId).Value;
+ newCreationInfo.OwnerId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId).Value;
}
+
+ rc = CreateSaveDataFileSystemCore(in newCreationInfo, leaveUnfinalized: false);
+ if (rc.IsFailure()) return rc.Miss();
+ }
+ else
+ {
+ rc = CreateSaveDataFileSystemCore(in creationInfo, leaveUnfinalized: false);
+ if (rc.IsFailure()) return rc.Miss();
}
- return CreateSaveDataFileSystemCore(in tempAttribute, in tempCreationInfo, in metaInfo, in hashSalt);
+ return Result.Success;
}
public Result CreateSaveDataFileSystemBySystemSaveDataId(in SaveDataAttribute attribute,
@@ -947,71 +1288,189 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (!IsStaticSaveDataIdValueRange(attribute.StaticSaveDataId))
return ResultFs.InvalidArgument.Log();
- SaveDataCreationInfo tempCreationInfo = creationInfo;
+ ulong ownerId = creationInfo.OwnerId == 0 ? programInfo.ProgramIdValue : creationInfo.OwnerId;
- if (tempCreationInfo.OwnerId == 0)
- {
- tempCreationInfo.OwnerId = programInfo.ProgramIdValue;
- }
+ rc = SaveDataAccessibilityChecker.CheckCreate(in attribute, ownerId, programInfo, programInfo.ProgramId);
+ if (rc.IsFailure()) return rc.Miss();
- rc = SaveDataAccessibilityChecker.CheckCreate(in attribute, in tempCreationInfo, programInfo,
- programInfo.ProgramId);
- if (rc.IsFailure()) return rc;
+ // Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
+ rc = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 newCreationInfo, in attribute, creationInfo.Size,
+ creationInfo.JournalSize, creationInfo.BlockSize, ownerId, creationInfo.Flags, creationInfo.SpaceId,
+ GetSaveDataFormatType(in attribute));
+ if (rc.IsFailure()) return rc.Miss();
+
+ newCreationInfo.IsHashSaltEnabled = false;
// Static system saves don't usually have meta files
- SaveDataMetaInfo metaInfo = default;
- Optional hashSalt = default;
+ newCreationInfo.MetaType = SaveDataMetaType.None;
+ newCreationInfo.MetaSize = 0;
- return CreateSaveDataFileSystemCore(in attribute, in tempCreationInfo, in metaInfo, in hashSalt);
+ rc = CreateSaveDataFileSystemCore(in newCreationInfo, leaveUnfinalized: false);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
}
- public Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize,
- long journalSize)
+ public Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize)
{
- throw new NotImplementedException();
+ SaveDataAttribute key;
+
+ Result rc = GetProgramInfo(out ProgramInfo programInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using (var accessor = new UniqueRef())
+ {
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().GetKey(out key, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ Result ReadExtraData(out SaveDataExtraData data)
+ {
+ using Path savePath = _saveDataRootPath.DangerousGetPath();
+ return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, key.Type, in savePath);
+ }
+
+ // Check if we have permissions to extend this save data.
+ rc = SaveDataAccessibilityChecker.CheckExtend(spaceId, in key, programInfo, ReadExtraData);
+ if (rc.IsFailure()) return rc.Miss();
+
+ // Check that the save data is in a state that we can start extending it.
+ rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ switch (value.State)
+ {
+ case SaveDataState.Normal:
+ break;
+ case SaveDataState.Processing:
+ case SaveDataState.MarkedForDeletion:
+ case SaveDataState.Extending:
+ return ResultFs.TargetLocked.Log();
+ case SaveDataState.State2:
+ return ResultFs.SaveDataCorrupted.Log();
+ default:
+ return ResultFs.InvalidSaveDataState.Log();
+ }
+
+ // Mark the save data as being extended.
+ rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Extending);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().Commit();
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ // Get the indexer key for the save data.
+ using (var accessor = new UniqueRef())
+ {
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().GetKey(out key, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ // Start the extension.
+ using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
+ Result extendResult = _serviceImpl.StartExtendSaveDataFileSystem(out long extendedTotalSize, saveDataId,
+ spaceId, key.Type, dataSize, journalSize, in saveDataRootPath);
+
+ if (extendResult.IsFailure())
+ {
+ // Try to return the save data to its original state if something went wrong.
+ using var accessor = new UniqueRef();
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
+ if (rc.IsSuccess())
+ {
+ _serviceImpl.RevertExtendSaveDataFileSystem(saveDataId, spaceId, value.Size, in saveDataRootPath);
+ }
+
+ rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Normal);
+ if (rc.IsSuccess())
+ {
+ accessor.Get.GetInterface().Commit().IgnoreResult();
+ }
+
+ return extendResult.Log();
+ }
+
+ // Update the save data's size in the indexer.
+ using (var accessor = new UniqueRef())
+ {
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ accessor.Get.GetInterface().SetSize(saveDataId, extendedTotalSize).IgnoreResult();
+
+ rc = accessor.Get.GetInterface().Commit();
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ // Finish the extension.
+ rc = _serviceImpl.FinishExtendSaveDataFileSystem(saveDataId, spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ // Set the save data's state back to normal.
+ using (var accessor = new UniqueRef())
+ {
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Normal);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().Commit();
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ return Result.Success;
}
- public Result OpenSaveDataFileSystem(ref SharedRef fileSystem,
- SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
+ public Result OpenSaveDataFileSystem(ref SharedRef fileSystem, SaveDataSpaceId spaceId,
+ in SaveDataAttribute attribute)
{
- return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, false);
+ return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, openReadOnly: false).Ret();
}
- public Result OpenReadOnlySaveDataFileSystem(ref SharedRef fileSystem,
- SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
+ public Result OpenReadOnlySaveDataFileSystem(ref SharedRef fileSystem, SaveDataSpaceId spaceId,
+ in SaveDataAttribute attribute)
{
- return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, true);
+ return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, openReadOnly: true).Ret();
}
- private Result OpenSaveDataFileSystemCore(ref SharedRef outFileSystem,
- out ulong saveDataId, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly,
- bool cacheExtraData)
+ public Result OpenSaveDataFileSystemCore(ref SharedRef outFileSystem, out ulong outSaveDataId,
+ SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData)
{
- UnsafeHelpers.SkipParamInit(out saveDataId);
+ UnsafeHelpers.SkipParamInit(out outSaveDataId);
using var accessor = new UniqueRef();
- ulong tempSaveDataId;
+ ulong saveDataId;
bool isStaticSaveDataId =
attribute.StaticSaveDataId != InvalidSystemSaveDataId && attribute.UserId == InvalidUserId;
// Get the ID of the save data
if (isStaticSaveDataId)
{
- tempSaveDataId = attribute.StaticSaveDataId;
+ saveDataId = attribute.StaticSaveDataId;
}
else
{
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().Get(out SaveDataIndexerValue indexerValue, in attribute);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (indexerValue.SpaceId != ConvertToRealSpaceId(spaceId))
return ResultFs.TargetNotFound.Log();
@@ -1019,17 +1478,17 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (indexerValue.State == SaveDataState.Extending)
return ResultFs.SaveDataExtending.Log();
- tempSaveDataId = indexerValue.SaveDataId;
+ saveDataId = indexerValue.SaveDataId;
}
// Open the save data using its ID
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
- Result saveFsResult = _serviceImpl.OpenSaveDataFileSystem(ref outFileSystem, spaceId, tempSaveDataId,
+ Result saveFsResult = _serviceImpl.OpenSaveDataFileSystem(ref outFileSystem, spaceId, saveDataId,
in saveDataRootPath, openReadOnly, attribute.Type, cacheExtraData);
if (saveFsResult.IsSuccess())
{
- saveDataId = tempSaveDataId;
+ outSaveDataId = saveDataId;
return Result.Success;
}
@@ -1040,7 +1499,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (ResultFs.PathNotFound.Includes(saveFsResult))
{
Result rc = RemoveSaveIndexerEntry();
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
return ResultFs.TargetNotFound.LogConverted(saveFsResult);
}
@@ -1048,40 +1507,40 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (ResultFs.TargetNotFound.Includes(saveFsResult))
{
Result rc = RemoveSaveIndexerEntry();
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
return saveFsResult;
Result RemoveSaveIndexerEntry()
{
- if (tempSaveDataId == SaveIndexerId)
+ if (saveDataId == SaveIndexerId)
return Result.Success;
if (isStaticSaveDataId)
{
// The accessor won't be open yet if the save has a static ID
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
// Check the space ID of the save data
rc = accessor.Get.GetInterface().Get(out SaveDataIndexerValue value, in key);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (value.SpaceId != ConvertToRealSpaceId(spaceId))
return ResultFs.TargetNotFound.Log();
}
// Remove the indexer entry. Nintendo ignores these results
- accessor.Get.GetInterface().Delete(tempSaveDataId).IgnoreResult();
+ accessor.Get.GetInterface().Delete(saveDataId).IgnoreResult();
accessor.Get.GetInterface().Commit().IgnoreResult();
return Result.Success;
}
}
- private Result OpenUserSaveDataFileSystemCore(ref SharedRef outFileSystem,
- SaveDataSpaceId spaceId, in SaveDataAttribute attribute, ProgramInfo programInfo, bool openReadOnly)
+ private Result OpenUserSaveDataFileSystemCore(ref SharedRef outFileSystem, SaveDataSpaceId spaceId,
+ in SaveDataAttribute attribute, ProgramInfo programInfo, bool openReadOnly)
{
StorageLayoutType storageFlag = DecidePossibleStorageFlag(attribute.Type, spaceId);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
@@ -1089,7 +1548,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Try grabbing the mount count semaphore
using var mountCountSemaphore = new UniqueRef();
Result rc = TryAcquireSaveDataMountCountSemaphore(ref mountCountSemaphore.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
bool useAsyncFileSystem = !_serviceImpl.IsAllowedDirectorySaveData(spaceId, in saveDataRootPath);
@@ -1098,8 +1557,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Open the file system
rc = OpenSaveDataFileSystemCore(ref fileSystem.Ref(), out ulong saveDataId, spaceId, in attribute,
- openReadOnly, true);
- if (rc.IsFailure()) return rc;
+ openReadOnly, cacheExtraData: true);
+ if (rc.IsFailure()) return rc.Miss();
// Can't use attribute in a closure, so copy the needed field
SaveDataType type = attribute.Type;
@@ -1107,13 +1566,12 @@ 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);
}
// Check if we have permissions to open this save data
rc = SaveDataAccessibilityChecker.CheckOpen(in attribute, programInfo, ReadExtraData);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
// Add all the wrappers for the file system
using var typeSetFileSystem =
@@ -1134,12 +1592,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var openEntryCountAdapter =
new SharedRef(new SaveDataOpenCountAdapter(ref saveService.Ref()));
- using var openCountFileSystem = new SharedRef(
- new OpenCountFileSystem(ref asyncFileSystem.Ref(), ref openEntryCountAdapter.Ref(),
- ref mountCountSemaphore.Ref()));
+ using var openCountFileSystem = new SharedRef(new OpenCountFileSystem(ref asyncFileSystem.Ref(),
+ ref openEntryCountAdapter.Ref(), ref mountCountSemaphore.Ref()));
var pathFlags = new PathFlags();
pathFlags.AllowBackslash();
+ pathFlags.AllowAllCharacters();
using SharedRef fileSystemAdapter =
FileSystemInterfaceAdapter.CreateShared(ref openCountFileSystem.Ref(), pathFlags, false);
@@ -1149,45 +1607,45 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
- private Result OpenUserSaveDataFileSystem(ref SharedRef outFileSystem,
- SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly)
+ private Result OpenUserSaveDataFileSystem(ref SharedRef outFileSystem, SaveDataSpaceId spaceId,
+ in SaveDataAttribute attribute, bool openReadOnly)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = SaveDataAccessibilityChecker.CheckOpenPre(in attribute, programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
SaveDataAttribute tempAttribute;
if (attribute.ProgramId.Value == 0)
{
- ProgramId programId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
+ ProgramId resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
- rc = SaveDataAttribute.Make(out tempAttribute, programId, attribute.Type, attribute.UserId,
+ rc = SaveDataAttribute.Make(out tempAttribute, resolvedProgramId, attribute.Type, attribute.UserId,
attribute.StaticSaveDataId, attribute.Index);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
else
{
tempAttribute = attribute;
}
- SaveDataSpaceId actualSpaceId;
+ SaveDataSpaceId targetSpaceId;
if (tempAttribute.Type == SaveDataType.Cache)
{
// Check whether the save is on the SD card or the BIS
- rc = GetCacheStorageSpaceId(out actualSpaceId, tempAttribute.ProgramId.Value);
- if (rc.IsFailure()) return rc;
+ rc = GetCacheStorageSpaceId(out targetSpaceId, tempAttribute.ProgramId.Value);
+ if (rc.IsFailure()) return rc.Miss();
}
else
{
- actualSpaceId = spaceId;
+ targetSpaceId = spaceId;
}
- return OpenUserSaveDataFileSystemCore(ref outFileSystem, actualSpaceId, in tempAttribute, programInfo,
- openReadOnly);
+ return OpenUserSaveDataFileSystemCore(ref outFileSystem, targetSpaceId, in tempAttribute, programInfo,
+ openReadOnly).Ret();
}
public Result OpenSaveDataFileSystemBySystemSaveDataId(ref SharedRef outFileSystem,
@@ -1197,7 +1655,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return ResultFs.InvalidArgument.Log();
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
StorageLayoutType storageFlag = DecidePossibleStorageFlag(attribute.Type, spaceId);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
@@ -1215,8 +1673,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Open the file system
rc = OpenSaveDataFileSystemCore(ref fileSystem.Ref(), out ulong saveDataId, spaceId, in attribute,
- false, true);
- if (rc.IsFailure()) return rc;
+ openReadOnly: false, cacheExtraData: true);
+ if (rc.IsFailure()) return rc.Miss();
// Can't use attribute in a closure, so copy the needed field
SaveDataType type = attribute.Type;
@@ -1224,13 +1682,12 @@ 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);
}
// Check if we have permissions to open this save data
rc = SaveDataAccessibilityChecker.CheckOpen(in attribute, programInfo, ReadExtraData);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
// Add all the wrappers for the file system
using var typeSetFileSystem =
@@ -1256,9 +1713,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
var pathFlags = new PathFlags();
pathFlags.AllowBackslash();
+ pathFlags.AllowAllCharacters();
using SharedRef fileSystemAdapter =
- FileSystemInterfaceAdapter.CreateShared(ref openCountFileSystem.Ref(), pathFlags, false);
+ FileSystemInterfaceAdapter.CreateShared(ref openCountFileSystem.Ref(), pathFlags,
+ allowAllOperations: false);
outFileSystem.SetByMove(ref fileSystemAdapter.Ref());
@@ -1276,79 +1735,82 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var accessor = new UniqueRef();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
return _serviceImpl.ReadSaveDataFileSystemExtraData(out extraData, spaceId, saveDataId, key.Type,
- in saveDataRootPath);
+ in saveDataRootPath).Ret();
}
- private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData, SaveDataSpaceId spaceId,
- ulong saveDataId, in SaveDataExtraData extraDataMask)
+ private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData,
+ Optional spaceId, ulong saveDataId, in SaveDataExtraData extraDataMask)
{
UnsafeHelpers.SkipParamInit(out extraData);
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
SaveDataSpaceId resolvedSpaceId;
SaveDataAttribute key;
- if (spaceId == SaveDataSpaceId.BisAuto)
+ if (!spaceId.HasValue)
{
using var accessor = new UniqueRef();
if (IsStaticSaveDataIdValueRange(saveDataId))
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
else
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.User);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
resolvedSpaceId = value.SpaceId;
rc = accessor.Get.GetInterface().GetKey(out key, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
else
{
using var accessor = new UniqueRef();
- rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId.ValueRo);
+ if (rc.IsFailure()) return rc.Miss();
- rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
- if (rc.IsFailure()) return rc;
+ rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue _, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
- resolvedSpaceId = value.SpaceId;
+ resolvedSpaceId = spaceId.ValueRo;
rc = accessor.Get.GetInterface().GetKey(out key, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
- Result ReadExtraData(out SaveDataExtraData data) => _serviceImpl.ReadSaveDataFileSystemExtraData(out data,
- resolvedSpaceId, saveDataId, key.Type, _saveDataRootPath.DangerousGetPath());
+ Result ReadExtraData(out SaveDataExtraData data)
+ {
+ using Path savePath = _saveDataRootPath.DangerousGetPath();
+ return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, resolvedSpaceId, saveDataId, key.Type,
+ in savePath);
+ }
- rc = SaveDataAccessibilityChecker.CheckReadExtraData(in key, in extraDataMask, programInfo,
- ReadExtraData);
- if (rc.IsFailure()) return rc;
+ rc = SaveDataAccessibilityChecker.CheckReadExtraData(in key, in extraDataMask, programInfo, ReadExtraData);
+ if (rc.IsFailure()) return rc.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData tempExtraData, resolvedSpaceId,
saveDataId, key.Type, in saveDataRootPath);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
MaskExtraData(ref tempExtraData, in extraDataMask);
extraData = tempExtraData;
@@ -1366,11 +1828,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
return ReadSaveDataFileSystemExtraDataCore(out SpanHelpers.AsStruct(extraData.Buffer),
- SaveDataSpaceId.BisAuto, saveDataId, in extraDataMask);
+ spaceId: default, saveDataId, in extraDataMask).Ret();
}
- public Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraData,
- SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
+ public Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraData, SaveDataSpaceId spaceId,
+ in SaveDataAttribute attribute)
{
if (extraData.Size != Unsafe.SizeOf())
return ResultFs.InvalidArgument.Log();
@@ -1378,7 +1840,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct(extraData.Buffer);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
SaveDataAttribute tempAttribute = attribute;
@@ -1388,17 +1850,17 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
rc = GetSaveDataInfo(out SaveDataInfo info, spaceId, in tempAttribute);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
// Make a mask for reading the entire extra data
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
- return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, info.SaveDataId, in extraDataMask);
+ return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, info.SaveDataId, in extraDataMask).Ret();
}
- public Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer extraData,
- SaveDataSpaceId spaceId, ulong saveDataId)
+ public Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer extraData, SaveDataSpaceId spaceId,
+ ulong saveDataId)
{
if (extraData.Size != Unsafe.SizeOf())
return ResultFs.InvalidArgument.Log();
@@ -1409,7 +1871,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
- return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, saveDataId, in extraDataMask);
+ return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, saveDataId, in extraDataMask).Ret();
}
public Result ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(OutBuffer extraData,
@@ -1427,7 +1889,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct(extraData.Buffer);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
SaveDataAttribute tempAttribute = attribute;
@@ -1437,9 +1899,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
rc = GetSaveDataInfo(out SaveDataInfo info, spaceId, in tempAttribute);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, info.SaveDataId, in maskRef);
+ return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, info.SaveDataId, in maskRef).Ret();
}
private Result WriteSaveDataFileSystemExtraDataCore(SaveDataSpaceId spaceId, ulong saveDataId,
@@ -1449,7 +1911,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
return _serviceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in saveDataRootPath,
- saveType, updateTimeStamp);
+ saveType, updateTimeStamp).Ret();
}
private Result WriteSaveDataFileSystemExtraDataWithMaskCore(ulong saveDataId, SaveDataSpaceId spaceId,
@@ -1458,32 +1920,35 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var accessor = new UniqueRef();
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- Result ReadExtraData(out SaveDataExtraData data) => _serviceImpl.ReadSaveDataFileSystemExtraData(out data,
- spaceId, saveDataId, key.Type, _saveDataRootPath.DangerousGetPath());
+ Result ReadExtraData(out SaveDataExtraData data)
+ {
+ using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
+ return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, spaceId, saveDataId, key.Type,
+ in saveDataRootPath);
+ }
- rc = SaveDataAccessibilityChecker.CheckWriteExtraData(in key, in extraDataMask, programInfo,
- ReadExtraData);
- if (rc.IsFailure()) return rc;
+ rc = SaveDataAccessibilityChecker.CheckWriteExtraData(in key, in extraDataMask, programInfo, ReadExtraData);
+ if (rc.IsFailure()) return rc.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
- rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraDataModify, spaceId,
- saveDataId, key.Type, in saveDataRootPath);
- if (rc.IsFailure()) return rc;
+ rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraDataModify, spaceId, saveDataId,
+ key.Type, in saveDataRootPath);
+ if (rc.IsFailure()) return rc.Miss();
ModifySaveDataExtraData(ref extraDataModify, in extraData, in extraDataMask);
return _serviceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraDataModify,
- in saveDataRootPath, key.Type, false);
+ in saveDataRootPath, key.Type, updateTimeStamp: false).Ret();
}
public Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraData)
@@ -1494,17 +1959,17 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
ref readonly SaveDataExtraData extraDataRef =
ref SpanHelpers.AsReadOnlyStruct(extraData.Buffer);
- var extraDataMask = new SaveDataExtraData();
+ SaveDataExtraData extraDataMask = default;
extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF);
- return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in extraDataMask);
+ return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in extraDataMask).Ret();
}
public Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in SaveDataAttribute attribute,
SaveDataSpaceId spaceId, InBuffer extraData, InBuffer extraDataMask)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
SaveDataAttribute tempAttribute = attribute;
@@ -1514,27 +1979,27 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
rc = GetSaveDataInfo(out SaveDataInfo info, spaceId, in tempAttribute);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- return WriteSaveDataFileSystemExtraDataWithMask(info.SaveDataId, spaceId, extraData, extraDataMask);
+ return WriteSaveDataFileSystemExtraDataWithMask(info.SaveDataId, spaceId, extraData, extraDataMask).Ret();
}
public Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId,
InBuffer extraData, InBuffer extraDataMask)
{
- if (extraDataMask.Size != Unsafe.SizeOf())
- return ResultFs.InvalidArgument.Log();
-
if (extraData.Size != Unsafe.SizeOf())
return ResultFs.InvalidArgument.Log();
- ref readonly SaveDataExtraData maskRef =
- ref SpanHelpers.AsReadOnlyStruct(extraDataMask.Buffer);
+ if (extraDataMask.Size != Unsafe.SizeOf())
+ return ResultFs.InvalidArgument.Log();
ref readonly SaveDataExtraData extraDataRef =
ref SpanHelpers.AsReadOnlyStruct(extraData.Buffer);
- return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in maskRef);
+ ref readonly SaveDataExtraData maskRef =
+ ref SpanHelpers.AsReadOnlyStruct(extraDataMask.Buffer);
+
+ return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in maskRef).Ret();
}
public Result OpenSaveDataInfoReader(ref SharedRef outInfoReader)
@@ -1542,23 +2007,23 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- if (!programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReader) ||
- !programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReaderForSystem))
- {
+ if (!programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReader))
+ return ResultFs.PermissionDenied.Log();
+
+ if (!programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReaderForSystem))
return ResultFs.PermissionDenied.Log();
- }
using var reader = new SharedRef();
using (var accessor = new UniqueRef())
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
outInfoReader.SetByMove(ref reader.Ref());
@@ -1566,31 +2031,31 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
- public Result OpenSaveDataInfoReaderBySaveDataSpaceId(
- ref SharedRef outInfoReader, SaveDataSpaceId spaceId)
+ public Result OpenSaveDataInfoReaderBySaveDataSpaceId(ref SharedRef outInfoReader,
+ SaveDataSpaceId spaceId)
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = CheckOpenSaveDataInfoReaderAccessControl(programInfo, spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var filterReader = new UniqueRef();
using (var accessor = new UniqueRef())
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var reader = new SharedRef();
rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
var filter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), programId: default,
- saveDataType: default, userId: default, saveDataId: default, index: default, rank: 0);
+ saveDataType: default, userId: default, saveDataId: default, index: default, (int)SaveDataRank.Primary);
filterReader.Reset(new SaveDataInfoFilterReader(ref reader.Ref(), in filter));
}
@@ -1606,25 +2071,25 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (!programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReaderForInternal))
return ResultFs.PermissionDenied.Log();
rc = CheckOpenSaveDataInfoReaderAccessControl(programInfo, spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var filterReader = new UniqueRef();
using (var accessor = new UniqueRef())
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var reader = new SharedRef();
rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in filter);
@@ -1645,15 +2110,15 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var accessor = new UniqueRef();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var filterReader =
new UniqueRef(new SaveDataInfoFilterReader(ref reader.Ref(), in infoFilter));
- return filterReader.Get.Read(out count, new OutBuffer(SpanHelpers.AsByteSpan(ref info)));
+ return filterReader.Get.Read(out count, new OutBuffer(SpanHelpers.AsByteSpan(ref info))).Ret();
}
public Result FindSaveDataWithFilter(out long count, OutBuffer saveDataInfoBuffer, SaveDataSpaceId spaceId,
@@ -1667,7 +2132,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = CheckOpenSaveDataInfoReaderAccessControl(programInfo, spaceId);
@@ -1678,18 +2143,36 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Don't have full info reader permissions. Check if we have find permissions.
rc = SaveDataAccessibilityChecker.CheckFind(in filter, programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
}
- var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in filter);
+ SaveDataFilter tempFilter = filter;
+ if (filter.Attribute.ProgramId == ProgramId.InvalidId)
+ {
+ tempFilter.Attribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
+ }
- return FindSaveDataWithFilterImpl(out count,
- out SpanHelpers.AsStruct(saveDataInfoBuffer.Buffer), spaceId, in infoFilter);
+ var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in tempFilter);
+
+ return FindSaveDataWithFilterImpl(out count, out SpanHelpers.AsStruct(saveDataInfoBuffer.Buffer),
+ spaceId, in infoFilter).Ret();
}
private Result CreateEmptyThumbnailFile(SaveDataSpaceId spaceId, ulong saveDataId)
{
- throw new NotImplementedException();
+ Result rc = _serviceImpl.CreateSaveDataMeta(saveDataId, spaceId, SaveDataMetaType.Thumbnail,
+ SaveDataMetaPolicy.ThumbnailFileSize);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var file = new UniqueRef();
+ rc = _serviceImpl.OpenSaveDataMeta(ref file.Ref(), saveDataId, spaceId, SaveDataMetaType.Thumbnail);
+ if (rc.IsFailure()) return rc.Miss();
+
+ Hash hash = default;
+ rc = file.Get.Write(0, hash.Value, WriteOption.Flush);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
}
private Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef fileSystem,
@@ -1714,15 +2197,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
UnsafeHelpers.SkipParamInit(out commitId);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (!programInfo.AccessControl.CanCall(OperationType.GetSaveDataCommitId))
return ResultFs.PermissionDenied.Log();
Unsafe.SkipInit(out SaveDataExtraData extraData);
- rc = ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer.FromStruct(ref extraData), spaceId,
- saveDataId);
- if (rc.IsFailure()) return rc;
+ rc = ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer.FromStruct(ref extraData), spaceId, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
commitId = Impl.Utility.ConvertZeroCommitId(in extraData);
return Result.Success;
@@ -1743,7 +2225,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return rc;
}
- return OpenSaveDataInfoReaderOnlyCacheStorage(ref outInfoReader, spaceId);
+ return OpenSaveDataInfoReaderOnlyCacheStorage(ref outInfoReader, spaceId).Ret();
}
private Result OpenSaveDataInfoReaderOnlyCacheStorage(ref SharedRef outInfoReader,
@@ -1752,7 +2234,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (spaceId != SaveDataSpaceId.SdUser && spaceId != SaveDataSpaceId.User)
return ResultFs.InvalidSaveDataSpaceId.Log();
@@ -1763,16 +2245,15 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using (var accessor = new UniqueRef())
{
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
ProgramId resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
- var filter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), resolvedProgramId,
- SaveDataType.Cache, userId: default, saveDataId: default, index: default,
- (int)SaveDataRank.Primary);
+ var filter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), resolvedProgramId, SaveDataType.Cache,
+ userId: default, saveDataId: default, index: default, (int)SaveDataRank.Primary);
filterReader.Reset(new SaveDataInfoFilterReader(ref reader.Ref(), in filter));
}
@@ -1782,8 +2263,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
- private Result OpenSaveDataMetaFileRaw(ref SharedRef file, SaveDataSpaceId spaceId,
- ulong saveDataId, SaveDataMetaType metaType, OpenMode mode)
+ private Result OpenSaveDataMetaFileRaw(ref SharedRef outFile, SaveDataSpaceId spaceId, ulong saveDataId,
+ SaveDataMetaType metaType, OpenMode mode)
{
throw new NotImplementedException();
}
@@ -1799,10 +2280,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
UnsafeHelpers.SkipParamInit(out spaceId);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- ulong programId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId).Value;
- return GetCacheStorageSpaceId(out spaceId, programId);
+ ulong resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId).Value;
+ return GetCacheStorageSpaceId(out spaceId, resolvedProgramId);
}
private Result GetCacheStorageSpaceId(out SaveDataSpaceId spaceId, ulong programId)
@@ -1813,8 +2294,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// Cache storage on the SD card will always take priority over case storage in NAND
if (_serviceImpl.IsSdCardAccessible())
{
- rc = SaveExists(out bool existsOnSdCard, SaveDataSpaceId.SdUser);
- if (rc.IsFailure()) return rc;
+ rc = DoesCacheStorageExist(out bool existsOnSdCard, SaveDataSpaceId.SdUser);
+ if (rc.IsFailure()) return rc.Miss();
if (existsOnSdCard)
{
@@ -1823,8 +2304,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
}
- rc = SaveExists(out bool existsOnNand, SaveDataSpaceId.User);
- if (rc.IsFailure()) return rc;
+ rc = DoesCacheStorageExist(out bool existsOnNand, SaveDataSpaceId.User);
+ if (rc.IsFailure()) return rc.Miss();
if (existsOnNand)
{
@@ -1834,14 +2315,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return ResultFs.TargetNotFound.Log();
- Result SaveExists(out bool exists, SaveDataSpaceId saveSpaceId)
+ Result DoesCacheStorageExist(out bool exists, SaveDataSpaceId saveSpaceId)
{
UnsafeHelpers.SkipParamInit(out exists);
- var infoFilter = new SaveDataInfoFilter(saveSpaceId, new ProgramId(programId), SaveDataType.Cache,
- default, default, default, 0);
+ var filter = new SaveDataInfoFilter(saveSpaceId, new ProgramId(programId), SaveDataType.Cache,
+ userId: default, saveDataId: default, index: default, (int)SaveDataRank.Primary);
- Result result = FindSaveDataWithFilterImpl(out long count, out _, saveSpaceId, in infoFilter);
+ Result result = FindSaveDataWithFilterImpl(out long count, out _, saveSpaceId, in filter);
if (result.IsFailure()) return result;
exists = count != 0;
@@ -1851,13 +2332,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
private Result FindCacheStorage(out SaveDataInfo saveInfo, out SaveDataSpaceId spaceId, ushort index)
{
- UnsafeHelpers.SkipParamInit(out saveInfo, out spaceId);
+ UnsafeHelpers.SkipParamInit(out saveInfo);
Result rc = GetCacheStorageSpaceId(out spaceId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
ProgramId resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
@@ -1865,7 +2346,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
userId: default, saveDataId: default, index, (int)SaveDataRank.Primary);
rc = FindSaveDataWithFilterImpl(out long count, out SaveDataInfo info, spaceId, in filter);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (count == 0)
return ResultFs.TargetNotFound.Log();
@@ -1879,10 +2360,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = FindCacheStorage(out SaveDataInfo saveInfo, out SaveDataSpaceId spaceId, index);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = Hos.Fs.DeleteSaveData(spaceId, saveInfo.SaveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
@@ -1894,12 +2375,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
Result rc = FindCacheStorage(out SaveDataInfo saveInfo, out SaveDataSpaceId spaceId, index);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
- rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId,
- saveInfo.SaveDataId, saveInfo.Type, in saveDataRootPath);
- if (rc.IsFailure()) return rc;
+ rc = _serviceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveInfo.SaveDataId,
+ saveInfo.Type, in saveDataRootPath);
+ if (rc.IsFailure()) return rc.Miss();
usableDataSize = extraData.DataSize;
journalSize = extraData.JournalSize;
@@ -1912,8 +2393,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
throw new NotImplementedException();
}
- public Result OpenSaveDataTransferManagerVersion2(
- ref SharedRef manager)
+ public Result OpenSaveDataTransferManagerVersion2(ref SharedRef manager)
{
throw new NotImplementedException();
}
@@ -1924,34 +2404,76 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
throw new NotImplementedException();
}
- public Result OpenSaveDataTransferManagerForRepair(
- ref SharedRef manager)
+ public Result OpenSaveDataTransferManagerForRepair(ref SharedRef manager)
{
throw new NotImplementedException();
}
- public Result OpenSaveDataTransferProhibiter(
- ref SharedRef prohibiter, Ncm.ApplicationId applicationId)
+ private Result OpenSaveDataTransferProhibiterCore(ref SharedRef prohibiter,
+ Ncm.ApplicationId applicationId)
{
throw new NotImplementedException();
}
- public Result OpenSaveDataMover(ref SharedRef saveMover,
- SaveDataSpaceId sourceSpaceId, SaveDataSpaceId destinationSpaceId, NativeHandle workBufferHandle,
- ulong workBufferSize)
+ public Result OpenSaveDataTransferProhibiter(ref SharedRef prohibiter,
+ Ncm.ApplicationId applicationId)
+ {
+ Result rc = GetProgramInfo(out ProgramInfo programInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ Assert.SdkNotNull(_serviceImpl.GetSaveDataTransferCryptoConfiguration());
+
+ rc = SaveDataAccessibilityChecker.CheckOpenProhibiter(applicationId, programInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return OpenSaveDataTransferProhibiterCore(ref prohibiter, applicationId).Ret();
+ }
+
+ public bool IsProhibited(ref UniqueLock outLock, ApplicationId applicationId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result OpenSaveDataMover(ref SharedRef saveMover, SaveDataSpaceId sourceSpaceId,
+ SaveDataSpaceId destinationSpaceId, NativeHandle workBufferHandle, ulong workBufferSize)
{
throw new NotImplementedException();
}
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{
- return _serviceImpl.SetSdCardEncryptionSeed(in seed);
+ return _serviceImpl.SetSdCardEncryptionSeed(in seed).Ret();
}
public Result ListAccessibleSaveDataOwnerId(out int readCount, OutBuffer idBuffer, ProgramId programId,
int startIndex, int bufferIdCount)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out readCount);
+
+ if (startIndex < 0)
+ return ResultFs.InvalidOffset.Log();
+
+ var ids = Span.Empty;
+
+ if (bufferIdCount > 0)
+ {
+ ids = MemoryMarshal.Cast(idBuffer.Buffer);
+
+ if (ids.Length < bufferIdCount)
+ return ResultFs.InvalidSize.Log();
+ }
+
+ Result rc = GetProgramInfo(out ProgramInfo callerProgramInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ if (!callerProgramInfo.AccessControl.CanCall(OperationType.ListAccessibleSaveDataOwnerId))
+ return ResultFs.PermissionDenied.Log();
+
+ rc = GetProgramInfoByProgramId(out ProgramInfo targetProgramInfo, programId.Value);
+ if (rc.IsFailure()) return rc.Miss();
+
+ targetProgramInfo.AccessControl.ListSaveDataOwnedId(out readCount, ids.Slice(0, bufferIdCount), startIndex);
+ return Result.Success;
}
private ProgramId ResolveDefaultSaveDataReferenceProgramId(ProgramId programId)
@@ -1962,7 +2484,55 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId,
OutBuffer workBuffer)
{
- throw new NotImplementedException();
+ // The caller needs the VerifySaveData permission.
+ Result rc = GetProgramInfo(out ProgramInfo programInfo);
+ if (rc.IsFailure()) return rc.Miss();
+
+ if (!programInfo.AccessControl.CanCall(OperationType.VerifySaveData))
+ return ResultFs.PermissionDenied.Log();
+
+ using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
+
+ try
+ {
+ // Get the information needed to open the file system.
+ SaveDataIndexerValue value;
+ SaveDataType saveDataType;
+
+ using (var accessor = new UniqueRef())
+ {
+ rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().GetValue(out value, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ saveDataType = key.Type;
+ }
+
+ // Open the save data file system.
+ using var fileSystem = new SharedRef();
+
+ using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
+ rc = _serviceImpl.OpenSaveDataFileSystem(ref fileSystem.Ref(), value.SpaceId, saveDataId,
+ in saveDataRootPath,
+ openReadOnly: false, saveDataType, cacheExtraData: true);
+ if (rc.IsFailure()) return rc.Miss();
+
+ // Verify the file system.
+ rc = Utility.VerifyDirectoryRecursively(fileSystem.Get, workBuffer.Buffer);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
+ }
+ finally
+ {
+ // Make sure we don't leak any invalid data.
+ workBuffer.Buffer.Clear();
+ }
}
public Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset)
@@ -1976,9 +2546,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var accessor = new UniqueRef();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- return CleanUpSaveData(accessor.Get);
+ return CleanUpSaveData(accessor.Get).Ret();
}
private Result CleanUpSaveData(SaveDataIndexerAccessor accessor)
@@ -1993,9 +2563,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var accessor = new UniqueRef();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
- return CompleteSaveDataExtension(accessor.Get);
+ return CompleteSaveDataExtension(accessor.Get).Ret();
}
private Result CompleteSaveDataExtension(SaveDataIndexerAccessor accessor)
@@ -2010,14 +2580,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var fileSystem = new SharedRef();
Result rc = _serviceImpl.OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), SaveDataSpaceId.Temporary);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
using var pathRoot = new Path();
rc = PathFunctions.SetUpFixedPath(ref pathRoot.Ref(), new[] { (byte)'/' });
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = fileSystem.Get.CleanDirectoryRecursively(in pathRoot);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
_serviceImpl.ResetTemporaryStorageIndexer();
return Result.Success;
@@ -2031,8 +2601,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result OpenMultiCommitManager(ref SharedRef outCommitManager)
{
- using SharedRef
- commitInterface = GetSharedMultiCommitInterfaceFromThis();
+ using SharedRef commitInterface = GetSharedMultiCommitInterfaceFromThis();
outCommitManager.Reset(new MultiCommitManager(_serviceImpl.FsServer, ref commitInterface.Ref()));
@@ -2041,56 +2610,54 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result OpenMultiCommitContext(ref SharedRef contextFileSystem)
{
- var attribute = new SaveDataAttribute
- {
- Index = 0,
- Type = SaveDataType.System,
- UserId = InvalidUserId,
- StaticSaveDataId = MultiCommitManager.SaveDataId,
- ProgramId = new ProgramId(MultiCommitManager.ProgramId)
- };
+ Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(MultiCommitManager.ProgramId),
+ SaveDataType.System, InvalidUserId, MultiCommitManager.SaveDataId, index: 0);
+ if (rc.IsFailure()) return rc.Miss();
- return OpenSaveDataFileSystemCore(ref contextFileSystem, out _, SaveDataSpaceId.System, in attribute, false,
- true);
+ using var fileSystem = new SharedRef();
+
+ rc = OpenSaveDataFileSystemCore(ref fileSystem.Ref(), out _, SaveDataSpaceId.System, in attribute,
+ openReadOnly: false, cacheExtraData: true);
+ if (rc.IsFailure()) return rc.Miss();
+
+ contextFileSystem.SetByMove(ref fileSystem.Ref());
+ return Result.Success;
}
public Result RecoverMultiCommit()
{
- return MultiCommitManager.Recover(_serviceImpl.FsServer, this, _serviceImpl);
+ return MultiCommitManager.Recover(_serviceImpl.FsServer, this, _serviceImpl).Ret();
}
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
{
- return _serviceImpl.IsProvisionallyCommittedSaveData(out isProvisionallyCommitted, in saveInfo);
+ return _serviceImpl.IsProvisionallyCommittedSaveData(out isProvisionallyCommitted, in saveInfo).Ret();
}
public Result RecoverProvisionallyCommittedSaveData(in SaveDataInfo saveInfo, bool doRollback)
{
- var attribute = new SaveDataAttribute
- {
- Index = saveInfo.Index,
- Type = saveInfo.Type,
- UserId = InvalidUserId,
- StaticSaveDataId = saveInfo.StaticSaveDataId,
- ProgramId = saveInfo.ProgramId
- };
+ Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, saveInfo.ProgramId, saveInfo.Type,
+ saveInfo.UserId, saveInfo.SaveDataId, saveInfo.Index);
+ if (rc.IsFailure()) return rc.Miss();
using var fileSystem = new SharedRef();
- Result rc = OpenSaveDataFileSystemCore(ref fileSystem.Ref(), out _, saveInfo.SpaceId, in attribute, false,
- false);
- if (rc.IsFailure()) return rc;
+ rc = OpenSaveDataFileSystemCore(ref fileSystem.Ref(), out _, saveInfo.SpaceId, in attribute,
+ openReadOnly: false, cacheExtraData: false);
+ if (rc.IsFailure()) return rc.Miss();
- if (doRollback)
+ if (!doRollback)
{
- rc = fileSystem.Get.Rollback();
+ rc = fileSystem.Get.Commit();
+ if (rc.IsFailure()) return rc.Miss();
}
else
{
- rc = fileSystem.Get.Commit();
+ rc = fileSystem.Get.Rollback();
+ if (rc.IsFailure()) return rc.Miss();
}
- return rc;
+ return Result.Success;
}
private Result TryAcquireSaveDataEntryOpenCountSemaphore(ref UniqueRef outSemaphoreLock)
@@ -2099,7 +2666,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = Utility.MakeUniqueLockWithPin(ref outSemaphoreLock, _openEntryCountSemaphore,
ref saveService.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
@@ -2110,7 +2677,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = Utility.MakeUniqueLockWithPin(ref outSemaphoreLock, _saveDataMountCountSemaphore,
ref saveService.Ref());
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
@@ -2123,7 +2690,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result SetSdCardAccessibility(bool isAccessible)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
if (!programInfo.AccessControl.CanCall(OperationType.SetSdCardAccessibility))
return ResultFs.PermissionDenied.Log();
@@ -2142,73 +2709,19 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
SaveDataSpaceId spaceId)
{
using var accessor = new UniqueRef();
- Result rc = _serviceImpl.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool neededInit, spaceId);
- if (rc.IsFailure()) return rc;
+ Result rc = _serviceImpl.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool isInitialOpen, spaceId);
+ if (rc.IsFailure()) return rc.Miss();
- if (neededInit)
+ if (isInitialOpen)
{
- // todo: nn::fssrv::SaveDataFileSystemService::CleanUpSaveDataCore
- // nn::fssrv::SaveDataFileSystemService::CompleteSaveDataExtensionCore
+ CleanUpSaveData(accessor.Get).IgnoreResult();
+ CompleteSaveDataExtension(accessor.Get).IgnoreResult();
}
outAccessor.Set(ref accessor.Ref());
return Result.Success;
}
- private Result GetProgramInfo(out ProgramInfo programInfo)
- {
- return _serviceImpl.GetProgramInfo(out programInfo, _processId);
- }
-
- private SaveDataSpaceId ConvertToRealSpaceId(SaveDataSpaceId spaceId)
- {
- return spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode
- ? SaveDataSpaceId.System
- : spaceId;
- }
-
- private bool IsStaticSaveDataIdValueRange(ulong id)
- {
- return unchecked((long)id) < 0;
- }
-
- private void ModifySaveDataExtraData(ref SaveDataExtraData currentExtraData, in SaveDataExtraData extraData,
- in SaveDataExtraData extraDataMask)
- {
- Span currentExtraDataBytes = SpanHelpers.AsByteSpan(ref currentExtraData);
- ReadOnlySpan extraDataBytes = SpanHelpers.AsReadOnlyByteSpan(in extraData);
- ReadOnlySpan extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
-
- for (int i = 0; i < Unsafe.SizeOf(); i++)
- {
- currentExtraDataBytes[i] = (byte)(extraDataBytes[i] & extraDataMaskBytes[i] |
- currentExtraDataBytes[i] & ~extraDataMaskBytes[i]);
- }
- }
-
- private void MaskExtraData(ref SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
- {
- Span extraDataBytes = SpanHelpers.AsByteSpan(ref extraData);
- ReadOnlySpan extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
-
- for (int i = 0; i < Unsafe.SizeOf(); i++)
- {
- extraDataBytes[i] &= extraDataMaskBytes[i];
- }
- }
-
- private StorageLayoutType DecidePossibleStorageFlag(SaveDataType type, SaveDataSpaceId spaceId)
- {
- if (type == SaveDataType.Cache || type == SaveDataType.Bcat)
- return StorageLayoutType.Bis | StorageLayoutType.SdCard | StorageLayoutType.Usb;
-
- if (type == SaveDataType.System ||
- spaceId != SaveDataSpaceId.SdSystem && spaceId != SaveDataSpaceId.SdUser)
- return StorageLayoutType.Bis;
-
- return StorageLayoutType.SdCard | StorageLayoutType.Usb;
- }
-
Result ISaveDataTransferCoreInterface.CreateSaveDataFileSystemCore(in SaveDataAttribute attribute,
in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional hashSalt,
bool leaveUnfinalized)
@@ -2229,28 +2742,21 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return WriteSaveDataFileSystemExtraDataCore(spaceId, saveDataId, in extraData, type, updateTimeStamp);
}
- Result ISaveDataTransferCoreInterface.OpenSaveDataMetaFileRaw(ref SharedRef file,
- SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode)
- {
- return OpenSaveDataMetaFileRaw(ref file, spaceId, saveDataId, metaType, mode);
- }
-
Result ISaveDataTransferCoreInterface.OpenSaveDataInternalStorageFileSystemCore(
- ref SharedRef fileSystem, SaveDataSpaceId spaceId, ulong saveDataId,
- bool useSecondMacKey)
+ ref SharedRef fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey)
{
return OpenSaveDataInternalStorageFileSystemCore(ref fileSystem, spaceId, saveDataId, useSecondMacKey);
}
+ Result ISaveDataTransferCoreInterface.OpenSaveDataMetaFileRaw(ref SharedRef outFile, SaveDataSpaceId spaceId,
+ ulong saveDataId, SaveDataMetaType metaType, OpenMode mode)
+ {
+ return OpenSaveDataMetaFileRaw(ref outFile, spaceId, saveDataId, metaType, mode);
+ }
+
Result ISaveDataTransferCoreInterface.OpenSaveDataIndexerAccessor(
ref UniqueRef outAccessor, SaveDataSpaceId spaceId)
{
return OpenSaveDataIndexerAccessor(ref outAccessor, spaceId);
}
-
- public void Dispose()
- {
- _openEntryCountSemaphore.Dispose();
- _saveDataMountCountSemaphore.Dispose();
- }
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
index 0876cfaa..d04a86d9 100644
--- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
+++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
@@ -15,13 +15,13 @@ using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv;
-public class SaveDataFileSystemServiceImpl
+public class SaveDataFileSystemServiceImpl : IDisposable
{
private Configuration _config;
private EncryptionSeed _encryptionSeed;
- private SaveDataFileSystemCacheManager _saveDataFsCacheManager;
- private SaveDataExtraDataAccessorCacheManager _extraDataCacheManager;
+ private SaveDataFileSystemCacheManager _saveFileSystemCacheManager;
+ private SaveDataExtraDataAccessorCacheManager _saveExtraDataCacheManager;
// Save data porter manager
private bool _isSdCardAccessible;
private TimeStampGetter _timeStampGetter;
@@ -40,22 +40,10 @@ public class SaveDataFileSystemServiceImpl
public Result Get(out long timeStamp)
{
- return _saveService.GetSaveDataCommitTimeStamp(out timeStamp);
+ return _saveService.GetSaveDataCommitTimeStamp(out timeStamp).Ret();
}
}
- public SaveDataFileSystemServiceImpl(in Configuration configuration)
- {
- _config = configuration;
- _saveDataFsCacheManager = new SaveDataFileSystemCacheManager();
- _extraDataCacheManager = new SaveDataExtraDataAccessorCacheManager();
-
- _timeStampGetter = new TimeStampGetter(this);
-
- Result rc = _saveDataFsCacheManager.Initialize(_config.MaxSaveFsCacheCount);
- Abort.DoAbortUnless(rc.IsSuccess());
- }
-
public struct Configuration
{
public BaseFileSystemServiceImpl BaseFsService;
@@ -68,7 +56,7 @@ public class SaveDataFileSystemServiceImpl
public IBufferManager BufferManager;
public RandomDataGenerator GenerateRandomData;
public SaveDataTransferCryptoConfiguration SaveTransferCryptoConfig;
- public int MaxSaveFsCacheCount;
+ public int SaveDataFileSystemCacheCount;
public Func IsPseudoSaveData;
public ISaveDataIndexerManager SaveIndexerManager;
@@ -76,10 +64,36 @@ public class SaveDataFileSystemServiceImpl
public FileSystemServer FsServer;
}
- internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
+ private static bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
{
- var registry = new ProgramRegistryImpl(_config.FsServer);
- return registry.GetProgramInfo(out programInfo, processId);
+ return spaceId == SaveDataSpaceId.System ||
+ spaceId == SaveDataSpaceId.User ||
+ spaceId == SaveDataSpaceId.Temporary ||
+ spaceId == SaveDataSpaceId.ProperSystem ||
+ spaceId == SaveDataSpaceId.SafeMode;
+ }
+
+ private static Result WipeData(IFileSystem fileSystem, in Path filePath, RandomDataGenerator random)
+ {
+ throw new NotImplementedException();
+ }
+
+ public SaveDataFileSystemServiceImpl(in Configuration configuration)
+ {
+ _config = configuration;
+ _saveFileSystemCacheManager = new SaveDataFileSystemCacheManager();
+ _saveExtraDataCacheManager = new SaveDataExtraDataAccessorCacheManager();
+
+ _timeStampGetter = new TimeStampGetter(this);
+
+ Result rc = _saveFileSystemCacheManager.Initialize(_config.SaveDataFileSystemCacheCount);
+ Abort.DoAbortUnless(rc.IsSuccess());
+ }
+
+ public void Dispose()
+ {
+ _saveFileSystemCacheManager.Dispose();
+ _saveExtraDataCacheManager.Dispose();
}
public Result DoesSaveDataEntityExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId)
@@ -116,7 +130,12 @@ public class SaveDataFileSystemServiceImpl
}
}
- // 14.3.0
+ public Result OpenSaveDataFile(ref SharedRef outFile, SaveDataSpaceId spaceId, ulong saveDataId,
+ OpenMode openMode)
+ {
+ throw new NotImplementedException();
+ }
+
public Result OpenSaveDataFileSystem(ref SharedRef outFileSystem, SaveDataSpaceId spaceId,
ulong saveDataId, in Path saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData)
{
@@ -141,10 +160,10 @@ public class SaveDataFileSystemServiceImpl
using var saveDataFs = new SharedRef();
- using (_saveDataFsCacheManager.GetScopedLock())
- using (_extraDataCacheManager.GetScopedLock())
+ using (_saveFileSystemCacheManager.GetScopedLock())
+ using (_saveExtraDataCacheManager.GetScopedLock())
{
- if (isEmulatedOnHost || !_saveDataFsCacheManager.GetCache(ref saveDataFs.Ref(), spaceId, saveDataId))
+ if (isEmulatedOnHost || !_saveFileSystemCacheManager.GetCache(ref saveDataFs.Ref(), spaceId, saveDataId))
{
bool isDeviceUniqueMac = IsDeviceUniqueMac(spaceId);
bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(type);
@@ -163,13 +182,13 @@ public class SaveDataFileSystemServiceImpl
using SharedRef extraDataAccessor =
SharedRef.CreateCopy(in saveDataFs);
- rc = _extraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId);
+ rc = _saveExtraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId);
if (rc.IsFailure()) return rc.Miss();
}
}
using var registerFs = new SharedRef(
- new SaveDataFileSystemCacheRegister(ref saveDataFs.Ref(), _saveDataFsCacheManager, spaceId, saveDataId));
+ new SaveDataFileSystemCacheRegister(ref saveDataFs.Ref(), _saveFileSystemCacheManager, spaceId, saveDataId));
if (openReadOnly)
{
@@ -203,11 +222,61 @@ public class SaveDataFileSystemServiceImpl
}
public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef outFileSystem,
- SaveDataSpaceId spaceId, ulong saveDataId, in Path saveDataRootPath, bool useSecondMacKey)
+ SaveDataSpaceId spaceId, ulong saveDataId, in Path saveDataRootPath, bool useSecondMacKey,
+ bool isReconstructible)
{
throw new NotImplementedException();
}
+ public Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId,
+ SaveDataType type, long dataSize, long journalSize, in Path saveDataRootPath, bool isExtensionStart)
+ {
+ throw new NotImplementedException();
+ }
+
+ private Result OpenSaveDataImageFile(ref UniqueRef outFile, SaveDataSpaceId spaceId, ulong saveDataId,
+ in Path saveDataRootPath)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result StartExtendSaveDataFileSystem(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId,
+ SaveDataType type, long dataSize, long journalSize, in Path saveDataRootPath)
+ {
+ return ExtendSaveDataFileSystemCore(out extendedTotalSize, saveDataId, spaceId, type, dataSize, journalSize,
+ in saveDataRootPath, isExtensionStart: true);
+ }
+
+ public Result ResumeExtendSaveDataFileSystem(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId,
+ SaveDataType type, in Path saveDataRootPath)
+ {
+ return ExtendSaveDataFileSystemCore(out extendedTotalSize, saveDataId, spaceId, type, dataSize: 0,
+ journalSize: 0, in saveDataRootPath, isExtensionStart: false);
+ }
+
+ public Result FinishExtendSaveDataFileSystem(ulong saveDataId, SaveDataSpaceId spaceId)
+ {
+ Result rc = DeleteSaveDataMeta(saveDataId, spaceId, SaveDataMetaType.ExtensionContext);
+ if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
+ return rc.Miss();
+
+ return Result.Success;
+ }
+
+ public void RevertExtendSaveDataFileSystem(ulong saveDataId, SaveDataSpaceId spaceId, long originalSize,
+ in Path saveDataRootPath)
+ {
+ using var saveDataFile = new UniqueRef();
+ Result rc = OpenSaveDataImageFile(ref saveDataFile.Ref(), spaceId, saveDataId, in saveDataRootPath);
+
+ if (rc.IsSuccess())
+ {
+ saveDataFile.Get.SetSize(originalSize).IgnoreResult();
+ }
+
+ FinishExtendSaveDataFileSystem(saveDataId, spaceId).IgnoreResult();
+ }
+
public Result QuerySaveDataTotalSize(out long totalSize, int blockSize, long dataSize, long journalSize)
{
// Todo: Implement
@@ -316,6 +385,82 @@ public class SaveDataFileSystemServiceImpl
return Result.Success;
}
+ public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataCreationInfo2 creationInfo,
+ in Path saveDataRootPath, bool skipFormat)
+ {
+ Unsafe.SkipInit(out Array18 saveImageNameBuffer);
+
+ long dataSize = creationInfo.Size;
+ long journalSize = creationInfo.JournalSize;
+ ulong ownerId = creationInfo.OwnerId;
+ SaveDataSpaceId spaceId = creationInfo.SpaceId;
+ SaveDataFlags flags = creationInfo.Flags;
+
+ using var fileSystem = new SharedRef();
+
+ Result rc = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), creationInfo.SpaceId, in saveDataRootPath,
+ allowEmulatedSave: false);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var saveImageName = new Path();
+ rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer.Items, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ bool isPseudoSaveFs = _config.IsPseudoSaveData();
+ bool isCreationSuccessful = false;
+
+ try
+ {
+ if (isPseudoSaveFs)
+ {
+ rc = FsSystem.Utility.EnsureDirectory(fileSystem.Get, in saveImageName);
+ if (rc.IsFailure()) return rc.Miss();
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+
+ SaveDataExtraData extraData = default;
+ extraData.Attribute = creationInfo.Attribute;
+ extraData.OwnerId = ownerId;
+
+ rc = GetSaveDataCommitTimeStamp(out extraData.TimeStamp);
+ if (rc.IsFailure())
+ extraData.TimeStamp = 0;
+
+ extraData.CommitId = 0;
+ _config.GenerateRandomData(SpanHelpers.AsByteSpan(ref extraData.CommitId));
+
+ extraData.Flags = flags;
+ extraData.DataSize = dataSize;
+ extraData.JournalSize = journalSize;
+ extraData.FormatType = creationInfo.FormatType;
+
+ rc = WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in saveDataRootPath,
+ creationInfo.Attribute.Type, updateTimeStamp: true);
+ if (rc.IsFailure()) return rc.Miss();
+
+ isCreationSuccessful = true;
+ return Result.Success;
+ }
+ finally
+ {
+ // Delete any created save if something goes wrong.
+ if (!isCreationSuccessful)
+ {
+ if (isPseudoSaveFs)
+ {
+ fileSystem.Get.DeleteDirectoryRecursively(in saveImageName).IgnoreResult();
+ }
+ else
+ {
+ fileSystem.Get.DeleteFile(in saveImageName).IgnoreResult();
+ }
+ }
+ }
+ }
+
public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataAttribute attribute,
in SaveDataCreationInfo creationInfo, in Path saveDataRootPath, in Optional hashSalt,
bool skipFormat)
@@ -379,11 +524,6 @@ public class SaveDataFileSystemServiceImpl
return Result.Success;
}
- private Result WipeData(IFileSystem fileSystem, in Path filePath, RandomDataGenerator random)
- {
- throw new NotImplementedException();
- }
-
public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile,
in Path saveDataRootPath)
{
@@ -391,7 +531,7 @@ public class SaveDataFileSystemServiceImpl
using var fileSystem = new SharedRef();
- _saveDataFsCacheManager.Unregister(spaceId, saveDataId);
+ _saveFileSystemCacheManager.Unregister(spaceId, saveDataId);
// Open the directory containing the save data
Result rc = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), spaceId, in saveDataRootPath, false);
@@ -433,13 +573,13 @@ public class SaveDataFileSystemServiceImpl
// Nintendo returns blank extra data for directory save data.
// We've extended directory save data to store extra data so we don't need to do that.
- using UniqueLockRef scopedLockFsCache = _saveDataFsCacheManager.GetScopedLock();
- using UniqueLockRef scopedLockExtraDataCache = _extraDataCacheManager.GetScopedLock();
+ using UniqueLockRef scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock();
+ using UniqueLockRef scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock();
using var extraDataAccessor = new SharedRef();
// Try to grab an extra data accessor for the requested save from the cache.
- Result rc = _extraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
+ Result rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
if (rc.IsSuccess())
{
@@ -456,7 +596,7 @@ public class SaveDataFileSystemServiceImpl
if (rc.IsFailure()) return rc.Miss();
// Try to grab an accessor from the cache again.
- rc = _extraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
+ rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
if (rc.IsFailure())
{
@@ -478,13 +618,13 @@ public class SaveDataFileSystemServiceImpl
// 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.
- using UniqueLockRef scopedLockFsCache = _saveDataFsCacheManager.GetScopedLock();
- using UniqueLockRef scopedLockExtraDataCache = _extraDataCacheManager.GetScopedLock();
+ using UniqueLockRef scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock();
+ using UniqueLockRef scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock();
using var extraDataAccessor = new SharedRef();
// Try to grab an extra data accessor for the requested save from the cache.
- Result rc = _extraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
+ Result rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
if (rc.IsFailure())
{
@@ -498,7 +638,7 @@ public class SaveDataFileSystemServiceImpl
if (rc.IsFailure()) return rc.Miss();
// Try to grab an accessor from the cache again.
- rc = _extraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
+ rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
if (rc.IsFailure())
{
@@ -592,12 +732,6 @@ public class SaveDataFileSystemServiceImpl
return Result.Success;
}
- public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef outFileSystem,
- SaveDataSpaceId spaceId, in Path basePath)
- {
- return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in basePath, true);
- }
-
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef outFileSystem,
SaveDataSpaceId spaceId, in Path basePath, bool createIfMissing)
{
@@ -680,15 +814,10 @@ public class SaveDataFileSystemServiceImpl
}
}
- public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
+ public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef outFileSystem,
+ SaveDataSpaceId spaceId, in Path basePath)
{
- _encryptionSeed = seed;
-
- _config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value);
- _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
- _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser);
-
- return Result.Success;
+ return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in basePath, true);
}
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
@@ -704,13 +833,15 @@ public class SaveDataFileSystemServiceImpl
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath);
}
- public bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
+ public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{
- return spaceId == SaveDataSpaceId.System ||
- spaceId == SaveDataSpaceId.User ||
- spaceId == SaveDataSpaceId.Temporary ||
- spaceId == SaveDataSpaceId.ProperSystem ||
- spaceId == SaveDataSpaceId.SafeMode;
+ _encryptionSeed = seed;
+
+ _config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value);
+ _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
+ _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser);
+
+ return Result.Success;
}
public void SetSdCardAccessibility(bool isAccessible)
@@ -753,6 +884,11 @@ public class SaveDataFileSystemServiceImpl
return programId;
}
+ public SaveDataTransferCryptoConfiguration GetSaveDataTransferCryptoConfiguration()
+ {
+ throw new NotImplementedException();
+ }
+
public Result GetSaveDataIndexCount(out int count)
{
UnsafeHelpers.SkipParamInit(out count);
@@ -766,10 +902,10 @@ public class SaveDataFileSystemServiceImpl
return Result.Success;
}
- public Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor, out bool neededInit,
- SaveDataSpaceId spaceId)
+ public Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor,
+ out bool isInitialOpen, SaveDataSpaceId spaceId)
{
- return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(ref outAccessor, out neededInit, spaceId);
+ return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(ref outAccessor, out isInitialOpen, spaceId);
}
public void ResetTemporaryStorageIndexer()
diff --git a/src/LibHac/FsSrv/SaveDataIndexerManager.cs b/src/LibHac/FsSrv/SaveDataIndexerManager.cs
index a2bb10cc..c7730787 100644
--- a/src/LibHac/FsSrv/SaveDataIndexerManager.cs
+++ b/src/LibHac/FsSrv/SaveDataIndexerManager.cs
@@ -122,14 +122,14 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager, IDisposable
/// The accessor must be disposed after use.
///
/// If the method returns successfully, contains the created accessor.
- /// If the method returns successfully, contains
- /// if the indexer needed to be initialized.
+ /// If the method returns successfully, contains
+ /// if the indexer needed to be initialized because this was the first time it was opened.
/// The of the indexer to open.
/// The of the operation.
public Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor,
- out bool neededInit, SaveDataSpaceId spaceId)
+ out bool isInitialOpen, SaveDataSpaceId spaceId)
{
- UnsafeHelpers.SkipParamInit(out neededInit);
+ UnsafeHelpers.SkipParamInit(out isInitialOpen);
if (_isBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User)
{
@@ -226,7 +226,7 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager, IDisposable
}
outAccessor.Reset(new SaveDataIndexerAccessor(indexer, ref indexerLock.Ref()));
- neededInit = wasIndexerInitialized;
+ isInitialOpen = wasIndexerInitialized;
return Result.Success;
}
diff --git a/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs b/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs
index c04a7be0..5b574bba 100644
--- a/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs
+++ b/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs
@@ -13,7 +13,7 @@ namespace LibHac.FsSrv.Sf;
///
/// The interface most programs use to interact with the FS service.
///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
public interface IFileSystemProxy : IDisposable
{
Result SetCurrentProcess(ulong processId);
@@ -42,6 +42,7 @@ public interface IFileSystemProxy : IDisposable
Result GetCacheStorageSize(out long dataSize, out long journalSize, ushort index);
Result CreateSaveDataFileSystemWithHashSalt(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt);
Result OpenHostFileSystemWithOption(ref SharedRef outFileSystem, in FspPath path, MountHostOption option);
+ Result CreateSaveDataFileSystemWithCreationInfo2(in SaveDataCreationInfo2 creationInfo);
Result OpenSaveDataFileSystem(ref SharedRef outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenSaveDataFileSystemBySystemSaveDataId(ref SharedRef outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenReadOnlySaveDataFileSystem(ref SharedRef outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs
index 5c102ac9..0ef83b6e 100644
--- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs
+++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs
@@ -136,6 +136,7 @@ public class TypeLayoutTests
Assert.Equal(0x40, GetOffset(in s, in s.OwnerId));
Assert.Equal(0x48, GetOffset(in s, in s.TimeStamp));
Assert.Equal(0x50, GetOffset(in s, in s.Flags));
+ Assert.Equal(0x54, GetOffset(in s, in s.FormatType));
Assert.Equal(0x58, GetOffset(in s, in s.DataSize));
Assert.Equal(0x60, GetOffset(in s, in s.JournalSize));
Assert.Equal(0x68, GetOffset(in s, in s.CommitId));