From 2b2b7471eafacd690f633e07f2ad96262eb02d3a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 9 Mar 2024 15:23:26 -0700 Subject: [PATCH] Implement the rest of SaveDataInternalStorageFileSystem --- src/LibHac/Common/ConstGenerics.cs | 6 + src/LibHac/Fs/Common/FileStorage.cs | 4 +- src/LibHac/FsSrv/DefaultFsServerObjects.cs | 2 +- src/LibHac/FsSrv/Delegates.cs | 4 + .../FsCreator/ISaveDataFileSystemCreator.cs | 2 +- .../FsCreator/SaveDataFileSystemCreator.cs | 511 +++++++++++++++++- src/LibHac/FsSrv/SaveDataSharedFileStorage.cs | 14 +- .../ApplicationTemporaryFileSystem.cs | 14 +- .../JournalIntegritySaveDataFileSystem.cs | 10 +- src/LibHac/FsSystem/SaveDataFileSystem.cs | 5 + .../SaveDataResultConvertFileSystem.cs | 5 +- .../SaveDataResultConverter.cs | 2 +- 12 files changed, 528 insertions(+), 51 deletions(-) create mode 100644 src/LibHac/Common/ConstGenerics.cs rename src/LibHac/{FsSrv/FsCreator => FsSystem}/SaveDataResultConvertFileSystem.cs (97%) rename src/LibHac/{FsSrv/FsCreator => FsSystem}/SaveDataResultConverter.cs (99%) diff --git a/src/LibHac/Common/ConstGenerics.cs b/src/LibHac/Common/ConstGenerics.cs new file mode 100644 index 00000000..b56f0093 --- /dev/null +++ b/src/LibHac/Common/ConstGenerics.cs @@ -0,0 +1,6 @@ +namespace LibHac.Common; + +public interface IConstant where T : struct +{ + static abstract T Value { get; } +} \ No newline at end of file diff --git a/src/LibHac/Fs/Common/FileStorage.cs b/src/LibHac/Fs/Common/FileStorage.cs index 0b26e54d..c2397679 100644 --- a/src/LibHac/Fs/Common/FileStorage.cs +++ b/src/LibHac/Fs/Common/FileStorage.cs @@ -179,7 +179,7 @@ public class FileStorageBasedFileSystem : FileStorage /// : The specified path does not exist or is a directory.
/// : When opening as , /// the file is already opened as . - public Result Initialize(ref SharedRef baseFileSystem, ref readonly Path path, OpenMode mode) + public Result Initialize(ref readonly SharedRef baseFileSystem, ref readonly Path path, OpenMode mode) { using var baseFile = new UniqueRef(); @@ -187,7 +187,7 @@ public class FileStorageBasedFileSystem : FileStorage if (res.IsFailure()) return res.Miss(); SetFile(baseFile.Get); - _baseFileSystem.SetByMove(ref baseFileSystem); + _baseFileSystem.SetByCopy(in baseFileSystem); _baseFile.Set(ref baseFile.Ref); return Result.Success; diff --git a/src/LibHac/FsSrv/DefaultFsServerObjects.cs b/src/LibHac/FsSrv/DefaultFsServerObjects.cs index b9aee8d1..a7df3d0c 100644 --- a/src/LibHac/FsSrv/DefaultFsServerObjects.cs +++ b/src/LibHac/FsSrv/DefaultFsServerObjects.cs @@ -37,7 +37,7 @@ public class DefaultFsServerObjects creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet); creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator(); creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator(); - creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, keySet, null, randomGenerator); + creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, null, randomGenerator); creators.GameCardStorageCreator = gcStorageCreator; creators.GameCardFileSystemCreator = new GameCardFileSystemCreator(new ArrayPoolMemoryResource(), gcStorageCreator, fsServer); creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet); diff --git a/src/LibHac/FsSrv/Delegates.cs b/src/LibHac/FsSrv/Delegates.cs index 7f789013..b20d67b5 100644 --- a/src/LibHac/FsSrv/Delegates.cs +++ b/src/LibHac/FsSrv/Delegates.cs @@ -1,8 +1,12 @@ using System; using LibHac.Common; +using LibHac.FsSrv.FsCreator; namespace LibHac.FsSrv; +public delegate Result GenerateSeedUniqueMac(Span outMacBuffer, ReadOnlySpan data, ReadOnlySpan seed); +public delegate Result GenerateDeviceUniqueMac(Span outMacBuffer, ReadOnlySpan data, DeviceUniqueMacType macType); + public delegate Result SaveTransferAesKeyGenerator(Span outKeyBuffer, SaveDataTransferCryptoConfiguration.KeyIndex index, ReadOnlySpan keySource, int keyGeneration); diff --git a/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs index 60ba88d5..01e4204b 100644 --- a/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs @@ -8,7 +8,7 @@ using LibHac.FsSystem.Save; namespace LibHac.FsSrv.FsCreator; -public interface ISaveDataFileSystemCreator +public interface ISaveDataFileSystemCreator : IDisposable { Result CreateRaw(ref SharedRef outFile, in SharedRef fileSystem, ulong saveDataId, OpenMode openMode); diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs index 615d067d..ecc6a188 100644 --- a/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs @@ -2,74 +2,314 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Common.FixedArrays; -using LibHac.Common.Keys; +using LibHac.Crypto; +using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; using LibHac.FsSrv.Impl; using LibHac.FsSystem; using LibHac.FsSystem.Save; using LibHac.Util; using OpenType = LibHac.FsSrv.SaveDataOpenTypeSetFileStorage.OpenType; +using ValueSubStorage = LibHac.Fs.ValueSubStorage; namespace LibHac.FsSrv.FsCreator; +public enum DeviceUniqueMacType +{ + Normal, + Temporary +} + +/// +/// Extends to allow initializing it with a +/// instead of directly using an . +/// +/// Based on nnSdk 17.5.0 (FS 17.0.0) +file class SaveDataInternalStorageFileSystem : LibHac.FsSystem.Save.SaveDataInternalStorageFileSystem +{ + private SharedRef _saveDataFileSystem; + + public SaveDataInternalStorageFileSystem() + { + _saveDataFileSystem = new SharedRef(); + } + + public override void Dispose() + { + _saveDataFileSystem.Destroy(); + base.Dispose(); + } + + public Result Initialize(ref readonly SharedRef saveDataFileSystem, + IMacGenerator normalMacGenerator, IMacGenerator temporaryMacGenerator, + IHash256GeneratorFactorySelector hashGeneratorFactorySelector) + { + Assert.SdkRequiresNotNull(in saveDataFileSystem); + Assert.SdkRequiresNotNull(normalMacGenerator); + Assert.SdkRequiresNotNull(temporaryMacGenerator); + Assert.SdkRequiresNotNull(hashGeneratorFactorySelector); + + _saveDataFileSystem.SetByCopy(in saveDataFileSystem); + + Result res = Initialize(_saveDataFileSystem.Get.GetInternalStorageFileSystem(), normalMacGenerator, + temporaryMacGenerator, hashGeneratorFactorySelector); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; + } +} + +file static class Anonymous +{ + public static Result GetSaveDataFormatType(out SaveDataFormatType outFormatType, IStorage saveImageStorage, + IBufferManager bufferManager, IMacGenerator macGenerator, + IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion) + { + UnsafeHelpers.SkipParamInit(out outFormatType); + + Result res = saveImageStorage.GetSize(out long storageSize); + if (res.IsFailure()) return res.Miss(); + + using var saveSubStorage = new ValueSubStorage(saveImageStorage, 0, storageSize); + + Result resultJournalIntegrity = JournalIntegritySaveDataFileSystem.VerifyMasterHeader(in saveSubStorage, + bufferManager, macGenerator, hashGeneratorFactorySelector, minimumVersion); + if (resultJournalIntegrity.IsSuccess()) + { + outFormatType = SaveDataFormatType.Normal; + return Result.Success; + } + + Result resultIntegrity = IntegritySaveDataFileSystem.VerifyMasterHeader(in saveSubStorage, bufferManager, + macGenerator, hashGeneratorFactorySelector); + if (resultIntegrity.IsSuccess()) + { + outFormatType = SaveDataFormatType.NoJournal; + return Result.Success; + } + + return resultJournalIntegrity.Miss(); + } +} + +/// +/// Used by for opening, formatting, and working with save data. +/// This class directly operates on save data s and the directories containing the save data, +/// whereas handles higher level tasks such as opening or creating +/// the save data images on the file system, managing caches, etc. +/// +/// Based on nnSdk 17.5.0 (FS 17.0.0) public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator { // Option to disable some restrictions enforced in actual FS. private static readonly bool EnforceSaveTypeRestrictions = false; + private class MacGenerationSeed + { + public const uint Size = Aes.KeySize128; + + public Array16 Value; + } + + private struct DeviceUniqueMacTypeNormal : IConstant + { + public static DeviceUniqueMacType Value => DeviceUniqueMacType.Normal; + } + + private struct DeviceUniqueMacTypeTemporary : IConstant + { + public static DeviceUniqueMacType Value => DeviceUniqueMacType.Temporary; + } + + private class DeviceUniqueMacGenerator : IMacGenerator where TMacType : IConstant + { + private GenerateDeviceUniqueMac _generatorFunction; + + public DeviceUniqueMacGenerator(GenerateDeviceUniqueMac generatorFunction) + { + _generatorFunction = generatorFunction; + } + + public Result Generate(Span macDestBuffer, ReadOnlySpan data) + { + return _generatorFunction(macDestBuffer, data, TMacType.Value).Ret(); + } + } + + private class SeedUniqueMacGenerator : IMacGenerator + { + private GenerateSeedUniqueMac _generatorFunction; + private MacGenerationSeed _seed; + + public SeedUniqueMacGenerator(GenerateSeedUniqueMac generatorFunction, MacGenerationSeed seed) + { + _generatorFunction = generatorFunction; + _seed = seed; + } + + public Result Generate(Span macDestBuffer, ReadOnlySpan data) + { + return _generatorFunction(macDestBuffer, data, _seed.Value).Ret(); + } + } + // ReSharper disable once NotAccessedField.Local private IBufferManager _bufferManager; - private RandomDataGenerator _randomGenerator; + private DeviceUniqueMacGenerator _deviceUniqueMacGeneratorNormal; + private DeviceUniqueMacGenerator _deviceUniqueMacGeneratorTemporary; + private SeedUniqueMacGenerator _seedUniqueMacGenerator; + private MacGenerationSeed _macGenerationSeed; + private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector; + private uint _saveDataMinimumVersion; + private RandomDataGenerator _randomDataGenerator; + private Func _debugValueGetter; // LibHac Additions - // ReSharper disable once NotAccessedField.Local - private KeySet _keySet; private FileSystemServer _fsServer; - public SaveDataFileSystemCreator(FileSystemServer fsServer, KeySet keySet, IBufferManager bufferManager, - RandomDataGenerator randomGenerator) + public SaveDataFileSystemCreator(FileSystemServer fsServer, IBufferManager bufferManager, + GenerateDeviceUniqueMac deviceUniqueMacGenerator, GenerateSeedUniqueMac seedUniqueMacGenerator, + RandomDataGenerator randomDataGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, + uint minimumVersion, Func debugValueGetter) + { + _fsServer = fsServer; + _macGenerationSeed = new MacGenerationSeed(); + + _bufferManager = bufferManager; + _deviceUniqueMacGeneratorNormal = new DeviceUniqueMacGenerator(deviceUniqueMacGenerator); + _deviceUniqueMacGeneratorTemporary = new DeviceUniqueMacGenerator(deviceUniqueMacGenerator); + _seedUniqueMacGenerator = new SeedUniqueMacGenerator(seedUniqueMacGenerator, _macGenerationSeed); + _hashGeneratorFactorySelector = hashGeneratorFactorySelector; + _saveDataMinimumVersion = minimumVersion; + _randomDataGenerator = randomDataGenerator; + _debugValueGetter = debugValueGetter; + + JournalIntegritySaveDataFileSystem.SetGenerateRandomFunction(fsServer, randomDataGenerator); + } + + public SaveDataFileSystemCreator(FileSystemServer fsServer, IBufferManager bufferManager, + RandomDataGenerator randomDataGenerator) { _bufferManager = bufferManager; - _randomGenerator = randomGenerator; + _randomDataGenerator = randomDataGenerator; _fsServer = fsServer; - _keySet = keySet; + } + + public void Dispose() { } + + private IMacGenerator GetDeviceUniqueMacGenerator(DeviceUniqueMacType macType) + { + switch (macType) + { + case DeviceUniqueMacType.Normal: + return _deviceUniqueMacGeneratorNormal; + case DeviceUniqueMacType.Temporary: + return _deviceUniqueMacGeneratorTemporary; + default: + Abort.UnexpectedDefault(); + return default; + } + } + + private IMacGenerator GetMacGenerator(bool isDeviceUnique, DeviceUniqueMacType macType) + { + return isDeviceUnique ? GetDeviceUniqueMacGenerator(macType) : _seedUniqueMacGenerator; } public Result Format(in ValueSubStorage saveImageStorage, long blockSize, int countExpandMax, uint blockCount, uint journalBlockCount, IBufferManager bufferManager, bool isDeviceUniqueMac, in HashSalt hashSalt, RandomDataGenerator encryptionKeyGenerator, bool isReconstructible, uint version) { - throw new NotImplementedException(); + Result res = JournalIntegritySaveDataFileSystemDriver.Format(in saveImageStorage, blockSize, blockCount, + journalBlockCount, countExpandMax, bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, hashSalt, + encryptionKeyGenerator, version); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; } public Result FormatAsIntegritySaveData(in ValueSubStorage saveImageStorage, long blockSize, uint blockCount, IBufferManager bufferManager, bool isDeviceUniqueMac, RandomDataGenerator encryptionKeyGenerator, bool isReconstructible, uint version) { - throw new NotImplementedException(); + Result res = IntegritySaveDataFileSystemDriver.Format(in saveImageStorage, blockSize, blockCount, bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + encryptionKeyGenerator, version); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; } public Result ExtractSaveDataParameters(out JournalIntegritySaveDataParameters outParams, IStorage saveFileStorage, bool isDeviceUniqueMac, bool isReconstructible) { - throw new NotImplementedException(); + Result res = SaveDataFileSystem.ExtractParameters(out outParams, saveFileStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + _saveDataMinimumVersion); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; } public Result ExtendSaveData(SaveDataExtender extender, in ValueSubStorage baseStorage, in ValueSubStorage logStorage, bool isDeviceUniqueMac, bool isReconstructible) { - throw new NotImplementedException(); + Assert.SdkRequiresNotNull(extender); + + Result res = extender.Extend(in baseStorage, in logStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + _saveDataMinimumVersion); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; } public void SetMacGenerationSeed(ReadOnlySpan seed) { - throw new NotImplementedException(); + Assert.SdkRequires(seed.Length == MacGenerationSeed.Size); + seed.CopyTo(_macGenerationSeed.Value); } public Result CreateRaw(ref SharedRef outFile, in SharedRef fileSystem, ulong saveDataId, OpenMode openMode) { - throw new NotImplementedException(); + Unsafe.SkipInit(out Array18 saveImageNameBuffer); + + using scoped var saveImageName = new Path(); + Result res = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer, saveDataId); + if (res.IsFailure()) return res.Miss(); + + res = fileSystem.Get.GetEntryType(out DirectoryEntryType type, in saveImageName); + + if (res.IsFailure()) + { + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); + } + + if (type == DirectoryEntryType.Directory) + { + return ResultFs.TargetNotFound.Log(); + } + + using var file = new UniqueRef(); + res = fileSystem.Get.OpenFile(ref file.Ref, in saveImageName, openMode); + if (res.IsFailure()) return res.Miss(); + + outFile.Set(ref file.Ref); + return Result.Success; } public Result Create(ref SharedRef outFileSystem, ref SharedRef baseFileSystem, @@ -87,7 +327,10 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator if (res.IsFailure()) { - return ResultFs.PathNotFound.Includes(res) ? ResultFs.TargetNotFound.LogConverted(res) : res.Miss(); + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); } using var saveDataFs = new SharedRef(); @@ -118,7 +361,7 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator return ResultFs.AllocationMemoryFailedInSaveDataFileSystemCreatorB.Log(); res = saveDirFs.Get.Initialize(isJournalingSupported, isMultiCommitSupported, !openReadOnly, - timeStampGetter, _randomGenerator); + timeStampGetter, _randomDataGenerator); if (res.IsFailure()) return res.Miss(); saveDataFs.SetByMove(ref saveDirFs.Ref); @@ -134,7 +377,37 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator OpenMode.ReadWrite, openType); if (res.IsFailure()) return res.Miss(); - throw new NotImplementedException(); + res = Anonymous.GetSaveDataFormatType(out SaveDataFormatType formatType, fileStorage.Get, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + _saveDataMinimumVersion); + if (res.IsFailure()) return res.Miss(); + + if (!SaveDataProperties.IsJournalingSupported(formatType)) + { + using var fs = new SharedRef(new ApplicationTemporaryFileSystem()); + res = fs.Get.Initialize(in fileStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + saveDataFs.SetByMove(ref fs.Ref); + } + else + { + using var fs = new SharedRef(new SaveDataFileSystem()); + if (!fs.HasValue) + return ResultFs.AllocationMemoryFailedInSaveDataFileSystemCreatorD.Log(); + + res = fs.Get.Initialize(in fileStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + timeStampGetter, _randomDataGenerator, _saveDataMinimumVersion, isMultiCommitSupported); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + saveDataFs.SetByMove(ref fs.Ref); + } } // Wrap the save FS in a result convert FS and set it as the output FS @@ -142,44 +415,234 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator new SaveDataResultConvertFileSystem(ref saveDataFs.Ref, isReconstructible)); outFileSystem.SetByMove(ref resultConvertFs.Ref); - return Result.Success; } public Result CreateExtraDataAccessor(ref SharedRef outExtraDataAccessor, in SharedRef baseStorage, bool isDeviceUniqueMac, bool isIntegritySaveData, bool isReconstructible) { - throw new NotImplementedException(); + using var saveDataFs = new SharedRef(); + + if (!isIntegritySaveData) + { + using var fs = new SharedRef(new ApplicationTemporaryFileSystem()); + Result res = fs.Get.Initialize(in baseStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + saveDataFs.SetByMove(ref fs.Ref); + } + else + { + using var fs = new SharedRef(new SaveDataFileSystem()); + Result res = fs.Get.Initialize(in baseStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + _saveDataMinimumVersion, canCommitProvisionally: false); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + saveDataFs.SetByMove(ref fs.Ref); + } + + using var resultConvertFs = new SharedRef( + new SaveDataResultConvertFileSystem(ref saveDataFs.Ref, isReconstructible)); + + outExtraDataAccessor.SetByMove(ref resultConvertFs.Ref); + return Result.Success; } public Result CreateInternalStorage(ref SharedRef outFileSystem, in SharedRef baseFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool isDeviceUniqueMac, bool useUniqueKey1, ISaveDataCommitTimeStampGetter timeStampGetter, bool isReconstructible) { - throw new NotImplementedException(); + Result res; + Unsafe.SkipInit(out Array18 saveImageNameBuffer); + + using (scoped var saveImageName = new Path()) + { + res = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer, saveDataId); + if (res.IsFailure()) return res.Miss(); + + res = baseFileSystem.Get.GetEntryType(out DirectoryEntryType entryType, in saveImageName); + + if (res.IsFailure()) + { + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); + } + + if (entryType == DirectoryEntryType.Directory) + return ResultFs.InvalidSaveDataEntryType.Log(); + } + + IMacGenerator normalMacGenerator = GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal); + IMacGenerator temporaryMacGenerator = GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Temporary); + IMacGenerator macGenerator = useUniqueKey1 ? temporaryMacGenerator : normalMacGenerator; + + using var fileStorage = new SharedRef(); + res = _fsServer.OpenSaveDataStorage(ref fileStorage.Ref, in baseFileSystem, spaceId, saveDataId, + OpenMode.ReadWrite, OpenType.Internal); + if (res.IsFailure()) return res.Miss(); + + using var saveFs = new SharedRef(new SaveDataFileSystem()); + res = saveFs.Get.Initialize(in fileStorage, _bufferManager, macGenerator, _hashGeneratorFactorySelector, + timeStampGetter, _randomDataGenerator, _saveDataMinimumVersion, canCommitProvisionally: false); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + using var internalStorage = new SharedRef(new SaveDataInternalStorageFileSystem()); + res = internalStorage.Get.Initialize(in saveFs, normalMacGenerator, temporaryMacGenerator, _hashGeneratorFactorySelector); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + using SharedRef tempSaveFs = SharedRef.CreateMove(ref saveFs.Ref); + using var resultConvertFs = new SharedRef( + new SaveDataResultConvertFileSystem(ref tempSaveFs.Ref, isReconstructible)); + + outFileSystem.SetByMove(ref resultConvertFs.Ref); + return Result.Success; } public Result RecoverMasterHeader(in SharedRef baseFileSystem, ulong saveDataId, IBufferManager bufferManager, bool isDeviceUniqueMac, bool isReconstructible) { - throw new NotImplementedException(); + Unsafe.SkipInit(out Array18 saveImageNameBuffer); + + using scoped var saveImageName = new Path(); + Result res = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer, saveDataId); + if (res.IsFailure()) return res.Miss(); + + res = baseFileSystem.Get.GetEntryType(out DirectoryEntryType entryType, in saveImageName); + + if (res.IsFailure()) + { + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); + } + + if (entryType == DirectoryEntryType.Directory) + { + // Directory save data doesn't have a master header + return Result.Success; + } + + using var fileStorage = new SharedRef(); + res = fileStorage.Get.Initialize(in baseFileSystem, in saveImageName, OpenMode.ReadWrite); + if (res.IsFailure()) return res.Miss(); + + res = fileStorage.Get.GetSize(out long size); + if (res.IsFailure()) return res.Miss(); + + using var fileSubStorage = new ValueSubStorage(fileStorage.Get, 0, size); + IMacGenerator macGenerator = GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal); + + res = JournalIntegritySaveDataFileSystem.RecoverMasterHeader(in fileSubStorage, bufferManager, macGenerator, + _hashGeneratorFactorySelector, _saveDataMinimumVersion); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; } public Result UpdateMac(in SharedRef baseFileSystem, ulong saveDataId, bool isDeviceUniqueMac, bool isReconstructible) { - throw new NotImplementedException(); + Unsafe.SkipInit(out Array18 saveImageNameBuffer); + + using scoped var saveImageName = new Path(); + Result res = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer, saveDataId); + if (res.IsFailure()) return res.Miss(); + + res = baseFileSystem.Get.GetEntryType(out DirectoryEntryType entryType, in saveImageName); + + if (res.IsFailure()) + { + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); + } + + if (entryType == DirectoryEntryType.Directory) + { + // Directory save data doesn't contain a MAC + return Result.Success; + } + + using var fileStorage = new SharedRef(); + res = fileStorage.Get.Initialize(in baseFileSystem, in saveImageName, OpenMode.ReadWrite); + if (res.IsFailure()) return res.Miss(); + + res = fileStorage.Get.GetSize(out long size); + if (res.IsFailure()) return res.Miss(); + + using var fileSubStorage = new ValueSubStorage(fileStorage.Get, 0, size); + IMacGenerator macGenerator = GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal); + + res = JournalIntegritySaveDataFileSystem.UpdateMac(in fileSubStorage, macGenerator, + _hashGeneratorFactorySelector, _saveDataMinimumVersion); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + return Result.Success; } public Result IsProvisionallyCommittedSaveData(out bool outIsProvisionallyCommitted, in SharedRef baseFileSystem, in SaveDataInfo info, bool isDeviceUniqueMac, ISaveDataCommitTimeStampGetter timeStampGetter, bool isReconstructible) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out outIsProvisionallyCommitted); + Unsafe.SkipInit(out Array18 saveImageNameBuffer); + + using scoped var saveImageName = new Path(); + Result res = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer, info.SaveDataId); + if (res.IsFailure()) return res.Miss(); + + res = baseFileSystem.Get.GetEntryType(out DirectoryEntryType entryType, in saveImageName); + + if (res.IsFailure()) + { + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); + } + + if (entryType == DirectoryEntryType.Directory) + return ResultFs.InvalidSaveDataEntryType.Log(); + + using var fileStorage = new SharedRef(); + res = _fsServer.OpenSaveDataStorage(ref fileStorage.Ref, in baseFileSystem, info.SpaceId, info.SaveDataId, + OpenMode.ReadWrite, OpenType.Internal); + if (res.IsFailure()) return res.Miss(); + + using var saveFs = new SharedRef(new SaveDataFileSystem()); + + res = saveFs.Get.Initialize(in fileStorage, _bufferManager, + GetMacGenerator(isDeviceUniqueMac, DeviceUniqueMacType.Normal), _hashGeneratorFactorySelector, + timeStampGetter, _randomDataGenerator, _saveDataMinimumVersion, canCommitProvisionally: false); + + res = SaveDataResultConverter.ConvertSaveDataFsResult(res, isReconstructible); + if (res.IsFailure()) return res.Miss(); + + outIsProvisionallyCommitted = saveFs.Get.GetCounterForBundledCommit() != 0; + return Result.Success; } public IMacGenerator GetMacGenerator(bool isDeviceUniqueMac, bool isTemporaryTransferSave) { - throw new NotImplementedException(); + DeviceUniqueMacType macType = isTemporaryTransferSave ? DeviceUniqueMacType.Temporary : DeviceUniqueMacType.Normal; + return GetMacGenerator(isDeviceUniqueMac, macType); } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs b/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs index cdebe6c9..c486a2f2 100644 --- a/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs +++ b/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs @@ -18,12 +18,12 @@ namespace LibHac.FsSrv; public static class SaveDataSharedFileStorageGlobalMethods { public static Result OpenSaveDataStorage(this FileSystemServer fsSrv, - ref SharedRef outSaveDataStorage, ref SharedRef baseFileSystem, + ref SharedRef outSaveDataStorage, ref readonly SharedRef baseFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, OpenMode mode, Optional type) { return fsSrv.Globals.SaveDataSharedFileStorage.SaveDataFileStorageHolder.OpenSaveDataStorage( - ref outSaveDataStorage, ref baseFileSystem, spaceId, saveDataId, mode, type); + ref outSaveDataStorage, in baseFileSystem, spaceId, saveDataId, mode, type); } } @@ -74,9 +74,9 @@ public class SaveDataOpenTypeSetFileStorage : FileStorageBasedFileSystem _mutex = new SdkMutexType(); } - public Result Initialize(ref SharedRef baseFileSystem, ref readonly Path path, OpenMode mode, OpenType type) + public Result Initialize(ref readonly SharedRef baseFileSystem, ref readonly Path path, OpenMode mode, OpenType type) { - Result res = Initialize(ref baseFileSystem, in path, mode); + Result res = Initialize(in baseFileSystem, in path, mode); if (res.IsFailure()) return res.Miss(); return SetOpenType(type); @@ -331,7 +331,7 @@ public class SaveDataFileStorageHolder } public Result OpenSaveDataStorage(ref SharedRef outSaveDataStorage, - ref SharedRef baseFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, OpenMode mode, + ref readonly SharedRef baseFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, OpenMode mode, Optional type) { Unsafe.SkipInit(out Array18 saveImageNameBuffer); @@ -344,7 +344,7 @@ public class SaveDataFileStorageHolder if (!type.HasValue) { using var fileStorage = new SharedRef(new FileStorageBasedFileSystem()); - res = fileStorage.Get.Initialize(ref baseFileSystem, in saveImageName, mode); + res = fileStorage.Get.Initialize(in baseFileSystem, in saveImageName, mode); if (res.IsFailure()) return res.Miss(); outSaveDataStorage.SetByMove(ref fileStorage.Ref); @@ -363,7 +363,7 @@ public class SaveDataFileStorageHolder else { baseFileStorage.Reset(new SaveDataOpenTypeSetFileStorage(_fsServer, spaceId, saveDataId)); - res = baseFileStorage.Get.Initialize(ref baseFileSystem, in saveImageName, mode, type.ValueRo); + res = baseFileStorage.Get.Initialize(in baseFileSystem, in saveImageName, mode, type.ValueRo); if (res.IsFailure()) return res.Miss(); using SharedRef baseFileStorageCopy = diff --git a/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs b/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs index 11254455..0629c0f1 100644 --- a/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs +++ b/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs @@ -54,7 +54,7 @@ file class ApplicationTemporaryFile : IFile } } -public class ApplicationTemporaryFileSystem : IFileSystem, ICacheableSaveDataFileSystem, ISaveDataExtraDataAccessor +public class ApplicationTemporaryFileSystem : ISaveDataFileSystem, ISaveDataExtraDataAccessor { private SharedRef _baseStorage; private IntegritySaveDataFileSystemDriver _saveFsDriver; @@ -166,12 +166,12 @@ public class ApplicationTemporaryFileSystem : IFileSystem, ICacheableSaveDataFil throw new NotImplementedException(); } - public bool IsSaveDataFileSystemCacheEnabled() + public override bool IsSaveDataFileSystemCacheEnabled() { throw new NotImplementedException(); } - public Result RollbackOnlyModified() + public override Result RollbackOnlyModified() { throw new NotImplementedException(); } @@ -181,22 +181,22 @@ public class ApplicationTemporaryFileSystem : IFileSystem, ICacheableSaveDataFil throw new NotImplementedException(); } - public Result WriteExtraData(in SaveDataExtraData extraData) + public override Result WriteExtraData(in SaveDataExtraData extraData) { throw new NotImplementedException(); } - public Result CommitExtraData(bool updateTimeStamp) + public override Result CommitExtraData(bool updateTimeStamp) { throw new NotImplementedException(); } - public Result ReadExtraData(out SaveDataExtraData extraData) + public override Result ReadExtraData(out SaveDataExtraData extraData) { throw new NotImplementedException(); } - public void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, + public override void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId) { throw new NotImplementedException(); diff --git a/src/LibHac/FsSystem/Save/JournalIntegritySaveDataFileSystem.cs b/src/LibHac/FsSystem/Save/JournalIntegritySaveDataFileSystem.cs index 7445279c..d9ca704e 100644 --- a/src/LibHac/FsSystem/Save/JournalIntegritySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/Save/JournalIntegritySaveDataFileSystem.cs @@ -322,7 +322,7 @@ public class JournalIntegritySaveDataFileSystem : IFileSystem throw new NotImplementedException(); } - public static void SetGenerateRandomFunction(RandomDataGenerator func) + public static void SetGenerateRandomFunction(FileSystemServer fsServer, RandomDataGenerator func) { throw new NotImplementedException(); } @@ -479,7 +479,7 @@ public class JournalIntegritySaveDataFileSystem : IFileSystem throw new NotImplementedException(); } - private Result MountSaveData(in FileSystemLayoutHeader layoutHeader, in SubStorage saveImageStorage, long blockSize, + private Result MountSaveData(in FileSystemLayoutHeader layoutHeader, in ValueSubStorage saveImageStorage, long blockSize, FileSystemBufferManagerSet integrityCacheBufferSet, IBufferManager bufferManager, SdkRecursiveMutex mutex) { throw new NotImplementedException(); @@ -497,7 +497,7 @@ public class JournalIntegritySaveDataFileSystem : IFileSystem } public Result Initialize( - in SubStorage saveImageStorage, + in ValueSubStorage saveImageStorage, FileSystemBufferManagerSet integrityCacheBufferSet, IBufferManager duplicateCacheBuffer, SdkRecursiveMutex mutex, @@ -518,13 +518,13 @@ public class JournalIntegritySaveDataFileSystem : IFileSystem throw new NotImplementedException(); } - public static Result UpdateMac(in SubStorage saveImageStorage, IMacGenerator macGenerator, + public static Result UpdateMac(in ValueSubStorage saveImageStorage, IMacGenerator macGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion) { throw new NotImplementedException(); } - public static Result RecoverMasterHeader(in SubStorage saveImageStorage, IBufferManager bufferManager, + public static Result RecoverMasterHeader(in ValueSubStorage saveImageStorage, IBufferManager bufferManager, IMacGenerator macGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion) { throw new NotImplementedException(); diff --git a/src/LibHac/FsSystem/SaveDataFileSystem.cs b/src/LibHac/FsSystem/SaveDataFileSystem.cs index b019ca10..ea9988d9 100644 --- a/src/LibHac/FsSystem/SaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/SaveDataFileSystem.cs @@ -177,6 +177,11 @@ public class SaveDataFileSystem : ISaveDataFileSystem, InternalStorageFileSystem throw new NotImplementedException(); } + public long GetCounterForBundledCommit() + { + throw new NotImplementedException(); + } + protected override Result DoCommitProvisionally(long counter) { throw new NotImplementedException(); diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataResultConvertFileSystem.cs b/src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs similarity index 97% rename from src/LibHac/FsSrv/FsCreator/SaveDataResultConvertFileSystem.cs rename to src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs index 2c853ae2..12c3f159 100644 --- a/src/LibHac/FsSrv/FsCreator/SaveDataResultConvertFileSystem.cs +++ b/src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs @@ -1,10 +1,9 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using static LibHac.FsSrv.FsCreator.SaveDataResultConverter; +using static LibHac.FsSystem.SaveDataResultConverter; -namespace LibHac.FsSrv.FsCreator; +namespace LibHac.FsSystem; /// /// Wraps an , converting its returned s diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataResultConverter.cs b/src/LibHac/FsSystem/SaveDataResultConverter.cs similarity index 99% rename from src/LibHac/FsSrv/FsCreator/SaveDataResultConverter.cs rename to src/LibHac/FsSystem/SaveDataResultConverter.cs index 53b7a524..50441e31 100644 --- a/src/LibHac/FsSrv/FsCreator/SaveDataResultConverter.cs +++ b/src/LibHac/FsSystem/SaveDataResultConverter.cs @@ -1,7 +1,7 @@ using LibHac.Diag; using LibHac.Fs; -namespace LibHac.FsSrv.FsCreator; +namespace LibHac.FsSystem; /// /// Contains functions for converting internal save data s to external s.