Implement SaveDataFileSystemService functions

This commit is contained in:
Alex Barney 2024-01-27 21:25:40 -07:00
parent beca253086
commit 78e16d3d61
12 changed files with 981 additions and 188 deletions

View file

@ -0,0 +1,11 @@
using System;
namespace LibHac.Fs.Save;
public static class InternalStorageFileNames
{
public static ReadOnlySpan<byte> InternalStorageFileNameAllocationTableMeta => "AllocationTableMeta"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameRawSaveData => "RawSaveData"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameRawSaveDataWithZeroFree => "RawSaveDataWithZeroFree"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameSaveDataControlArea => "SaveDataControlArea"u8;
}

View file

@ -1,11 +1,26 @@
using System;
using LibHac.Common;
namespace LibHac.FsSrv;
public delegate Result SaveTransferAesKeyGenerator(Span<byte> key,
public delegate Result SaveTransferAesKeyGenerator(Span<byte> outKeyBuffer,
SaveDataTransferCryptoConfiguration.KeyIndex index, ReadOnlySpan<byte> keySource, int keyGeneration);
public delegate Result SaveTransferCmacGenerator(Span<byte> mac, ReadOnlySpan<byte> data,
SaveDataTransferCryptoConfiguration.KeyIndex index, int keyGeneration);
public delegate Result SaveTransferCmacGenerator(Span<byte> outMacBuffer, ReadOnlySpan<byte> data,
SaveDataTransferCryptoConfiguration.Attributes attribute, SaveDataTransferCryptoConfiguration.KeyIndex index,
int keyGeneration);
public delegate Result SaveTransferOpenDecryptor(
ref SharedRef<SaveDataTransferCryptoConfiguration.IDecryptor> outDecryptor,
SaveDataTransferCryptoConfiguration.Attributes attribute, SaveDataTransferCryptoConfiguration.KeyIndex keyIndex,
ReadOnlySpan<byte> keySource, int keyGeneration, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> mac);
public delegate Result SaveTransferOpenEncryptor(
ref SharedRef<SaveDataTransferCryptoConfiguration.IEncryptor> outEncryptor,
SaveDataTransferCryptoConfiguration.Attributes attribute, SaveDataTransferCryptoConfiguration.KeyIndex keyIndex,
ReadOnlySpan<byte> keySource, int keyGeneration, ReadOnlySpan<byte> iv);
public delegate bool VerifyRsaSignature(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> modulus,
ReadOnlySpan<byte> exponent, ReadOnlySpan<byte> data);
public delegate Result PatrolAllocateCountGetter(out long successCount, out long failureCount);

View file

@ -11,7 +11,7 @@ public interface ISaveDataTransferCoreInterface : IDisposable
{
Result GetFreeSpaceSizeForSaveData(out long outFreeSpaceSize, SaveDataSpaceId spaceId);
Result QuerySaveDataTotalSize(out long outTotalSize, long dataSize, long journalSize);
Result CheckSaveDataFile(long saveDataId, SaveDataSpaceId spaceId);
Result CheckSaveDataFile(ulong saveDataId, SaveDataSpaceId spaceId);
Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt, bool leaveUnfinalized);
Result GetSaveDataInfo(out SaveDataInfo saveInfo, SaveDataSpaceId spaceId, SaveDataAttribute attribute);
Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData, SaveDataSpaceId spaceId, ulong saveDataId, bool isTemporarySaveData);

View file

@ -38,7 +38,7 @@ public class SaveDataMover : ISaveDataMover
private State _state;
private SdkMutex _mutex;
public SaveDataMover(SharedRef<ISaveDataTransferCoreInterface> transferInterface, SaveDataSpaceId sourceSpaceId,
public SaveDataMover(in SharedRef<ISaveDataTransferCoreInterface> transferInterface, SaveDataSpaceId sourceSpaceId,
SaveDataSpaceId destinationSpaceId, NativeHandle transferMemoryHandle, ulong transferMemorySize)
{
throw new NotImplementedException();

View file

@ -113,8 +113,15 @@ public static class SaveDataProperties
}
}
public static bool IsObsoleteSystemSaveData(in SaveDataInfo info)
{
return false;
throw new NotImplementedException();
}
public static bool IsWipingNeededAtCleanUp(in SaveDataInfo info)
{
return false;
throw new NotImplementedException();
}

View file

