Fixup ProgramRegistryManager and ProgramInfo

This commit is contained in:
Alex Barney 2022-01-12 01:36:54 -07:00
parent 04a2e51cfa
commit 8ba258034b
7 changed files with 186 additions and 145 deletions

View file

@ -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;
@ -63,4 +64,4 @@ public readonly struct FileSystemServerImpl
internal ref FileSystemServerGlobals Globals => ref FsSrv.Globals;
internal FileSystemServerImpl(FileSystemServer parentServer) => FsSrv = parentServer;
}
}

View 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
};
}

View file

@ -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,20 +42,19 @@ 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)
{
if (info.Contains(processId))
return ResultFs.InvalidArgument.Log();
}
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
ProgramInfoList.AddLast(programInfo);
return Result.Success;
foreach (ProgramInfo info in _programInfoList)
{
if (info.Contains(processId))
return ResultFs.InvalidArgument.Log();
}
_programInfoList.AddLast(programInfo);
return Result.Success;
}
/// <summary>
@ -78,19 +65,18 @@ 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)
{
if (node.Value.Contains(processId))
{
ProgramInfoList.Remove(node);
return Result.Success;
}
}
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
return ResultFs.InvalidArgument.Log();
for (LinkedListNode<ProgramInfo> node = _programInfoList.First; node != null; node = node.Next)
{
if (node.Value.Contains(processId))
{
_programInfoList.Remove(node);
return Result.Success;
}
}
return ResultFs.InvalidArgument.Log();
}
/// <summary>
@ -103,26 +89,25 @@ 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 = ProgramInfo.GetProgramInfoForInitialProcess(_fsServer);
return Result.Success;
}
foreach (ProgramInfo info in _programInfoList)
{
if (info.Contains(processId))
{
programInfo = GetProgramInfoForInitialProcess();
programInfo = info;
return Result.Success;
}
foreach (ProgramInfo info in ProgramInfoList)
{
if (info.Contains(processId))
{
programInfo = info;
return Result.Success;
}
}
UnsafeHelpers.SkipParamInit(out programInfo);
return ResultFs.ProgramInfoNotFound.Log();
}
UnsafeHelpers.SkipParamInit(out programInfo);
return ResultFs.ProgramInfoNotFound.Log();
}
/// <summary>
@ -135,77 +120,18 @@ 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)
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
foreach (ProgramInfo info in _programInfoList)
{
foreach (ProgramInfo info in ProgramInfoList)
if (info.ProgramId.Value == programId)
{
if (info.ProgramId.Value == programId)
{
programInfo = info;
return Result.Success;
}
programInfo = info;
return Result.Success;
}
UnsafeHelpers.SkipParamInit(out programInfo);
return ResultFs.ProgramInfoNotFound.Log();
}
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
};
}
}

View file

@ -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);
@ -91,4 +91,4 @@ public class ProgramRegistryImpl : IProgramRegistry
_processId = processId;
return Result.Success;
}
}
}

View file

@ -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

View file

@ -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
@ -82,4 +80,4 @@ public class Horizon
return client;
}
}
}

View file

@ -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; }
@ -34,4 +37,4 @@ public readonly struct OsStateImpl
internal HorizonClient Hos => Os.Hos;
internal OsStateImpl(OsState parent) => Os = parent;
}
}