Update SaveDataFileSystemService for 14.0.0

- Creating save data file systems now uses a new SaveDataCreationInfo2 struct.
- Ensure all implemented functions in the class are accurate to 14.0.0
This commit is contained in:
Alex Barney 2022-04-28 15:13:46 -07:00
parent 3725c21928
commit 7b44441ff2
14 changed files with 1256 additions and 589 deletions

View file

@ -43,4 +43,7 @@ public static class Log
diag.LogImpl(in metaData, message); diag.LogImpl(in metaData, message);
} }
/// <summary>"<c>$</c>"</summary>
public static ReadOnlySpan<byte> EmptyModuleName => new[] { (byte)'$' }; // "$"
} }

View file

@ -94,9 +94,17 @@ public ref struct Path
[DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")]
public struct Stored : IDisposable public struct Stored : IDisposable
{ {
private static readonly byte[] EmptyBuffer = { 0 };
private byte[] _buffer; private byte[] _buffer;
private int _length; private int _length;
public Stored()
{
_buffer = EmptyBuffer;
_length = 0;
}
public void Dispose() public void Dispose()
{ {
byte[] buffer = Shared.Move(ref _buffer); byte[] buffer = Shared.Move(ref _buffer);
@ -160,7 +168,8 @@ public ref struct Path
byte[] oldBuffer = _buffer; byte[] oldBuffer = _buffer;
_buffer = 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<byte>.Shared.Return(oldBuffer); ArrayPool<byte>.Shared.Return(oldBuffer);
return Result.Success; return Result.Success;

View file

@ -16,8 +16,7 @@ public enum SaveDataSpaceId : byte
Temporary = 3, Temporary = 3,
SdUser = 4, SdUser = 4,
ProperSystem = 100, ProperSystem = 100,
SafeMode = 101, SafeMode = 101
BisAuto = 127
} }
public enum SaveDataType : byte public enum SaveDataType : byte
@ -99,6 +98,7 @@ public struct SaveDataExtraData
public ulong OwnerId; public ulong OwnerId;
public long TimeStamp; public long TimeStamp;
public SaveDataFlags Flags; public SaveDataFlags Flags;
public SaveDataFormatType FormatType;
public long DataSize; public long DataSize;
public long JournalSize; public long JournalSize;
public long CommitId; public long CommitId;
@ -371,7 +371,7 @@ internal static class SaveDataTypesValidity
{ {
public static bool IsValid(in SaveDataAttribute attribute) 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) public static bool IsValid(in SaveDataCreationInfo creationInfo)

View file

@ -4,8 +4,9 @@ namespace LibHac.Fs.Impl;
internal readonly struct SaveDataMetaPolicy internal readonly struct SaveDataMetaPolicy
{ {
internal const int ThumbnailFileSize = 0x40060;
private readonly SaveDataType _type; private readonly SaveDataType _type;
private const int ThumbnailFileSize = 0x40060;
public SaveDataMetaPolicy(SaveDataType saveType) public SaveDataMetaPolicy(SaveDataType saveType)
{ {
@ -39,4 +40,4 @@ internal readonly struct SaveDataMetaPolicy
GenerateMetaInfo(out SaveDataMetaInfo metaInfo); GenerateMetaInfo(out SaveDataMetaInfo metaInfo);
return metaInfo.Type; return metaInfo.Type;
} }
} }

View file

@ -352,6 +352,14 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
return saveFsService.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in creationInfo); 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, public Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize,
long journalSize) long journalSize)
{ {

View file

@ -136,7 +136,7 @@ public static class FileSystemServerInitializer
saveFsServiceConfig.BufferManager = bufferManager; saveFsServiceConfig.BufferManager = bufferManager;
saveFsServiceConfig.GenerateRandomData = config.RandomGenerator; saveFsServiceConfig.GenerateRandomData = config.RandomGenerator;
saveFsServiceConfig.IsPseudoSaveData = () => true; saveFsServiceConfig.IsPseudoSaveData = () => true;
saveFsServiceConfig.MaxSaveFsCacheCount = 1; saveFsServiceConfig.SaveDataFileSystemCacheCount = 1;
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager; saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
saveFsServiceConfig.FsServer = server; saveFsServiceConfig.FsServer = server;

View file

@ -5,7 +5,7 @@ namespace LibHac.FsSrv;
public interface ISaveDataIndexerManager public interface ISaveDataIndexerManager
{ {
Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, out bool neededInit, SaveDataSpaceId spaceId); Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, out bool isInitialOpen, SaveDataSpaceId spaceId);
void ResetIndexer(SaveDataSpaceId spaceId); void ResetIndexer(SaveDataSpaceId spaceId);
void InvalidateIndexer(SaveDataSpaceId spaceId); void InvalidateIndexer(SaveDataSpaceId spaceId);
} }

View file

@ -2,15 +2,15 @@
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Os;
using LibHac.Util; using LibHac.Util;
using IFileSf = LibHac.FsSrv.Sf.IFile;
namespace LibHac.FsSrv.Impl; namespace LibHac.FsSrv.Impl;
public interface ISaveDataTransferCoreInterface : IDisposable public interface ISaveDataTransferCoreInterface : IDisposable
{ {
Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId); Result GetFreeSpaceSizeForSaveData(out long outFreeSpaceSize, SaveDataSpaceId spaceId);
Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize); Result QuerySaveDataTotalSize(out long outTotalSize, long dataSize, long journalSize);
Result CheckSaveDataFile(long saveDataId, SaveDataSpaceId spaceId); Result CheckSaveDataFile(long saveDataId, SaveDataSpaceId spaceId);
Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt, bool leaveUnfinalized); Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt, bool leaveUnfinalized);
Result GetSaveDataInfo(out SaveDataInfo saveInfo, SaveDataSpaceId spaceId, in SaveDataAttribute attribute); 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 WriteSaveDataFileSystemExtraDataCore(SaveDataSpaceId spaceId, ulong saveDataId, in SaveDataExtraData extraData, SaveDataType type, bool updateTimeStamp);
Result FinalizeSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId); Result FinalizeSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
Result CancelSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId); Result CancelSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
Result OpenSaveDataFile(ref SharedRef<IFileSf> file, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, SaveDataMetaType metaType); Result OpenSaveDataFile(ref SharedRef<IFile> oufFile, SaveDataSpaceId spaceId, ulong saveDataId, OpenMode mode);
Result OpenSaveDataMetaFileRaw(ref SharedRef<IFile> file, SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode); Result OpenSaveDataMetaFileRaw(ref SharedRef<IFile> outFile, SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode);
Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef<IFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey); Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef<IFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey);
Result OpenSaveDataFileSystemCore(ref SharedRef<IFileSystem> outFileSystem, out ulong outSaveDataId, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData);
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize); Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId); Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2); Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2);
Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state); Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state);
Result SetSaveDataRank(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataRank rank); Result SetSaveDataRank(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataRank rank);
Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, SaveDataSpaceId spaceId); Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, SaveDataSpaceId spaceId);
} bool IsProhibited(ref UniqueLock<SdkMutex> outLock, ApplicationId applicationId);
}

