mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add DebugConfigurationService
This commit is contained in:
parent
37b8249a8b
commit
ed6a34d857
7 changed files with 359 additions and 3 deletions
74
src/LibHac/Fs/Shim/Debug.cs
Normal file
74
src/LibHac/Fs/Shim/Debug.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Fs.Shim;
|
||||
|
||||
public enum DebugOptionKey : uint
|
||||
{
|
||||
SaveDataEncryption = 0x20454453 // "SDE "
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains debug-related functions for the FS service.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
||||
public static class DebugShim
|
||||
{
|
||||
public static Result CreatePaddingFile(this FileSystemClient fs, long size)
|
||||
{
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fileSystemProxy.Get.CreatePaddingFile(size);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result DeleteAllPaddingFiles(this FileSystemClient fs)
|
||||
{
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fileSystemProxy.Get.DeleteAllPaddingFiles();
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result OverrideSaveDataTransferTokenSignVerificationKey(this FileSystemClient fs,
|
||||
ReadOnlySpan<byte> keyBuffer)
|
||||
{
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fileSystemProxy.Get.OverrideSaveDataTransferTokenSignVerificationKey(new InBuffer(keyBuffer));
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result SetDebugOption(this FileSystemClient fs, DebugOptionKey key, long value)
|
||||
{
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fileSystemProxy.Get.RegisterDebugConfiguration((uint)key, value);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result UnsetDebugOption(this FileSystemClient fs, DebugOptionKey key)
|
||||
{
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fileSystemProxy.Get.UnregisterDebugConfiguration((uint)key);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
163
src/LibHac/FsSrv/DebugConfigurationService.cs
Normal file
163
src/LibHac/FsSrv/DebugConfigurationService.cs
Normal file
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
/// <summary>
|
||||
/// Handles debug configuration calls for <see cref="FileSystemProxyImpl"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public struct DebugConfigurationService
|
||||
{
|
||||
private DebugConfigurationServiceImpl _serviceImpl;
|
||||
private ulong _processId;
|
||||
|
||||
// LibHac addition
|
||||
private readonly FileSystemServer _fsServer;
|
||||
|
||||
public DebugConfigurationService(FileSystemServer fsServer, DebugConfigurationServiceImpl serviceImpl,
|
||||
ulong processId)
|
||||
{
|
||||
_serviceImpl = serviceImpl;
|
||||
_processId = processId;
|
||||
_fsServer = fsServer;
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
using var programRegistry = new ProgramRegistryImpl(_fsServer);
|
||||
|
||||
return programRegistry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
|
||||
public Result Register(uint key, long value)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo, _processId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetDebugConfiguration))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
_serviceImpl.Register(key, value);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Unregister(uint key)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo, _processId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetDebugConfiguration))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
_serviceImpl.Unregister(key);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages a key-value list of debug settings.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class DebugConfigurationServiceImpl : IDisposable
|
||||
{
|
||||
private Configuration _config;
|
||||
private Array4<Entry> _entries;
|
||||
private SdkMutexType _mutex;
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public bool IsDisabled;
|
||||
}
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public uint Key;
|
||||
public long Value;
|
||||
}
|
||||
|
||||
public DebugConfigurationServiceImpl(in Configuration config)
|
||||
{
|
||||
_config = config;
|
||||
_mutex = new SdkMutexType();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Register(uint key, long value)
|
||||
{
|
||||
Abort.DoAbortUnless(key != 0);
|
||||
|
||||
if (_config.IsDisabled)
|
||||
return;
|
||||
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
for (int i = 0; i < _entries.ItemsRo.Length; i++)
|
||||
{
|
||||
// Update the existing value if the key is already registered
|
||||
if (_entries[i].Key == key)
|
||||
{
|
||||
_entries[i].Value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _entries.ItemsRo.Length; i++)
|
||||
{
|
||||
if (_entries[i].Key == 0)
|
||||
{
|
||||
_entries[i].Key = key;
|
||||
_entries[i].Value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Abort.DoAbort();
|
||||
}
|
||||
|
||||
public void Unregister(uint key)
|
||||
{
|
||||
Abort.DoAbortUnless(key != 0);
|
||||
|
||||
if (_config.IsDisabled)
|
||||
return;
|
||||
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
for (int i = 0; i < _entries.ItemsRo.Length; i++)
|
||||
{
|
||||
if (_entries[i].Key == key)
|
||||
{
|
||||
_entries[i].Key = 0;
|
||||
_entries[i].Value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long Get(DebugOptionKey key, long defaultValue)
|
||||
{
|
||||
Abort.DoAbortUnless(key != 0);
|
||||
|
||||
if (_config.IsDisabled)
|
||||
return defaultValue;
|
||||
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
for (int i = 0; i < _entries.ItemsRo.Length; i++)
|
||||
{
|
||||
if (_entries[i].Key == (uint)key)
|
||||
{
|
||||
return _entries[i].Value;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
|
@ -14,4 +14,5 @@ public class FileSystemProxyConfiguration
|
|||
public StatusReportServiceImpl StatusReportService { get; set; }
|
||||
public ProgramRegistryServiceImpl ProgramRegistryService { get; set; }
|
||||
public AccessLogServiceImpl AccessLogService { get; set; }
|
||||
}
|
||||
public DebugConfigurationServiceImpl DebugConfigurationService { get; set; }
|
||||
}
|
|
@ -35,6 +35,7 @@ public static class FileSystemProxyImplGlobalMethods
|
|||
g.StatusReportServiceImpl = configuration.StatusReportService;
|
||||
g.ProgramRegistryServiceImpl = configuration.ProgramRegistryService;
|
||||
g.AccessLogServiceImpl = configuration.AccessLogService;
|
||||
g.DebugConfigurationServiceImpl = configuration.DebugConfigurationService;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +50,7 @@ internal struct FileSystemProxyImplGlobals
|
|||
public StatusReportServiceImpl StatusReportServiceImpl;
|
||||
public ProgramRegistryServiceImpl ProgramRegistryServiceImpl;
|
||||
public AccessLogServiceImpl AccessLogServiceImpl;
|
||||
public DebugConfigurationServiceImpl DebugConfigurationServiceImpl;
|
||||
public Optional<FileSystemProxyCoreImpl> FileSystemProxyCoreImpl;
|
||||
}
|
||||
|
||||
|
@ -146,6 +148,11 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
|
|||
return new AccessLogService(Globals.AccessLogServiceImpl, _currentProcess);
|
||||
}
|
||||
|
||||
private DebugConfigurationService GetDebugConfigurationService()
|
||||
{
|
||||
return new DebugConfigurationService(_fsServer, Globals.DebugConfigurationServiceImpl, _currentProcess);
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithId(ref SharedRef<IFileSystemSf> outFileSystem, in FspPath path,
|
||||
ulong id, FileSystemProxyType fsType)
|
||||
{
|
||||
|
@ -1151,4 +1158,14 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
|
|||
{
|
||||
return GetBaseFileSystemService().OpenBisWiper(ref outBisWiper, transferMemoryHandle, transferMemorySize);
|
||||
}
|
||||
|
||||
public Result RegisterDebugConfiguration(uint key, long value)
|
||||
{
|
||||
return GetDebugConfigurationService().Register(key, value);
|
||||
}
|
||||
|
||||
public Result UnregisterDebugConfiguration(uint key)
|
||||
{
|
||||
return GetDebugConfigurationService().Unregister(key);
|
||||
}
|
||||
}
|
|
@ -74,6 +74,11 @@ public static class FileSystemServerInitializer
|
|||
Memory<byte> heapBuffer = GC.AllocateArray<byte>(BufferManagerHeapSize, true);
|
||||
bufferManager.Initialize(BufferManagerCacheSize, heapBuffer, BufferManagerBlockSize);
|
||||
|
||||
// Todo: Assign based on the value of "IsDevelopment"
|
||||
var debugConfigurationServiceConfig = new DebugConfigurationServiceImpl.Configuration();
|
||||
debugConfigurationServiceConfig.IsDisabled = false;
|
||||
var debugConfigurationService = new DebugConfigurationServiceImpl(in debugConfigurationServiceConfig);
|
||||
|
||||
var saveDataIndexerManager = new SaveDataIndexerManager(server.Hos.Fs, Fs.SaveData.SaveIndexerId,
|
||||
new ArrayPoolMemoryResource(), new SdHandleManager(), false);
|
||||
|
||||
|
@ -175,7 +180,8 @@ public static class FileSystemServerInitializer
|
|||
TimeService = timeService,
|
||||
StatusReportService = statusReportService,
|
||||
ProgramRegistryService = programRegistryService,
|
||||
AccessLogService = accessLogService
|
||||
AccessLogService = accessLogService,
|
||||
DebugConfigurationService = debugConfigurationService
|
||||
};
|
||||
|
||||
server.InitializeFileSystemProxy(fspConfig);
|
||||
|
|
|
@ -124,8 +124,10 @@ public interface IFileSystemProxy : IDisposable
|
|||
Result FlushAccessLogOnSdCard();
|
||||
Result OutputApplicationInfoAccessLog(in ApplicationInfo applicationInfo);
|
||||
Result OutputMultiProgramTagAccessLog();
|
||||
Result RegisterDebugConfiguration(uint key, long value);
|
||||
Result UnregisterDebugConfiguration(uint key);
|
||||
Result OverrideSaveDataTransferTokenSignVerificationKey(InBuffer key);
|
||||
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
|
||||
Result OpenMultiCommitManager(ref SharedRef<IMultiCommitManager> outCommitManager);
|
||||
Result OpenBisWiper(ref SharedRef<IWiper> outBisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Ncm;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests;
|
||||
|
||||
public class Debug
|
||||
{
|
||||
[Fact]
|
||||
public void SetDebugOption_KeyIsZero_Aborts()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
Assert.Throws<HorizonResultException>(() => fs.SetDebugOption(0, 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetDebugOption_NoPermissions_ReturnsPermissionDenied()
|
||||
{
|
||||
Horizon hos = HorizonFactory.CreateBasicHorizon();
|
||||
|
||||
HorizonClient client =
|
||||
hos.CreateHorizonClient(new ProgramLocation(new ProgramId(1), StorageId.BuiltInSystem), 0);
|
||||
|
||||
Assert.Result(ResultFs.PermissionDenied, client.Fs.SetDebugOption((DebugOptionKey)1, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetDebugOption_DebugConfigIsFull_Aborts()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)1, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)2, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)3, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)4, 0));
|
||||
|
||||
Assert.Throws<LibHacException>(() => fs.SetDebugOption((DebugOptionKey)5, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetDebugOption_ReplaceExistingValueWhenFull_ReturnsSuccess()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)1, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)2, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)3, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)4, 0));
|
||||
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)1, 10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetDebugOption_AfterRemovingKeyWhenFull_ReturnsSuccess()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)1, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)2, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)3, 0));
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)4, 0));
|
||||
|
||||
Assert.Success(fs.UnsetDebugOption((DebugOptionKey)2));
|
||||
|
||||
Assert.Success(fs.SetDebugOption((DebugOptionKey)2, 4));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnsetDebugOption_UnsetExistingKey_ReturnsSuccess()
|
||||
{
|
||||
const DebugOptionKey key = DebugOptionKey.SaveDataEncryption;
|
||||
const long value = 0;
|
||||
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
Assert.Success(fs.SetDebugOption(key, value));
|
||||
Assert.Success(fs.UnsetDebugOption(key));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnsetDebugOption_NoPermissions_ReturnsPermissionDenied()
|
||||
{
|
||||
Horizon hos = HorizonFactory.CreateBasicHorizon();
|
||||
|
||||
HorizonClient client =
|
||||
hos.CreateHorizonClient(new ProgramLocation(new ProgramId(1), StorageId.BuiltInSystem), 0);
|
||||
|
||||
Assert.Result(ResultFs.PermissionDenied, client.Fs.UnsetDebugOption((DebugOptionKey)1));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue