mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add IFileSystemProxyForLoader and IProgramRegistry interfaces
This commit is contained in:
parent
071b608f5f
commit
3184e6ca7e
18
src/LibHac/Fs/CodeVerificationData.cs
Normal file
18
src/LibHac/Fs/CodeVerificationData.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x124)]
|
||||
public struct CodeVerificationData
|
||||
{
|
||||
private const int Signature2Size = 0x100;
|
||||
|
||||
[FieldOffset(0x000)] private byte _signature2;
|
||||
[FieldOffset(0x100)] public Buffer32 NcaHeaderHash;
|
||||
[FieldOffset(0x120)] public bool IsValid;
|
||||
|
||||
public Span<byte> NcaSignature2 => SpanHelpers.CreateSpan(ref _signature2, Signature2Size);
|
||||
}
|
||||
}
|
@ -6,17 +6,24 @@ using LibHac.Diag;
|
||||
using LibHac.Fs.Accessors;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public partial class FileSystemClient
|
||||
{
|
||||
private HorizonClient Hos { get; }
|
||||
private IFileSystemProxy FsProxy { get; set; }
|
||||
internal HorizonClient Hos { get; }
|
||||
|
||||
private IFileSystemProxy FsProxy { get; set; }
|
||||
private readonly object _fspInitLocker = new object();
|
||||
|
||||
private IFileSystemProxyForLoader FsProxyForLoader { get; set; }
|
||||
private readonly object _fsplInitLocker = new object();
|
||||
|
||||
private IProgramRegistry ProgramRegistry { get; set; }
|
||||
private readonly object _progRegInitLocker = new object();
|
||||
|
||||
internal ITimeSpanGenerator Time { get; }
|
||||
private IAccessLog AccessLog { get; set; }
|
||||
|
||||
@ -44,7 +51,7 @@ namespace LibHac.Fs
|
||||
{
|
||||
if (FsProxy != null) return FsProxy;
|
||||
|
||||
lock (_fspInitLocker)
|
||||
lock (_fsplInitLocker)
|
||||
{
|
||||
if (FsProxy != null) return FsProxy;
|
||||
|
||||
@ -67,6 +74,58 @@ namespace LibHac.Fs
|
||||
}
|
||||
}
|
||||
|
||||
public IFileSystemProxyForLoader GetFileSystemProxyForLoaderServiceObject()
|
||||
{
|
||||
if (FsProxyForLoader != null) return FsProxyForLoader;
|
||||
|
||||
lock (_fspInitLocker)
|
||||
{
|
||||
if (FsProxyForLoader != null) return FsProxyForLoader;
|
||||
|
||||
if (!HasFileSystemServer())
|
||||
{
|
||||
throw new InvalidOperationException("Client was not initialized with a server object.");
|
||||
}
|
||||
|
||||
Result rc = Hos.Sm.GetService(out IFileSystemProxyForLoader fsProxy, "fsp-ldr");
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
throw new HorizonResultException(rc, "Failed to create file system proxy service object.");
|
||||
}
|
||||
|
||||
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
|
||||
|
||||
FsProxyForLoader = fsProxy;
|
||||
return FsProxyForLoader;
|
||||
}
|
||||
}
|
||||
|
||||
public IProgramRegistry GetProgramRegistryServiceObject()
|
||||
{
|
||||
if (ProgramRegistry != null) return ProgramRegistry;
|
||||
|
||||
lock (_progRegInitLocker)
|
||||
{
|
||||
if (ProgramRegistry != null) return ProgramRegistry;
|
||||
|
||||
if (!HasFileSystemServer())
|
||||
{
|
||||
throw new InvalidOperationException("Client was not initialized with a server object.");
|
||||
}
|
||||
|
||||
Result rc = Hos.Sm.GetService(out IProgramRegistry registry, "fsp-pr");
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
throw new HorizonResultException(rc, "Failed to create registry service object.");
|
||||
}
|
||||
|
||||
ProgramRegistry = registry;
|
||||
return ProgramRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
public Result Register(U8Span mountName, IFileSystem fileSystem)
|
||||
{
|
||||
return Register(mountName, fileSystem, null);
|
||||
|
58
src/LibHac/Fs/Shim/Code.cs
Normal file
58
src/LibHac/Fs/Shim/Code.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class Code
|
||||
{
|
||||
public static Result MountCode(this FileSystemClient fs, out CodeVerificationData verificationData,
|
||||
U8Span mountName, U8Span path, ProgramId programId)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = MountCodeImpl(fs, out verificationData, mountName, path, programId);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLog(rc, startTime, endTime,
|
||||
$", name: \"{mountName.ToString()}\", name: \"{path.ToString()}\", programid: 0x{programId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MountCodeImpl(fs, out verificationData, mountName, path, programId);
|
||||
}
|
||||
|
||||
if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
fs.EnableFileSystemAccessorAccessLog(mountName);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
private static Result MountCodeImpl(this FileSystemClient fs, out CodeVerificationData verificationData,
|
||||
U8Span mountName, U8Span path, ProgramId programId)
|
||||
{
|
||||
Unsafe.SkipInit(out verificationData);
|
||||
|
||||
Result rc = MountHelpers.CheckMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FspPath.FromSpan(out FspPath fsPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
|
||||
|
||||
rc = fsProxy.OpenCodeFileSystem(out IFileSystem codeFs, out verificationData, in fsPath, programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, codeFs);
|
||||
}
|
||||
}
|
||||
}
|
15
src/LibHac/Fs/Shim/LoaderApi.cs
Normal file
15
src/LibHac/Fs/Shim/LoaderApi.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class LoaderApi
|
||||
{
|
||||
public static Result IsArchivedProgram(this FileSystemClient fs, out bool isArchived, ProcessId processId)
|
||||
{
|
||||
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
|
||||
|
||||
return fsProxy.IsArchivedProgram(out isArchived, processId.Value);
|
||||
}
|
||||
}
|
||||
}
|
34
src/LibHac/Fs/Shim/ProgramRegistry.cs
Normal file
34
src/LibHac/Fs/Shim/ProgramRegistry.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class ProgramRegistry
|
||||
{
|
||||
/// <inheritdoc cref="ProgramRegistryImpl.RegisterProgram"/>
|
||||
public static Result RegisterProgram(this FileSystemClient fs, ulong processId, ProgramId programId,
|
||||
StorageId storageId, ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
||||
{
|
||||
IProgramRegistry registry = fs.GetProgramRegistryServiceObject();
|
||||
|
||||
Result rc = registry.SetCurrentProcess(fs.Hos.ProcessId.Value);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return registry.RegisterProgram(processId, programId, storageId, accessControlData,
|
||||
accessControlDescriptor);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ProgramRegistryImpl.UnregisterProgram"/>
|
||||
public static Result UnregisterProgram(this FileSystemClient fs, ulong processId)
|
||||
{
|
||||
IProgramRegistry registry = fs.GetProgramRegistryServiceObject();
|
||||
|
||||
Result rc = registry.SetCurrentProcess(fs.Hos.ProcessId.Value);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return registry.UnregisterProgram(processId);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Kvdb;
|
||||
using LibHac.Ncm;
|
||||
@ -12,7 +13,7 @@ using LibHac.Spl;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public class FileSystemProxy : IFileSystemProxy
|
||||
public class FileSystemProxy : IFileSystemProxy, IFileSystemProxyForLoader
|
||||
{
|
||||
private FileSystemProxyCore FsProxyCore { get; }
|
||||
internal HorizonClient Hos { get; }
|
||||
@ -71,6 +72,17 @@ namespace LibHac.FsSrv
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenCodeFileSystem(out IFileSystem fileSystem, out CodeVerificationData verificationData, in FspPath path,
|
||||
ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result IsArchivedProgram(out bool isArchived, ulong processId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result SetCurrentProcess(ulong processId)
|
||||
{
|
||||
CurrentProcess = processId;
|
||||
|
@ -55,6 +55,8 @@ namespace LibHac.FsSrv
|
||||
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
|
||||
|
||||
Hos.Sm.RegisterService(new FileSystemProxyService(this), "fsp-srv").IgnoreResult();
|
||||
Hos.Sm.RegisterService(new FileSystemProxyForLoaderService(this), "fsp-ldr").IgnoreResult();
|
||||
Hos.Sm.RegisterService(new ProgramRegistryService(this), "fsp-pr").IgnoreResult();
|
||||
|
||||
// NS usually takes care of this
|
||||
if (Hos.Fs.IsSdCardInserted())
|
||||
@ -84,6 +86,16 @@ namespace LibHac.FsSrv
|
||||
return new FileSystemProxy(Hos, FsProxyCore);
|
||||
}
|
||||
|
||||
private FileSystemProxy GetFileSystemProxyForLoaderServiceObject()
|
||||
{
|
||||
return new FileSystemProxy(Hos, FsProxyCore);
|
||||
}
|
||||
|
||||
private ProgramRegistryImpl GetProgramRegistryServiceObject()
|
||||
{
|
||||
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryServiceImpl);
|
||||
}
|
||||
|
||||
internal bool IsCurrentProcess(ulong processId)
|
||||
{
|
||||
ulong currentId = Hos.Os.GetCurrentProcessId().Value;
|
||||
@ -106,6 +118,38 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private class FileSystemProxyForLoaderService : IServiceObject
|
||||
{
|
||||
private readonly FileSystemServer _server;
|
||||
|
||||
public FileSystemProxyForLoaderService(FileSystemServer server)
|
||||
{
|
||||
_server = server;
|
||||
}
|
||||
|
||||
public Result GetServiceObject(out object serviceObject)
|
||||
{
|
||||
serviceObject = _server.GetFileSystemProxyForLoaderServiceObject();
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private class ProgramRegistryService : IServiceObject
|
||||
{
|
||||
private readonly FileSystemServer _server;
|
||||
|
||||
public ProgramRegistryService(FileSystemServer server)
|
||||
{
|
||||
_server = server;
|
||||
}
|
||||
|
||||
public Result GetServiceObject(out object serviceObject)
|
||||
{
|
||||
serviceObject = _server.GetProgramRegistryServiceObject();
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -100,7 +100,7 @@ namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
lock (ProgramInfoList)
|
||||
{
|
||||
if(ProgramInfo.IsInitialProgram(processId))
|
||||
if (ProgramInfo.IsInitialProgram(processId))
|
||||
{
|
||||
programInfo = GetProgramInfoForInitialProcess();
|
||||
return Result.Success;
|
||||
@ -178,7 +178,7 @@ namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
// Todo: We have no kernel to call into, so use hardcoded values for now
|
||||
const int initialProcessIdLowerBound = 1;
|
||||
const int initialProcessIdUpperBound = 10;
|
||||
const int initialProcessIdUpperBound = 0x50;
|
||||
|
||||
return initialProcessIdLowerBound >= processId && processId <= initialProcessIdUpperBound;
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
internal class ProgramRegistryImpl
|
||||
internal class ProgramRegistryImpl : IProgramRegistry
|
||||
{
|
||||
private ulong _processId;
|
||||
|
||||
|
45
src/LibHac/FsSrv/Sf/FspPath.cs
Normal file
45
src/LibHac/FsSrv/Sf/FspPath.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsSrv.Sf
|
||||
{
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)]
|
||||
public readonly struct FspPath
|
||||
{
|
||||
internal const int MaxLength = 0x300;
|
||||
|
||||
#if DEBUG
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300;
|
||||
#endif
|
||||
|
||||
public ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
|
||||
public static Result FromSpan(out FspPath fspPath, ReadOnlySpan<byte> path)
|
||||
{
|
||||
Unsafe.SkipInit(out fspPath);
|
||||
|
||||
Span<byte> str = SpanHelpers.AsByteSpan(ref fspPath);
|
||||
|
||||
// Ensure null terminator even if the creation fails for safety
|
||||
str[0x301] = 0;
|
||||
|
||||
var sb = new U8StringBuilder(str);
|
||||
bool overflowed = sb.Append(path).Overflowed;
|
||||
|
||||
return overflowed ? ResultFs.TooLongPath.Log() : Result.Success;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator U8Span(in FspPath value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value));
|
||||
|
||||
public override string ToString() => StringUtils.Utf8ZToString(Str);
|
||||
}
|
||||
}
|
15
src/LibHac/FsSrv/Sf/IFileSystemProxyForLoader.cs
Normal file
15
src/LibHac/FsSrv/Sf/IFileSystemProxyForLoader.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.FsSrv.Sf
|
||||
{
|
||||
public interface IFileSystemProxyForLoader
|
||||
{
|
||||
Result OpenCodeFileSystem(out IFileSystem fileSystem, out CodeVerificationData verificationData,
|
||||
in FspPath path, ProgramId programId);
|
||||
|
||||
Result IsArchivedProgram(out bool isArchived, ulong processId);
|
||||
Result SetCurrentProcess(ulong processId);
|
||||
}
|
||||
}
|
14
src/LibHac/FsSrv/Sf/IProgramRegistry.cs
Normal file
14
src/LibHac/FsSrv/Sf/IProgramRegistry.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.FsSrv.Sf
|
||||
{
|
||||
public interface IProgramRegistry
|
||||
{
|
||||
Result RegisterProgram(ulong processId, ProgramId programId, StorageId storageId,
|
||||
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor);
|
||||
|
||||
Result UnregisterProgram(ulong processId);
|
||||
Result SetCurrentProcess(ulong processId);
|
||||
}
|
||||
}
|
@ -24,7 +24,10 @@ namespace LibHac.FsSystem
|
||||
|
||||
public static Result FromSpan(out FsPath fsPath, ReadOnlySpan<byte> path)
|
||||
{
|
||||
fsPath = new FsPath();
|
||||
Unsafe.SkipInit(out fsPath);
|
||||
|
||||
// Ensure null terminator even if the creation fails for safety
|
||||
fsPath.Str[0x301] = 0;
|
||||
|
||||
var sb = new U8StringBuilder(fsPath.Str);
|
||||
bool overflowed = sb.Append(path).Overflowed;
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using LibHac.Bcat;
|
||||
using LibHac.Diag;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Os;
|
||||
using LibHac.Sm;
|
||||
@ -8,26 +9,39 @@ namespace LibHac
|
||||
{
|
||||
public class Horizon
|
||||
{
|
||||
private const int InitialProcessCountMax = 0x50;
|
||||
internal ITimeSpanGenerator Time { get; }
|
||||
internal ServiceManager ServiceManager { get; }
|
||||
|
||||
// long instead of ulong because the ulong Interlocked.Increment overload
|
||||
// wasn't added until .NET 5
|
||||
private long _currentInitialProcessId;
|
||||
private long _currentProcessId;
|
||||
|
||||
public Horizon(ITimeSpanGenerator timer, FileSystemServerConfig fsServerConfig)
|
||||
{
|
||||
_currentProcessId = 0;
|
||||
_currentProcessId = InitialProcessCountMax;
|
||||
|
||||
Time = timer ?? new StopWatchTimeSpanGenerator();
|
||||
ServiceManager = new ServiceManager();
|
||||
|
||||
// ReSharper disable ObjectCreationAsStatement
|
||||
new FileSystemServer(CreateHorizonClient(), fsServerConfig);
|
||||
new FileSystemServer(CreatePrivilegedHorizonClient(), fsServerConfig);
|
||||
new BcatServer(CreateHorizonClient());
|
||||
// ReSharper restore ObjectCreationAsStatement
|
||||
}
|
||||
|
||||
public HorizonClient CreatePrivilegedHorizonClient()
|
||||
{
|
||||
ulong processId = (ulong)Interlocked.Increment(ref _currentInitialProcessId);
|
||||
|
||||
Abort.DoAbortUnless(processId <= InitialProcessCountMax, "Created too many privileged clients.");
|
||||
|
||||
// Todo: Register process with FS
|
||||
|
||||
return new HorizonClient(this, new ProcessId(processId));
|
||||
}
|
||||
|
||||
public HorizonClient CreateHorizonClient()
|
||||
{
|
||||
ulong processId = (ulong)Interlocked.Increment(ref _currentProcessId);
|
||||
|
Loading…
x
Reference in New Issue
Block a user