View file

@ -46,7 +46,7 @@ internal class MultiCommitManager : IMultiCommitManager
{ {
private const int MaxFileSystemCount = 10; private const int MaxFileSystemCount = 10;
public const ulong ProgramId = 0x0100000000000000; public const ulong ProgramId = 0;
public const ulong SaveDataId = 0x8000000000000001; public const ulong SaveDataId = 0x8000000000000001;
private const long SaveDataSize = 0xC000; private const long SaveDataSize = 0xC000;
private const long SaveJournalSize = 0xC000; private const long SaveJournalSize = 0xC000;

File diff suppressed because it is too large Load diff

View file

@ -15,13 +15,13 @@ using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv; namespace LibHac.FsSrv;
public class SaveDataFileSystemServiceImpl public class SaveDataFileSystemServiceImpl : IDisposable
{ {
private Configuration _config; private Configuration _config;
private EncryptionSeed _encryptionSeed; private EncryptionSeed _encryptionSeed;
private SaveDataFileSystemCacheManager _saveDataFsCacheManager; private SaveDataFileSystemCacheManager _saveFileSystemCacheManager;
private SaveDataExtraDataAccessorCacheManager _extraDataCacheManager; private SaveDataExtraDataAccessorCacheManager _saveExtraDataCacheManager;
// Save data porter manager // Save data porter manager
private bool _isSdCardAccessible; private bool _isSdCardAccessible;
private TimeStampGetter _timeStampGetter; private TimeStampGetter _timeStampGetter;
@ -40,22 +40,10 @@ public class SaveDataFileSystemServiceImpl
public Result Get(out long timeStamp) 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 struct Configuration
{ {
public BaseFileSystemServiceImpl BaseFsService; public BaseFileSystemServiceImpl BaseFsService;
@ -68,7 +56,7 @@ public class SaveDataFileSystemServiceImpl
public IBufferManager BufferManager; public IBufferManager BufferManager;
public RandomDataGenerator GenerateRandomData; public RandomDataGenerator GenerateRandomData;
public SaveDataTransferCryptoConfiguration SaveTransferCryptoConfig; public SaveDataTransferCryptoConfiguration SaveTransferCryptoConfig;
public int MaxSaveFsCacheCount; public int SaveDataFileSystemCacheCount;
public Func<bool> IsPseudoSaveData; public Func<bool> IsPseudoSaveData;
public ISaveDataIndexerManager SaveIndexerManager; public ISaveDataIndexerManager SaveIndexerManager;
@ -76,10 +64,36 @@ public class SaveDataFileSystemServiceImpl
public FileSystemServer FsServer; public FileSystemServer FsServer;
} }
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId) private static bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
{ {
var registry = new ProgramRegistryImpl(_config.FsServer); return spaceId == SaveDataSpaceId.System ||
return registry.GetProgramInfo(out programInfo, processId); 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) 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<IFile> outFile, SaveDataSpaceId spaceId, ulong saveDataId,
OpenMode openMode)
{
throw new NotImplementedException();
}
public Result OpenSaveDataFileSystem(ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId, public Result OpenSaveDataFileSystem(ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId,
ulong saveDataId, in Path saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData) ulong saveDataId, in Path saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData)
{ {
@ -141,10 +160,10 @@ public class SaveDataFileSystemServiceImpl
using var saveDataFs = new SharedRef<ISaveDataFileSystem>(); using var saveDataFs = new SharedRef<ISaveDataFileSystem>();
using (_saveDataFsCacheManager.GetScopedLock()) using (_saveFileSystemCacheManager.GetScopedLock())
using (_extraDataCacheManager.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 isDeviceUniqueMac = IsDeviceUniqueMac(spaceId);
bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(type); bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(type);
@ -163,13 +182,13 @@ public class SaveDataFileSystemServiceImpl
using SharedRef<ISaveDataExtraDataAccessor> extraDataAccessor = using SharedRef<ISaveDataExtraDataAccessor> extraDataAccessor =
SharedRef<ISaveDataExtraDataAccessor>.CreateCopy(in saveDataFs); SharedRef<ISaveDataExtraDataAccessor>.CreateCopy(in saveDataFs);
rc = _extraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId); rc = _saveExtraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
} }
} }
using var registerFs = new SharedRef<SaveDataFileSystemCacheRegister>( using var registerFs = new SharedRef<SaveDataFileSystemCacheRegister>(
new SaveDataFileSystemCacheRegister(ref saveDataFs.Ref(), _saveDataFsCacheManager, spaceId, saveDataId)); new SaveDataFileSystemCacheRegister(ref saveDataFs.Ref(), _saveFileSystemCacheManager, spaceId, saveDataId));
if (openReadOnly) if (openReadOnly)
{ {
@ -203,11 +222,61 @@ public class SaveDataFileSystemServiceImpl
} }
public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef<IFileSystem> outFileSystem, public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, ulong saveDataId, in Path saveDataRootPath, bool useSecondMacKey) SaveDataSpaceId spaceId, ulong saveDataId, in Path saveDataRootPath, bool useSecondMacKey,
bool isReconstructible)
{ {
throw new NotImplementedException(); 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<IFile> 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<IFile>();
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) public Result QuerySaveDataTotalSize(out long totalSize, int blockSize, long dataSize, long journalSize)
{ {
// Todo: Implement // Todo: Implement
@ -316,6 +385,82 @@ public class SaveDataFileSystemServiceImpl
return Result.Success; return Result.Success;
} }
public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataCreationInfo2 creationInfo,
in Path saveDataRootPath, bool skipFormat)
{
Unsafe.SkipInit(out Array18<byte> 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<IFileSystem>();
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, public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataAttribute attribute,
in SaveDataCreationInfo creationInfo, in Path saveDataRootPath, in Optional<HashSalt> hashSalt, in SaveDataCreationInfo creationInfo, in Path saveDataRootPath, in Optional<HashSalt> hashSalt,
bool skipFormat) bool skipFormat)
@ -379,11 +524,6 @@ public class SaveDataFileSystemServiceImpl
return Result.Success; 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, public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile,
in Path saveDataRootPath) in Path saveDataRootPath)
{ {
@ -391,7 +531,7 @@ public class SaveDataFileSystemServiceImpl
using var fileSystem = new SharedRef<IFileSystem>(); using var fileSystem = new SharedRef<IFileSystem>();
_saveDataFsCacheManager.Unregister(spaceId, saveDataId); _saveFileSystemCacheManager.Unregister(spaceId, saveDataId);
// Open the directory containing the save data // Open the directory containing the save data
Result rc = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), spaceId, in saveDataRootPath, false); 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. // 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. // We've extended directory save data to store extra data so we don't need to do that.
using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveDataFsCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock();
using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _extraDataCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock();
using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>(); using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>();
// Try to grab an extra data accessor for the requested save from the cache. // 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()) if (rc.IsSuccess())
{ {
@ -456,7 +596,7 @@ public class SaveDataFileSystemServiceImpl
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
// Try to grab an accessor from the cache again. // 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()) if (rc.IsFailure())
{ {
@ -478,13 +618,13 @@ public class SaveDataFileSystemServiceImpl
// Nintendo does nothing when writing directory save data extra data. // 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. // We've extended directory save data to store extra data so we don't return early.
using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveDataFsCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock();
using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _extraDataCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock();
using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>(); using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>();
// Try to grab an extra data accessor for the requested save from the cache. // 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()) if (rc.IsFailure())
{ {
@ -498,7 +638,7 @@ public class SaveDataFileSystemServiceImpl
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
// Try to grab an accessor from the cache again. // 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()) if (rc.IsFailure())
{ {
@ -592,12 +732,6 @@ public class SaveDataFileSystemServiceImpl
return Result.Success; return Result.Success;
} }
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, in Path basePath)
{
return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in basePath, true);
}
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem, public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, in Path basePath, bool createIfMissing) 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<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, in Path basePath)
{ {
_encryptionSeed = seed; return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in basePath, true);
_config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value);
_config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
_config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser);
return Result.Success;
} }
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo) public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
@ -704,13 +833,15 @@ public class SaveDataFileSystemServiceImpl
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath); return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath);
} }
public bool IsDeviceUniqueMac(SaveDataSpaceId spaceId) public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{ {
return spaceId == SaveDataSpaceId.System || _encryptionSeed = seed;
spaceId == SaveDataSpaceId.User ||
spaceId == SaveDataSpaceId.Temporary || _config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value);
spaceId == SaveDataSpaceId.ProperSystem || _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
spaceId == SaveDataSpaceId.SafeMode; _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser);
return Result.Success;
} }
public void SetSdCardAccessibility(bool isAccessible) public void SetSdCardAccessibility(bool isAccessible)
@ -753,6 +884,11 @@ public class SaveDataFileSystemServiceImpl
return programId; return programId;
} }
public SaveDataTransferCryptoConfiguration GetSaveDataTransferCryptoConfiguration()
{
throw new NotImplementedException();
}
public Result GetSaveDataIndexCount(out int count) public Result GetSaveDataIndexCount(out int count)
{ {
UnsafeHelpers.SkipParamInit(out count); UnsafeHelpers.SkipParamInit(out count);
@ -766,10 +902,10 @@ public class SaveDataFileSystemServiceImpl
return Result.Success; return Result.Success;
} }
public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, out bool neededInit, public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor,
SaveDataSpaceId spaceId) 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() public void ResetTemporaryStorageIndexer()