@ -10,7 +10,7 @@ using IFile = LibHac.Fs.Fsa.IFile;
namespace LibHac.FsSrv.Impl;
public class SaveDataTransferManager : IDisposable
public class SaveDataTransferManager : ISaveDataTransferManager
{
private SharedRef<ISaveDataTransferCoreInterface> _transferCoreInterface;
private SaveDataTransferCryptoConfiguration _cryptoConfig;

View file

@ -182,13 +182,19 @@ public class SaveDataTransferManagerForSaveDataRepair<TPolicy> : ISaveDataTransf
private Optional<AesKey> _kek;
private SaveDataTransferManagerForSaveDataRepair.KeyPackageV0.Content _keyPackage;
private SaveDataPorterManager _porterManager;
private bool _canOpenTool;
private bool _canOpenPorterWithKey;
public SaveDataTransferManagerForSaveDataRepair(SaveDataTransferCryptoConfiguration cryptoConfig,
ref readonly SharedRef<ISaveDataTransferCoreInterface> coreInterface, SaveDataPorterManager porterManager,
bool canOpenTool)
bool canOpenPorterWithKey)
{
throw new NotImplementedException();
_transferInterface = SharedRef<ISaveDataTransferCoreInterface>.CreateCopy(in coreInterface);
_cryptoConfig = cryptoConfig;
_isKeyPackageSet = false;
_kek = new Optional<AesKey>();
_porterManager = porterManager;
_canOpenPorterWithKey = canOpenPorterWithKey;
_cryptoConfig.GenerateRandomData(_challengeData.Value);
}
public void Dispose()

View file

