Implement SaveDataExtender

This commit is contained in:
Alex Barney 2024-02-19 23:04:19 -07:00
parent 164382f998
commit 2501cd24d0
4 changed files with 123 additions and 56 deletions

View file

@ -1,16 +1,25 @@
// ReSharper disable UnusedMember.Local UnusedType.Local using System.Runtime.CompilerServices;
#pragma warning disable CS0169 // Field is never used using System.Runtime.InteropServices;
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value using LibHac.Common;
using System; using LibHac.Diag;
using System.Runtime.CompilerServices;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.FsSystem.Save; using LibHac.FsSystem.Save;
using LibHac.Util;
namespace LibHac.FsSrv.Impl; namespace LibHac.FsSrv.Impl;
/// <summary>
/// Extends a save data image to a larger size. Keeps track of the extension progress by writing information about
/// the extension to an "extension context" storage.
/// </summary>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataExtender public class SaveDataExtender
{ {
private const uint MagicCode = 0x43545845; // EXTC
private const uint Version1 = 0x10000;
private const uint Version2 = 0x20000;
private enum State private enum State
{ {
Initial = 1, Initial = 1,
@ -18,6 +27,7 @@ public class SaveDataExtender
Committed = 3 Committed = 3
} }
[StructLayout(LayoutKind.Sequential)]
private struct Context private struct Context
{ {
public uint Magic; public uint Magic;
@ -36,62 +46,119 @@ public class SaveDataExtender
public SaveDataExtender() public SaveDataExtender()
{ {
throw new NotImplementedException(); _contextStorage = null;
} }
public long GetLogSize() => JournalIntegritySaveDataFileSystemDriver.QueryExpandLogSize(_context.BlockSize,
GetJournalBlockCount(), GetAvailableBlockCount());
public long GetAvailableSize() => _context.AvailableSize;
public long GetJournalSize() => _context.JournalSize;
public long GetExtendedSaveDataSize() => _context.ExtendedSaveDataSize;
private uint GetAvailableBlockCount() => (uint)BitUtil.DivideUp(_context.AvailableSize, _context.BlockSize);
private uint GetJournalBlockCount() => (uint)BitUtil.DivideUp(_context.JournalSize, _context.BlockSize);
public Result InitializeContext(in JournalIntegritySaveDataParameters param, long sizeAvailable, long sizeReserved) public Result InitializeContext(in JournalIntegritySaveDataParameters param, long sizeAvailable, long sizeReserved)
{ {
throw new NotImplementedException(); _context.Magic = MagicCode;
_context.Version = Version2;
_context.State = State.Initial;
_context.AvailableSize = sizeAvailable;
_context.JournalSize = sizeReserved;
_context.BlockSize = param.BlockSize;
Result res = JournalIntegritySaveDataFileSystemDriver.QueryTotalSize(out _context.ExtendedSaveDataSize,
param.BlockSize, GetAvailableBlockCount(), GetJournalBlockCount(), param.CountExpandMax, param.Version);
if (res.IsFailure()) return res.Miss();
return Result.Success;
} }
public Result WriteContext(IStorage contextStorage) public Result WriteContext(IStorage contextStorage)
{ {
throw new NotImplementedException(); Result res = contextStorage.Write(0, SpanHelpers.AsReadOnlyByteSpan(in _context));
if (res.IsFailure()) return res.Miss();
res = contextStorage.Flush();
if (res.IsFailure()) return res.Miss();
return Result.Success;
} }
public Result ReadContext(IStorage contextStorage) public Result ReadContext(IStorage contextStorage)
{ {
throw new NotImplementedException(); Assert.SdkRequiresNull(_contextStorage);
} Assert.SdkRequiresNotNull(contextStorage);
public long GetLogSize() Result res = contextStorage.Read(0, SpanHelpers.AsByteSpan(ref _context));
{ if (res.IsFailure()) return res.Miss();
throw new NotImplementedException();
}
public long GetAvailableSize() if (_context.Magic != MagicCode)
{ return ResultFs.IncorrectSaveDataExtensionContextMagicCode.Log();
throw new NotImplementedException();
}
public long GetJournalSize() if (_context.Version == Version1)
{ {
throw new NotImplementedException(); UpdateContextV1ToV2(ref _context);
} }
public long GetExtendedSaveDataSize() if (_context.Version != Version2)
{ return ResultFs.UnsupportedSaveDataVersion.Log();
throw new NotImplementedException();
}
private uint GetAvailableBlockCount() State state = _context.State;
{ if (state != State.Initial && state != State.Extended && state != State.Committed)
throw new NotImplementedException(); return ResultFs.InvalidSaveDataExtensionContextState.Log();
}
private uint GetJournalBlockCount() if (_context.BlockSize <= 0)
{ return ResultFs.InvalidSaveDataExtensionContextParameter.Log();
throw new NotImplementedException();
if (_context.ExtendedSaveDataSize <= 0)
return ResultFs.InvalidSaveDataExtensionContextParameter.Log();
_contextStorage = contextStorage;
return Result.Success;
} }
public Result Extend(in ValueSubStorage saveDataStorage, in ValueSubStorage logStorage, IBufferManager bufferManager, public Result Extend(in ValueSubStorage saveDataStorage, in ValueSubStorage logStorage, IBufferManager bufferManager,
IMacGenerator macGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion) IMacGenerator macGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion)
{ {
throw new NotImplementedException(); Assert.SdkRequiresNotNull(_contextStorage);
if (_context.State == State.Initial)
{
Result res = JournalIntegritySaveDataFileSystemDriver.OperateExpand(in saveDataStorage, in logStorage,
_context.BlockSize, GetAvailableBlockCount(), GetJournalBlockCount(), bufferManager, macGenerator,
hashGeneratorFactorySelector, minimumVersion);
if (res.IsFailure()) return res.Miss();
_context.State = State.Extended;
res = WriteContext(_contextStorage);
if (res.IsFailure()) return res.Miss();
}
if (_context.State == State.Extended)
{
Result res = JournalIntegritySaveDataFileSystemDriver.CommitExpand(in saveDataStorage, in logStorage,
_context.BlockSize, bufferManager);
if (res.IsFailure()) return res.Miss();
_context.State = State.Committed;
res = WriteContext(_contextStorage);
if (res.IsFailure()) return res.Miss();
}
if (_context.State != State.Committed)
return ResultFs.BadState.Log();
return Result.Success;
} }
private void UpdateContextV1ToV2(ref Context context) private void UpdateContextV1ToV2(ref Context context)
{ {
throw new NotImplementedException(); Assert.SdkAssert(context.Version == Version1);
context.AvailableSize *= context.BlockSize;
context.JournalSize *= context.BlockSize;
context.Version = Version2;
} }
} }

View file

@ -10,13 +10,13 @@ namespace LibHac.FsSrv.Impl;
/// <summary> /// <summary>
/// Holds the <see cref="ISaveDataExtraDataAccessor"/>s for opened save data file systems. /// Holds the <see cref="ISaveDataExtraDataAccessor"/>s for opened save data file systems.
/// </summary> /// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks> /// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorObserver public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorObserver
{ {
/// <summary> /// <summary>
/// Holds a single cached extra data accessor identified by its save data ID and save data space ID. /// Holds a single cached extra data accessor identified by its save data ID and save data space ID.
/// </summary> /// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks> /// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
[NonCopyable] [NonCopyable]
private struct Cache : IDisposable private struct Cache : IDisposable
{ {

View file

@ -11,7 +11,7 @@ namespace LibHac.FsSrv.Impl;
/// Manages a list of cached save data file systems. Each file system is registered and retrieved /// Manages a list of cached save data file systems. Each file system is registered and retrieved
/// based on its save data ID and save data space ID. /// based on its save data ID and save data space ID.
/// </summary> /// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks> /// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataFileSystemCacheManager : IDisposable public class SaveDataFileSystemCacheManager : IDisposable
{ {
[NonCopyable] [NonCopyable]

View file

@ -9,7 +9,7 @@ namespace LibHac.FsSrv.Impl;
/// Wraps an <see cref="ISaveDataFileSystem"/>. /// Wraps an <see cref="ISaveDataFileSystem"/>.
/// Upon disposal the base file system is returned to the provided <see cref="SaveDataFileSystemCacheManager"/>. /// Upon disposal the base file system is returned to the provided <see cref="SaveDataFileSystemCacheManager"/>.
/// </summary> /// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks> /// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataFileSystemCacheRegister : IFileSystem public class SaveDataFileSystemCacheRegister : IFileSystem
{ {
private SharedRef<ISaveDataFileSystem> _baseFileSystem; private SharedRef<ISaveDataFileSystem> _baseFileSystem;
@ -36,87 +36,87 @@ public class SaveDataFileSystemCacheRegister : IFileSystem
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode)
{ {
return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode); return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode).Ret();
} }
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path, protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path,
OpenDirectoryMode mode) OpenDirectoryMode mode)
{ {
return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode); return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode).Ret();
} }
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path) protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
{ {
return _baseFileSystem.Get.GetEntryType(out entryType, in path); return _baseFileSystem.Get.GetEntryType(out entryType, in path).Ret();
} }
protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option) protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
{ {
return _baseFileSystem.Get.CreateFile(in path, size, option); return _baseFileSystem.Get.CreateFile(in path, size, option).Ret();
} }
protected override Result DoDeleteFile(ref readonly Path path) protected override Result DoDeleteFile(ref readonly Path path)
{ {
return _baseFileSystem.Get.DeleteFile(in path); return _baseFileSystem.Get.DeleteFile(in path).Ret();
} }
protected override Result DoCreateDirectory(ref readonly Path path) protected override Result DoCreateDirectory(ref readonly Path path)
{ {
return _baseFileSystem.Get.CreateDirectory(in path); return _baseFileSystem.Get.CreateDirectory(in path).Ret();
} }
protected override Result DoDeleteDirectory(ref readonly Path path) protected override Result DoDeleteDirectory(ref readonly Path path)
{ {
return _baseFileSystem.Get.DeleteDirectory(in path); return _baseFileSystem.Get.DeleteDirectory(in path).Ret();
} }
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path) protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
{ {
return _baseFileSystem.Get.DeleteDirectoryRecursively(in path); return _baseFileSystem.Get.DeleteDirectoryRecursively(in path).Ret();
} }
protected override Result DoCleanDirectoryRecursively(ref readonly Path path) protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
{ {
return _baseFileSystem.Get.CleanDirectoryRecursively(in path); return _baseFileSystem.Get.CleanDirectoryRecursively(in path).Ret();
} }
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath) protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
{ {
return _baseFileSystem.Get.RenameFile(in currentPath, in newPath); return _baseFileSystem.Get.RenameFile(in currentPath, in newPath).Ret();
} }
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath) protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
{ {
return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath); return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath).Ret();
} }
protected override Result DoCommit() protected override Result DoCommit()
{ {
return _baseFileSystem.Get.Commit(); return _baseFileSystem.Get.Commit().Ret();
} }
protected override Result DoCommitProvisionally(long counter) protected override Result DoCommitProvisionally(long counter)
{ {
return _baseFileSystem.Get.CommitProvisionally(counter); return _baseFileSystem.Get.CommitProvisionally(counter).Ret();
} }
protected override Result DoRollback() protected override Result DoRollback()
{ {
return _baseFileSystem.Get.Rollback(); return _baseFileSystem.Get.Rollback().Ret();
} }
protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path) protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path)
{ {
return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path); return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path).Ret();
} }
protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path) protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path)
{ {
return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path); return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path).Ret();
} }
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute) protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
{ {
return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute); return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute).Ret();
} }
} }