View file

@ -122,14 +122,14 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager, IDisposable
/// The accessor must be disposed after use. /// The accessor must be disposed after use.
/// </remarks> /// </remarks>
/// <param name="outAccessor">If the method returns successfully, contains the created accessor.</param> /// <param name="outAccessor">If the method returns successfully, contains the created accessor.</param>
/// <param name="neededInit">If the method returns successfully, contains <see langword="true"/> /// <param name="isInitialOpen">If the method returns successfully, contains <see langword="true"/>
/// if the indexer needed to be initialized.</param> /// if the indexer needed to be initialized because this was the first time it was opened.</param>
/// <param name="spaceId">The <see cref="SaveDataSpaceId"/> of the indexer to open.</param> /// <param name="spaceId">The <see cref="SaveDataSpaceId"/> of the indexer to open.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor,
out bool neededInit, SaveDataSpaceId spaceId) out bool isInitialOpen, SaveDataSpaceId spaceId)
{ {
UnsafeHelpers.SkipParamInit(out neededInit); UnsafeHelpers.SkipParamInit(out isInitialOpen);
if (_isBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User) if (_isBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User)
{ {
@ -226,7 +226,7 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager, IDisposable
} }
outAccessor.Reset(new SaveDataIndexerAccessor(indexer, ref indexerLock.Ref())); outAccessor.Reset(new SaveDataIndexerAccessor(indexer, ref indexerLock.Ref()));
neededInit = wasIndexerInitialized; isInitialOpen = wasIndexerInitialized;
return Result.Success; return Result.Success;
} }