@ -2,6 +2,7 @@
#pragma warning disable CS0169 // Field is never used
using System;
using System.Collections.Generic;
using LibHac.FsSrv.Sf;
using LibHac.Os;
namespace LibHac.FsSrv.Impl;
@ -36,7 +37,7 @@ public abstract class Prohibitee : IDisposable
public abstract ApplicationId GetApplicationId();
}
public class SaveDataPorterProhibiter : IDisposable
public class SaveDataPorterProhibiter : ISaveDataTransferProhibiter
{
// IntrusiveList
private SaveDataPorterManager _porterManager;

File diff suppressed because it is too large Load diff

View file

@ -12,10 +12,33 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Os;
using LibHac.Util;
using static LibHac.FsSrv.Anonymous;
using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv;
file static class Anonymous
{
public static bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
{
return spaceId == SaveDataSpaceId.System ||
spaceId == SaveDataSpaceId.User ||
spaceId == SaveDataSpaceId.Temporary ||
spaceId == SaveDataSpaceId.ProperSystem ||
spaceId == SaveDataSpaceId.SafeMode;
}
public static Result WipeData(IFileSystem fileSystem, ref readonly Path filePath, RandomDataGenerator random)
{
throw new NotImplementedException();
}
public static Result WipeMasterHeader(IFileSystem fileSystem, ref readonly Path filePath, RandomDataGenerator random)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Handles the lower-level operations on save data.
/// <see cref="SaveDataFileSystemService"/> uses this class to provide save data APIs at a higher level of abstraction.
@ -30,8 +53,10 @@ public class SaveDataFileSystemServiceImpl : IDisposable
private SaveDataFileSystemCacheManager _saveFileSystemCacheManager;
private SaveDataExtraDataAccessorCacheManager _saveExtraDataCacheManager;
// Save data porter manager
private SaveDataPorterManager _saveDataPorterManager;
private bool _isSdCardAccessible;
private Optional<uint> _integritySaveDataVersion;
private Optional<uint> _journalIntegritySaveDataVersion;
private TimeStampGetter _timeStampGetter;
internal HorizonClient Hos => _config.FsServer.Hos;
@ -69,29 +94,17 @@ public class SaveDataFileSystemServiceImpl : IDisposable
public ISaveDataIndexerManager SaveIndexerManager;
public DebugConfigurationServiceImpl DebugConfigService;
public uint JournalIntegritySaveDataVersion;
public uint JournalIntegritySupportedVersionMin;
public uint JournalIntegritySupportedVersionMax;
public uint IntegritySaveDataVersion;
public uint IntegritySupportedVersionMin;
public uint IntegritySupportedVersionMax;
// LibHac additions
public FileSystemServer FsServer;
}
private static bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
{
return spaceId == SaveDataSpaceId.System ||
spaceId == SaveDataSpaceId.User ||
spaceId == SaveDataSpaceId.Temporary ||
spaceId == SaveDataSpaceId.ProperSystem ||
spaceId == SaveDataSpaceId.SafeMode;
}
private static Result WipeData(IFileSystem fileSystem, ref readonly Path filePath, RandomDataGenerator random)
{
throw new NotImplementedException();
}
private static Result WipeMasterHeader(IFileSystem fileSystem, ref readonly Path filePath, RandomDataGenerator random)
{
throw new NotImplementedException();
}
public SaveDataFileSystemServiceImpl(in Configuration configuration)
{
_config = configuration;
@ -115,6 +128,32 @@ public class SaveDataFileSystemServiceImpl : IDisposable
return _config.DebugConfigService;
}
public void SetIntegritySaveDataVersion(uint version)
{
_integritySaveDataVersion.Set(version);
}
public void SetJournalIntegritySaveDataVersion(uint version)
{
_journalIntegritySaveDataVersion.Set(version);
}
public uint GetIntegritySaveDataVersion()
{
if (_integritySaveDataVersion.HasValue)
return _integritySaveDataVersion.Value;
return _config.IntegritySaveDataVersion;
}
public uint GetJournalIntegritySaveDataVersion()
{
if (_journalIntegritySaveDataVersion.HasValue)
return _journalIntegritySaveDataVersion.Value;
return _config.JournalIntegritySaveDataVersion;
}
public Result DoesSaveDataEntityExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId)
{
UnsafeHelpers.SkipParamInit(out exists);
@ -149,6 +188,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable
}
}
private Result GetSaveDataCommitTimeStamp(out long timeStamp)
{
return _config.TimeService.GetCurrentPosixTime(out timeStamp);
}
public Result OpenSaveDataFile(ref SharedRef<IFile> outFile, SaveDataSpaceId spaceId, ulong saveDataId,
OpenMode openMode)
{
@ -237,7 +281,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
saveDataMetaIdDirectoryNameBuffer, saveDataId);
if (res.IsFailure()) return res.Miss();
return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in saveDataMetaIdDirectoryName).Ret();
return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, saveDataId, in saveDataMetaIdDirectoryName).Ret();
}
public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef<IFileSystem> outFileSystem,
@ -253,8 +297,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable
throw new NotImplementedException();
}
public Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId,
SaveDataType type, long dataSize, long journalSize, ref readonly Path saveDataRootPath, bool isExtensionStart)
private Result OpenSaveDataExtensionContextFile(ref UniqueRef<IFile> outFile, IFile saveDataFile, ulong saveDataId,
SaveDataSpaceId spaceId, long dataSize, long journalSize, bool createIfMissing, bool isReconstructible)
{
throw new NotImplementedException();
}
@ -273,6 +317,12 @@ public class SaveDataFileSystemServiceImpl : IDisposable
journalSize: 0, in saveDataRootPath, isExtensionStart: false);
}
private Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId,
SaveDataType type, long dataSize, long journalSize, ref readonly Path saveDataRootPath, bool isExtensionStart)
{
throw new NotImplementedException();
}
public Result FinishExtendSaveDataFileSystem(ulong saveDataId, SaveDataSpaceId spaceId)
{
Result res = DeleteSaveDataMeta(saveDataId, spaceId, SaveDataMetaType.ExtensionContext);
@ -296,13 +346,6 @@ public class SaveDataFileSystemServiceImpl : IDisposable
FinishExtendSaveDataFileSystem(saveDataId, spaceId).IgnoreResult();
}
public Result QuerySaveDataTotalSize(out long totalSize, int blockSize, long dataSize, long journalSize)
{
// Todo: Implement
totalSize = 0;
return Result.Success;
}
public Result CreateSaveDataMeta(ulong saveDataId, SaveDataSpaceId spaceId, SaveDataMetaType metaType,
long metaFileSize)
{
@ -356,8 +399,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
Result res = PathFunctions.SetUpFixedPath(ref saveDataMetaDirectoryName.Ref(), metaDirName);
if (res.IsFailure()) return res.Miss();
res = OpenSaveDataDirectoryFileSystemImpl(ref fileSystem.Ref, spaceId, in saveDataMetaDirectoryName,
createIfMissing: false);
res = OpenSaveDataDirectoryFileSystemImpl(ref fileSystem.Ref, spaceId, saveDataId, in saveDataMetaDirectoryName, createIfMissing: false);
if (res.IsFailure()) return res.Miss();
using scoped var saveDataIdDirectoryName = new Path();
@ -400,6 +442,13 @@ public class SaveDataFileSystemServiceImpl : IDisposable
return Result.Success;
}
public Result QuerySaveDataTotalSize(out long totalSize, long blockSize, long dataSize, long journalSize)
{
// Todo: Implement
totalSize = 0;
return Result.Success;
}
public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataCreationInfo2 creationInfo,
ref readonly Path saveDataRootPath, bool skipFormat)
{
@ -631,9 +680,14 @@ public class SaveDataFileSystemServiceImpl : IDisposable
throw new NotImplementedException();
}
private Result GetSaveDataCommitTimeStamp(out long timeStamp)
public Result RecoverSaveDataFileSystemMasterHeader(SaveDataSpaceId spaceId, ulong saveDataId)
{
return _config.TimeService.GetCurrentPosixTime(out timeStamp);
throw new NotImplementedException();
}
public Result UpdateSaveDataFileSystemMac(SaveDataSpaceId spaceId, ulong saveDataId)
{
throw new NotImplementedException();
}
private bool IsSaveEmulated(ref readonly Path saveDataRootPath)
@ -641,10 +695,10 @@ public class SaveDataFileSystemServiceImpl : IDisposable
return !saveDataRootPath.IsEmpty();
}
public Result OpenSaveDataDirectoryFileSystem(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, ulong saveDataId)
public Result OpenSaveDataDirectoryFileSystem(ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId,
ulong saveDataId)
{
using scoped var rootPath = new Path();
using var rootPath = new Path();
return OpenSaveDataDirectoryFileSystem(ref outFileSystem, spaceId, saveDataId, in rootPath, allowEmulatedSave: true);
}
@ -703,20 +757,14 @@ public class SaveDataFileSystemServiceImpl : IDisposable
res = PathFunctions.SetUpFixedPath(ref saveDataAreaDirectoryName.Ref(), saveDirName);
if (res.IsFailure()) return res.Miss();
res = OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in saveDataAreaDirectoryName);
res = OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, saveDataId, in saveDataAreaDirectoryName);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, ref readonly Path directoryPath)
{
return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in directoryPath, createIfMissing: true);
}
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, ref readonly Path directoryPath, bool createIfMissing)
private Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId,
ulong saveDataId, ref readonly Path directoryPath, bool createIfMissing)
{
using var baseFileSystem = new SharedRef<IFileSystem>();
@ -812,6 +860,12 @@ public class SaveDataFileSystemServiceImpl : IDisposable
return Result.Success;
}
private Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId,
ulong saveDataId, ref readonly Path directoryPath)
{
return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, saveDataId, in directoryPath, createIfMissing: true);
}
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
{
throw new NotImplementedException();
@ -881,6 +935,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable
throw new NotImplementedException();
}
public SaveDataPorterManager GetSaveDataPorterManager()
{
return _saveDataPorterManager;
}
public Result GetSaveDataIndexCount(out int count)
{
UnsafeHelpers.SkipParamInit(out count);

View file

@ -17,9 +17,13 @@ public class SaveDataTransferCryptoConfiguration
public Span<byte> KekEncryptionKeyModulus => _kekEncryptionKeyModulus;
public Span<byte> KeyPackageSigningModulus => _keyPackageSigningModulus;
public SaveTransferAesKeyGenerator GenerateAesKey { get; set; }
public RandomDataGenerator GenerateRandomData { get; set; }
public SaveTransferCmacGenerator GenerateCmac { get; set; }
public SaveTransferOpenDecryptor OpenDecryptor { get; set; }
public SaveTransferOpenEncryptor OpenEncryptor { get; set; }
public VerifyRsaSignature VerifySignature { get; set; }
public Action ResetConfiguration { get; set; }
public SaveTransferAesKeyGenerator GenerateAesKey { get; set; }
public enum KeyIndex
{
@ -34,7 +38,10 @@ public class SaveDataTransferCryptoConfiguration
SaveDataRepairInitialDataMacAfterRepair
}
public enum Attributes { }
public enum Attributes
{
Default = 0
}
public interface IEncryptor : IDisposable
{

View file

@ -0,0 +1,18 @@
using System;
namespace LibHac.FsSystem.Save;
public static class InternalStorageFileNames
{
public static ReadOnlySpan<byte> InternalStorageFileNameIntegrity => "InternalStorageFileNameIntegrity"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegrityExtraData => "InternalStorageFileNameIntegrityExtraData"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegrityHashAlgorithm => "InternalStorageFileNameHashAlgorithm"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegritySeed => "InternalStorageFileNameIntegritySeed"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegritySeedEnabled => "InternalStorageFileNameIntegrity"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegritySha2Salt => "InternalStorageFileNameIntegritySha2Salt"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegritySha2SaltWithZeroFree => "InternalStorageFileNameIntegritySha2SaltWithZeroFree"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameIntegrityWithZeroFree => "InternalStorageFileNameIntegrityWithZeroFree"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameJournal => "InternalStorageFileNameJournal"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameMasterHeaderMac => "MasterHeaderMac"u8;
public static ReadOnlySpan<byte> InternalStorageFileNameSha2Salt => "InternalStorageFileNameSha2Salt"u8;
}