mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Fixup ProgramRegistryManager and ProgramInfo
This commit is contained in:
parent
04a2e51cfa
commit
8ba258034b
7 changed files with 186 additions and 145 deletions
|
@ -29,6 +29,7 @@ internal struct FileSystemServerGlobals
|
|||
public ProgramRegistryImplGlobals ProgramRegistryImpl;
|
||||
public DeviceEventSimulatorGlobals DeviceEventSimulator;
|
||||
public AccessControlGlobals AccessControl;
|
||||
public ProgramInfoGlobals ProgramInfo;
|
||||
public StorageDeviceManagerFactoryGlobals StorageDeviceManagerFactory;
|
||||
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
|
||||
public MultiCommitManagerGlobals MultiCommitManager;
|
||||
|
|
120
src/LibHac/FsSrv/Impl/ProgramInfo.cs
Normal file
120
src/LibHac/FsSrv/Impl/ProgramInfo.cs
Normal file
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSrv.Impl;
|
||||
|
||||
public static class ProgramInfoGlobalMethods
|
||||
{
|
||||
public static bool IsInitialProgram(this FileSystemServer fsSrv, ulong processId)
|
||||
{
|
||||
ref ProgramInfoGlobals g = ref fsSrv.Globals.ProgramInfo;
|
||||
using (var guard = new InitializationGuard(ref g.InitialProcessIdRangeInitGuard, fsSrv.Globals.InitMutex))
|
||||
{
|
||||
if (!guard.IsInitialized)
|
||||
{
|
||||
// Todo: We have no kernel to call into, so use hardcoded values for now
|
||||
g.InitialProcessIdMin = OsState.InitialProcessCountMin;
|
||||
g.InitialProcessIdMax = OsState.InitialProcessCountMax;
|
||||
|
||||
Abort.DoAbortUnless(0 < g.InitialProcessIdMin && g.InitialProcessIdMin <= g.InitialProcessIdMax,
|
||||
"Invalid initial process ID range");
|
||||
}
|
||||
}
|
||||
|
||||
Abort.DoAbortUnless(g.InitialProcessIdMin != 0);
|
||||
|
||||
return g.InitialProcessIdMin <= processId && processId <= g.InitialProcessIdMax;
|
||||
}
|
||||
|
||||
public static bool IsCurrentProcess(this FileSystemServer fsSrv, ulong processId)
|
||||
{
|
||||
ref ProgramInfoGlobals g = ref fsSrv.Globals.ProgramInfo;
|
||||
using (var guard = new InitializationGuard(ref g.CurrentProcessIdInitGuard, fsSrv.Globals.InitMutex))
|
||||
{
|
||||
if (!guard.IsInitialized)
|
||||
{
|
||||
g.CurrentProcessId = fsSrv.Hos.Os.GetCurrentProcessId().Value;
|
||||
}
|
||||
}
|
||||
|
||||
return g.CurrentProcessId == processId;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ProgramInfoGlobals
|
||||
{
|
||||
public nint CurrentProcessIdInitGuard;
|
||||
public ulong CurrentProcessId;
|
||||
|
||||
public nint InitialProcessIdRangeInitGuard;
|
||||
public ulong InitialProcessIdMin;
|
||||
public ulong InitialProcessIdMax;
|
||||
|
||||
public nint ProgramInfoForInitialProcessInitGuard;
|
||||
public ProgramInfo ProgramInfoForInitialProcess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the program ID, storage location and FS permissions of a running process.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class ProgramInfo
|
||||
{
|
||||
private readonly ulong _processId;
|
||||
public ProgramId ProgramId { get; }
|
||||
public StorageId StorageId { get; }
|
||||
public AccessControl AccessControl { get; }
|
||||
|
||||
public ulong ProgramIdValue => ProgramId.Value;
|
||||
|
||||
private ProgramInfo(FileSystemServer fsServer, ReadOnlySpan<byte> accessControlData,
|
||||
ReadOnlySpan<byte> accessControlDescriptor)
|
||||
{
|
||||
_processId = 0;
|
||||
AccessControl = new AccessControl(fsServer, accessControlData, accessControlDescriptor, ulong.MaxValue);
|
||||
ProgramId = default;
|
||||
StorageId = 0;
|
||||
}
|
||||
|
||||
public ProgramInfo(FileSystemServer fsServer, ulong processId, ProgramId programId, StorageId storageId,
|
||||
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
||||
{
|
||||
_processId = processId;
|
||||
AccessControl = new AccessControl(fsServer, accessControlData, accessControlDescriptor);
|
||||
ProgramId = programId;
|
||||
StorageId = storageId;
|
||||
}
|
||||
|
||||
public bool Contains(ulong processId) => _processId == processId;
|
||||
|
||||
public static ProgramInfo GetProgramInfoForInitialProcess(FileSystemServer fsSrv)
|
||||
{
|
||||
ref ProgramInfoGlobals g = ref fsSrv.Globals.ProgramInfo;
|
||||
using (var guard = new InitializationGuard(ref g.ProgramInfoForInitialProcessInitGuard, fsSrv.Globals.InitMutex))
|
||||
{
|
||||
if (!guard.IsInitialized)
|
||||
{
|
||||
g.ProgramInfoForInitialProcess = new ProgramInfo(fsSrv, InitialProcessAccessControlDataHeader,
|
||||
InitialProcessAccessControlDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return g.ProgramInfoForInitialProcess;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> InitialProcessAccessControlDataHeader => new byte[]
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1C, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> InitialProcessAccessControlDescriptor => new byte[]
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
}
|
|
@ -3,43 +3,31 @@ using System.Collections.Generic;
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSrv.Impl;
|
||||
|
||||
/// <summary>
|
||||
/// Handles adding, removing, and accessing <see cref="ProgramInfo"/> from the <see cref="ProgramRegistryImpl"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 10.0.0 (nnSdk 10.4.0)</remarks>
|
||||
internal class ProgramRegistryManager
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal class ProgramRegistryManager : IDisposable
|
||||
{
|
||||
// Note: FS keeps each ProgramInfo in a shared_ptr, but there aren't any non-memory resources
|
||||
// that need to be freed, so we use plain ProgramInfos
|
||||
private LinkedList<ProgramInfo> ProgramInfoList { get; }
|
||||
private FileSystemServer FsServer { get; }
|
||||
private LinkedList<ProgramInfo> _programInfoList;
|
||||
private SdkMutexType _mutex;
|
||||
|
||||
// Note: This variable is global in FS. It's moved to ProgramRegistryManager here because it
|
||||
// relies on some state kept in FileSystemServer, and it's only used by ProgramRegistryManager
|
||||
private ProgramInfo _programInfoForInitialProcess;
|
||||
private readonly object _programInfoForInitialProcessGuard = new object();
|
||||
private FileSystemServer _fsServer;
|
||||
|
||||
public ProgramRegistryManager(FileSystemServer fsServer)
|
||||
{
|
||||
ProgramInfoList = new LinkedList<ProgramInfo>();
|
||||
FsServer = fsServer;
|
||||
_programInfoList = new LinkedList<ProgramInfo>();
|
||||
_mutex = new SdkMutexType();
|
||||
_fsServer = fsServer;
|
||||
}
|
||||
|
||||
private ProgramInfo GetProgramInfoForInitialProcess()
|
||||
{
|
||||
if (_programInfoForInitialProcess == null)
|
||||
{
|
||||
lock (_programInfoForInitialProcessGuard)
|
||||
{
|
||||
_programInfoForInitialProcess ??= ProgramInfo.CreateProgramInfoForInitialProcess(FsServer);
|
||||
}
|
||||
}
|
||||
|
||||
return _programInfoForInitialProcess;
|
||||
}
|
||||
public void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a program with information about that program in the program registry.
|
||||
|
@ -54,21 +42,20 @@ internal class ProgramRegistryManager
|
|||
public Result RegisterProgram(ulong processId, ProgramId programId, StorageId storageId,
|
||||
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
||||
{
|
||||
var programInfo = new ProgramInfo(FsServer, processId, programId, storageId, accessControlData,
|
||||
var programInfo = new ProgramInfo(_fsServer, processId, programId, storageId, accessControlData,
|
||||
accessControlDescriptor);
|
||||
|
||||
lock (ProgramInfoList)
|
||||
{
|
||||
foreach (ProgramInfo info in ProgramInfoList)
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
foreach (ProgramInfo info in _programInfoList)
|
||||
{
|
||||
if (info.Contains(processId))
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
ProgramInfoList.AddLast(programInfo);
|
||||
_programInfoList.AddLast(programInfo);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the program with the specified process ID.
|
||||
|
@ -78,20 +65,19 @@ internal class ProgramRegistryManager
|
|||
/// <see cref="ResultFs.InvalidArgument"/>: The process ID is not registered.</returns>
|
||||
public Result UnregisterProgram(ulong processId)
|
||||
{
|
||||
lock (ProgramInfoList)
|
||||
{
|
||||
for (LinkedListNode<ProgramInfo> node = ProgramInfoList.First; node != null; node = node.Next)
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
for (LinkedListNode<ProgramInfo> node = _programInfoList.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value.Contains(processId))
|
||||
{
|
||||
ProgramInfoList.Remove(node);
|
||||
_programInfoList.Remove(node);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ProgramInfo"/> associated with the specified process ID.
|
||||
|
@ -103,15 +89,15 @@ internal class ProgramRegistryManager
|
|||
/// <see cref="ResultFs.ProgramInfoNotFound"/>: The <see cref="ProgramInfo"/> was not found.</returns>
|
||||
public Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
lock (ProgramInfoList)
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
if (_fsServer.IsInitialProgram(processId))
|
||||
{
|
||||
if (ProgramInfo.IsInitialProgram(processId))
|
||||
{
|
||||
programInfo = GetProgramInfoForInitialProcess();
|
||||
programInfo = ProgramInfo.GetProgramInfoForInitialProcess(_fsServer);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
foreach (ProgramInfo info in ProgramInfoList)
|
||||
foreach (ProgramInfo info in _programInfoList)
|
||||
{
|
||||
if (info.Contains(processId))
|
||||
{
|
||||
|
@ -123,7 +109,6 @@ internal class ProgramRegistryManager
|
|||
UnsafeHelpers.SkipParamInit(out programInfo);
|
||||
return ResultFs.ProgramInfoNotFound.Log();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ProgramInfo"/> associated with the specified program ID.
|
||||
|
@ -135,9 +120,9 @@ internal class ProgramRegistryManager
|
|||
/// <see cref="ResultFs.ProgramInfoNotFound"/>: The <see cref="ProgramInfo"/> was not found.</returns>
|
||||
public Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
|
||||
{
|
||||
lock (ProgramInfoList)
|
||||
{
|
||||
foreach (ProgramInfo info in ProgramInfoList)
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
foreach (ProgramInfo info in _programInfoList)
|
||||
{
|
||||
if (info.ProgramId.Value == programId)
|
||||
{
|
||||
|
@ -149,63 +134,4 @@ internal class ProgramRegistryManager
|
|||
UnsafeHelpers.SkipParamInit(out programInfo);
|
||||
return ResultFs.ProgramInfoNotFound.Log();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ProgramInfo
|
||||
{
|
||||
private ulong ProcessId { get; }
|
||||
public ProgramId ProgramId { get; }
|
||||
public StorageId StorageId { get; }
|
||||
public AccessControl AccessControl { get; }
|
||||
|
||||
public ulong ProgramIdValue => ProgramId.Value;
|
||||
|
||||
public ProgramInfo(FileSystemServer fsServer, ulong processId, ProgramId programId, StorageId storageId,
|
||||
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
||||
{
|
||||
ProcessId = processId;
|
||||
AccessControl = new AccessControl(fsServer, accessControlData, accessControlDescriptor);
|
||||
ProgramId = programId;
|
||||
StorageId = storageId;
|
||||
}
|
||||
|
||||
private ProgramInfo(FileSystemServer fsServer, ReadOnlySpan<byte> accessControlData,
|
||||
ReadOnlySpan<byte> accessControlDescriptor)
|
||||
{
|
||||
ProcessId = 0;
|
||||
AccessControl = new AccessControl(fsServer, accessControlData, accessControlDescriptor, ulong.MaxValue);
|
||||
ProgramId = default;
|
||||
StorageId = 0;
|
||||
}
|
||||
|
||||
public bool Contains(ulong processId) => ProcessId == processId;
|
||||
|
||||
public static bool IsInitialProgram(ulong processId)
|
||||
{
|
||||
// Todo: We have no kernel to call into, so use hardcoded values for now
|
||||
const int initialProcessIdLowerBound = 1;
|
||||
const int initialProcessIdUpperBound = 0x50;
|
||||
|
||||
return initialProcessIdLowerBound <= processId && processId <= initialProcessIdUpperBound;
|
||||
}
|
||||
|
||||
internal static ProgramInfo CreateProgramInfoForInitialProcess(FileSystemServer fsServer)
|
||||
{
|
||||
return new ProgramInfo(fsServer, InitialProcessAccessControlDataHeader,
|
||||
InitialProcessAccessControlDescriptor);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> InitialProcessAccessControlDataHeader => new byte[]
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1C, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> InitialProcessAccessControlDescriptor => new byte[]
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
}
|
|
@ -50,7 +50,7 @@ public class ProgramRegistryImpl : IProgramRegistry
|
|||
public Result RegisterProgram(ulong processId, ProgramId programId, StorageId storageId,
|
||||
InBuffer accessControlData, InBuffer accessControlDescriptor)
|
||||
{
|
||||
if (!ProgramInfo.IsInitialProgram(_processId))
|
||||
if (!_fsServer.IsInitialProgram(_processId))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return Globals.ServiceImpl.RegisterProgramInfo(processId, programId, storageId, accessControlData.Buffer,
|
||||
|
@ -63,7 +63,7 @@ public class ProgramRegistryImpl : IProgramRegistry
|
|||
/// <inheritdoc cref="ProgramRegistryManager.UnregisterProgram" />
|
||||
public Result UnregisterProgram(ulong processId)
|
||||
{
|
||||
if (!ProgramInfo.IsInitialProgram(_processId))
|
||||
if (!_fsServer.IsInitialProgram(_processId))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return Globals.ServiceImpl.UnregisterProgramInfo(processId);
|
||||
|
|
|
@ -517,7 +517,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
|||
// Only the FS process may delete the save indexer's save data.
|
||||
if (saveDataId == SaveData.SaveIndexerId)
|
||||
{
|
||||
if (!IsCurrentProcess(_processId))
|
||||
if (!_serviceImpl.FsServer.IsCurrentProcess(_processId))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
actualSpaceId = spaceId;
|
||||
|
@ -2157,13 +2157,6 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
|
|||
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
|
||||
}
|
||||
|
||||
private bool IsCurrentProcess(ulong processId)
|
||||
{
|
||||
ulong currentId = Hos.Os.GetCurrentProcessId().Value;
|
||||
|
||||
return processId == currentId;
|
||||
}
|
||||
|
||||
private SaveDataSpaceId ConvertToRealSpaceId(SaveDataSpaceId spaceId)
|
||||
{
|
||||
return spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode
|
||||
|
|
|
@ -12,8 +12,6 @@ namespace LibHac;
|
|||
|
||||
public class Horizon
|
||||
{
|
||||
private const int InitialProcessCountMax = 0x50;
|
||||
|
||||
internal ITickGenerator TickGenerator { get; }
|
||||
internal ServiceManager ServiceManager { get; }
|
||||
private HorizonClient LoaderClient { get; }
|
||||
|
@ -23,7 +21,7 @@ public class Horizon
|
|||
|
||||
public Horizon(HorizonConfiguration config)
|
||||
{
|
||||
_currentProcessId = InitialProcessCountMax;
|
||||
_currentProcessId = OsState.InitialProcessCountMax + 1;
|
||||
|
||||
TickGenerator = config.TickGenerator ?? new DefaultTickGenerator();
|
||||
ServiceManager = new ServiceManager();
|
||||
|
@ -35,7 +33,7 @@ public class Horizon
|
|||
{
|
||||
ulong processId = Interlocked.Increment(ref _currentInitialProcessId);
|
||||
|
||||
Abort.DoAbortUnless(processId <= InitialProcessCountMax, "Created too many privileged clients.");
|
||||
Abort.DoAbortUnless(processId <= OsState.InitialProcessCountMax, "Created too many privileged clients.");
|
||||
|
||||
// Todo: Register process with FS
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ namespace LibHac.Os;
|
|||
|
||||
public class OsState : IDisposable
|
||||
{
|
||||
internal const int InitialProcessCountMin = 1;
|
||||
internal const int InitialProcessCountMax = 0x50;
|
||||
|
||||
public OsStateImpl Impl => new OsStateImpl(this);
|
||||
internal HorizonClient Hos { get; }
|
||||
internal OsResourceManager ResourceManager { get; }
|
||||
|
|
Loading…
Reference in a new issue