View file

@ -13,7 +13,7 @@ namespace LibHac.FsSrv.Sf;
/// <summary> /// <summary>
/// The interface most programs use to interact with the FS service. /// The interface most programs use to interact with the FS service.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks> /// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public interface IFileSystemProxy : IDisposable public interface IFileSystemProxy : IDisposable
{ {
Result SetCurrentProcess(ulong processId); Result SetCurrentProcess(ulong processId);
@ -42,6 +42,7 @@ public interface IFileSystemProxy : IDisposable
Result GetCacheStorageSize(out long dataSize, out long journalSize, ushort index); 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 CreateSaveDataFileSystemWithHashSalt(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt);
Result OpenHostFileSystemWithOption(ref SharedRef<IFileSystemSf> outFileSystem, in FspPath path, MountHostOption option); Result OpenHostFileSystemWithOption(ref SharedRef<IFileSystemSf> outFileSystem, in FspPath path, MountHostOption option);
Result CreateSaveDataFileSystemWithCreationInfo2(in SaveDataCreationInfo2 creationInfo);
Result OpenSaveDataFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute); Result OpenSaveDataFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenSaveDataFileSystemBySystemSaveDataId(ref SharedRef<IFileSystemSf> outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute); Result OpenSaveDataFileSystemBySystemSaveDataId(ref SharedRef<IFileSystemSf> outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenReadOnlySaveDataFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute); Result OpenReadOnlySaveDataFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);

View file

@ -136,6 +136,7 @@ public class TypeLayoutTests
Assert.Equal(0x40, GetOffset(in s, in s.OwnerId)); Assert.Equal(0x40, GetOffset(in s, in s.OwnerId));
Assert.Equal(0x48, GetOffset(in s, in s.TimeStamp)); Assert.Equal(0x48, GetOffset(in s, in s.TimeStamp));
Assert.Equal(0x50, GetOffset(in s, in s.Flags)); 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(0x58, GetOffset(in s, in s.DataSize));
Assert.Equal(0x60, GetOffset(in s, in s.JournalSize)); Assert.Equal(0x60, GetOffset(in s, in s.JournalSize));
Assert.Equal(0x68, GetOffset(in s, in s.CommitId)); Assert.Equal(0x68, GetOffset(in s, in s.CommitId));