Add IFileSystemProxyForLoader and IProgramRegistry interfaces

This commit is contained in:
Alex Barney 2020-08-23 19:33:25 -07:00
parent 071b608f5f
commit 3184e6ca7e
14 changed files with 342 additions and 10 deletions

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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