diff --git a/src/LibHac/Bcat/BcatServer.cs b/src/LibHac/Bcat/BcatServer.cs index 1ddf0c96..20e72bf1 100644 --- a/src/LibHac/Bcat/BcatServer.cs +++ b/src/LibHac/Bcat/BcatServer.cs @@ -34,7 +34,7 @@ namespace LibHac.Bcat IServiceCreator service = GetServiceCreator(type); - Result rc = Hos.Sm.RegisterService(service, name); + Result rc = Hos.Sm.RegisterService(new BcatServiceObject(service), name); if (rc.IsFailure()) { throw new HorizonResultException(rc, "Abort"); diff --git a/src/LibHac/Bcat/Detail/Ipc/BcatServiceObject.cs b/src/LibHac/Bcat/Detail/Ipc/BcatServiceObject.cs new file mode 100644 index 00000000..cff3d6f7 --- /dev/null +++ b/src/LibHac/Bcat/Detail/Ipc/BcatServiceObject.cs @@ -0,0 +1,20 @@ +using LibHac.Sm; + +namespace LibHac.Bcat.Detail.Ipc +{ + internal class BcatServiceObject : IServiceObject + { + private IServiceCreator _serviceCreator; + + public BcatServiceObject(IServiceCreator serviceCreator) + { + _serviceCreator = serviceCreator; + } + + public Result GetServiceObject(out object serviceObject) + { + serviceObject = _serviceCreator; + return Result.Success; + } + } +} diff --git a/src/LibHac/Fs/FileSystemClient.cs b/src/LibHac/Fs/FileSystemClient.cs index 7b9b86d0..1f605a9b 100644 --- a/src/LibHac/Fs/FileSystemClient.cs +++ b/src/LibHac/Fs/FileSystemClient.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Accessors; using LibHac.Fs.Fsa; using LibHac.FsSrv; @@ -11,7 +12,7 @@ namespace LibHac.Fs { public partial class FileSystemClient { - private FileSystemServer FsSrv { get; } + private HorizonClient Hos { get; } private IFileSystemProxy FsProxy { get; set; } private readonly object _fspInitLocker = new object(); @@ -26,22 +27,17 @@ namespace LibHac.Fs Time = timer ?? new StopWatchTimeSpanGenerator(); } - public FileSystemClient(FileSystemServer fsServer, ITimeSpanGenerator timer) + public FileSystemClient(HorizonClient horizonClient) { - FsSrv = fsServer; - Time = timer ?? new StopWatchTimeSpanGenerator(); - } + Hos = horizonClient; + Time = horizonClient.Time; - internal FileSystemClient(FileSystemServer fsServer, IFileSystemProxy fsProxy, ITimeSpanGenerator timer) - { - FsSrv = fsServer; - FsProxy = fsProxy; - Time = timer ?? new StopWatchTimeSpanGenerator(); + Assert.NotNull(Time); } public bool HasFileSystemServer() { - return FsSrv != null; + return Hos != null; } public IFileSystemProxy GetFileSystemProxyServiceObject() @@ -57,8 +53,16 @@ namespace LibHac.Fs throw new InvalidOperationException("Client was not initialized with a server object."); } - FsProxy = FsSrv.CreateFileSystemProxyService(); + Result rc = Hos.Sm.GetService(out IFileSystemProxy fsProxy, "fsp-srv"); + if (rc.IsFailure()) + { + throw new HorizonResultException(rc, "Failed to create file system proxy service object."); + } + + fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult(); + + FsProxy = fsProxy; return FsProxy; } } diff --git a/src/LibHac/FsSrv/FileSystemProxy.cs b/src/LibHac/FsSrv/FileSystemProxy.cs index ede552e8..b38c46d0 100644 --- a/src/LibHac/FsSrv/FileSystemProxy.cs +++ b/src/LibHac/FsSrv/FileSystemProxy.cs @@ -15,7 +15,7 @@ namespace LibHac.FsSrv public class FileSystemProxy : IFileSystemProxy { private FileSystemProxyCore FsProxyCore { get; } - internal FileSystemServer FsServer { get; } + internal HorizonClient Hos { get; } public ulong CurrentProcess { get; private set; } @@ -24,10 +24,10 @@ namespace LibHac.FsSrv public FsPath SaveDataRootPath { get; } public bool AutoCreateSaveData { get; private set; } - internal FileSystemProxy(FileSystemProxyCore fsProxyCore, FileSystemServer fsServer) + internal FileSystemProxy(HorizonClient horizonClient, FileSystemProxyCore fsProxyCore) { FsProxyCore = fsProxyCore; - FsServer = fsServer; + Hos = horizonClient; CurrentProcess = ulong.MaxValue; SaveDataSize = 0x2000000; diff --git a/src/LibHac/FsSrv/FileSystemServer.cs b/src/LibHac/FsSrv/FileSystemServer.cs index 0a54e754..c07765d1 100644 --- a/src/LibHac/FsSrv/FileSystemServer.cs +++ b/src/LibHac/FsSrv/FileSystemServer.cs @@ -3,6 +3,7 @@ using LibHac.Fs; using LibHac.Fs.Impl; using LibHac.Fs.Shim; using LibHac.FsSrv.Creators; +using LibHac.Sm; namespace LibHac.FsSrv { @@ -13,15 +14,17 @@ namespace LibHac.FsSrv private FileSystemProxyCore FsProxyCore { get; } /// The client instance to be used for internal operations like save indexer access. - public FileSystemClient FsClient { get; } + public HorizonClient Hos { get; } + public bool IsDebugMode { get; } private ITimeSpanGenerator Timer { get; } /// - /// Creates a new . + /// Creates a new and registers its services using the provided HOS client. /// + /// The that will be used by this server. /// The configuration for the created . - public FileSystemServer(FileSystemServerConfig config) + public FileSystemServer(HorizonClient horizonClient, FileSystemServerConfig config) { if (config.FsCreators == null) throw new ArgumentException("FsCreators must not be null"); @@ -29,6 +32,8 @@ namespace LibHac.FsSrv if (config.DeviceOperator == null) throw new ArgumentException("DeviceOperator must not be null"); + Hos = horizonClient; + IsDebugMode = false; ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet(); @@ -41,17 +46,19 @@ namespace LibHac.FsSrv }; FsProxyCore = new FileSystemProxyCore(fspConfig, externalKeySet, config.DeviceOperator); - var fsProxy = new FileSystemProxy(FsProxyCore, this); - FsClient = new FileSystemClient(this, fsProxy, Timer); - // NS usually takes care of this - if (FsClient.IsSdCardInserted()) - FsClient.SetSdCardAccessibility(true); - - FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(FsClient, SaveIndexerId, + FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId, new ArrayPoolMemoryResource(), new SdHandleManager(), false)); + FileSystemProxy fsProxy = GetFileSystemProxyServiceObject(); + fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult(); fsProxy.CleanUpTemporaryStorage().IgnoreResult(); + + Hos.Sm.RegisterService(new FileSystemProxyService(this), "fsp-srv").IgnoreResult(); + + // NS usually takes care of this + if (Hos.Fs.IsSdCardInserted()) + Hos.Fs.SetSdCardAccessibility(true); } /// @@ -69,12 +76,35 @@ namespace LibHac.FsSrv /// The created . public FileSystemClient CreateFileSystemClient(ITimeSpanGenerator timer) { - return new FileSystemClient(this, timer); + return new FileSystemClient(Hos); } - public IFileSystemProxy CreateFileSystemProxyService() + private FileSystemProxy GetFileSystemProxyServiceObject() { - return new FileSystemProxy(FsProxyCore, this); + return new FileSystemProxy(Hos, FsProxyCore); + } + + internal bool IsCurrentProcess(ulong processId) + { + ulong currentId = Hos.Os.GetCurrentProcessId().Value; + + return processId == currentId; + } + + private class FileSystemProxyService : IServiceObject + { + private readonly FileSystemServer _server; + + public FileSystemProxyService(FileSystemServer server) + { + _server = server; + } + + public Result GetServiceObject(out object serviceObject) + { + serviceObject = _server.GetFileSystemProxyServiceObject(); + return Result.Success; + } } } diff --git a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs index db2827b0..ea44f8e1 100644 --- a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs +++ b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs @@ -109,7 +109,7 @@ namespace LibHac.FsSrv.Impl return rc; } - rc = FsProxy.FsServer.FsClient.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, + rc = FsProxy.Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, SaveDataFlags.None); if (rc.IsFailure()) return rc; } diff --git a/src/LibHac/FsSrv/Impl/ProgramRegistryManager.cs b/src/LibHac/FsSrv/Impl/ProgramRegistryManager.cs index df866f7e..1f9c5ebf 100644 --- a/src/LibHac/FsSrv/Impl/ProgramRegistryManager.cs +++ b/src/LibHac/FsSrv/Impl/ProgramRegistryManager.cs @@ -183,12 +183,6 @@ namespace LibHac.FsSrv.Impl return initialProcessIdLowerBound >= processId && processId <= initialProcessIdUpperBound; } - public static bool IsCurrentProcess(ulong processId) - { - // Todo: Don't use hardcoded value - return true; - } - internal static ProgramInfo CreateProgramInfoForInitialProcess(FileSystemServer fsServer) { return new ProgramInfo(fsServer, InitialProcessAccessControlDataHeader, diff --git a/src/LibHac/Horizon.cs b/src/LibHac/Horizon.cs index 2f6509cf..85c5f110 100644 --- a/src/LibHac/Horizon.cs +++ b/src/LibHac/Horizon.cs @@ -1,7 +1,7 @@ -using LibHac.Common; -using LibHac.Fs; +using System.Threading; +using LibHac.Bcat; using LibHac.FsSrv; -using LibHac.FsSrv.Creators; +using LibHac.Os; using LibHac.Sm; namespace LibHac @@ -9,63 +9,32 @@ namespace LibHac public class Horizon { internal ITimeSpanGenerator Time { get; } - private FileSystemServer FileSystemServer { get; set; } internal ServiceManager ServiceManager { get; } - private readonly object _initLocker = new object(); + // long instead of ulong because the ulong Interlocked.Increment overload + // wasn't added until .NET 5 + private long _currentProcessId; - public Horizon(ITimeSpanGenerator timer) + public Horizon(ITimeSpanGenerator timer, FileSystemServerConfig fsServerConfig) { + _currentProcessId = 0; + Time = timer ?? new StopWatchTimeSpanGenerator(); - ServiceManager = new ServiceManager(this); + ServiceManager = new ServiceManager(); + + // ReSharper disable ObjectCreationAsStatement + new FileSystemServer(CreateHorizonClient(), fsServerConfig); + new BcatServer(CreateHorizonClient()); + // ReSharper restore ObjectCreationAsStatement } - public Horizon(ITimeSpanGenerator timer, FileSystemServer fsServer) + public HorizonClient CreateHorizonClient() { - Time = timer ?? new StopWatchTimeSpanGenerator(); - FileSystemServer = fsServer; - ServiceManager = new ServiceManager(this); - } + ulong processId = (ulong)Interlocked.Increment(ref _currentProcessId); - private Result OpenFileSystemClient(out FileSystemClient client) - { - if (FileSystemServer is null) - { - client = default; - return ResultLibHac.ServiceNotInitialized.Log(); - } + // Todo: Register process with FS - client = FileSystemServer.CreateFileSystemClient(); - return Result.Success; - } - - public Result CreateHorizonClient(out HorizonClient client) - { - Result rc = OpenFileSystemClient(out FileSystemClient fsClient); - if (rc.IsFailure()) - { - client = default; - return rc; - } - - client = new HorizonClient(this, fsClient); - return Result.Success; - } - - public void InitializeFileSystemServer(FileSystemCreators fsCreators, IDeviceOperator deviceOperator) - { - if (FileSystemServer != null) return; - - lock (_initLocker) - { - if (FileSystemServer != null) return; - - var config = new FileSystemServerConfig(); - config.FsCreators = fsCreators; - config.DeviceOperator = deviceOperator; - - FileSystemServer = new FileSystemServer(config); - } + return new HorizonClient(this, new ProcessId(processId)); } } } diff --git a/src/LibHac/HorizonClient.cs b/src/LibHac/HorizonClient.cs index 0801e6d6..029c698f 100644 --- a/src/LibHac/HorizonClient.cs +++ b/src/LibHac/HorizonClient.cs @@ -1,6 +1,7 @@ using System; using LibHac.Arp; using LibHac.Fs; +using LibHac.Os; using LibHac.Sm; namespace LibHac @@ -9,19 +10,25 @@ namespace LibHac { // ReSharper disable once UnusedAutoPropertyAccessor.Local private Horizon Horizon { get; } + internal ProcessId ProcessId { get; } private Lazy ArpLazy { get; } public FileSystemClient Fs { get; } public ServiceManagerClient Sm { get; } + public OsClient Os { get; } public ArpClient Arp => ArpLazy.Value; - internal HorizonClient(Horizon horizon, FileSystemClient fsClient) + public ITimeSpanGenerator Time => Horizon.Time; + + internal HorizonClient(Horizon horizon, ProcessId processId) { Horizon = horizon; + ProcessId = processId; - Fs = fsClient; + Fs = new FileSystemClient(this); Sm = new ServiceManagerClient(horizon.ServiceManager); + Os = new OsClient(this); ArpLazy = new Lazy(InitArpClient, true); } diff --git a/src/LibHac/Os/OsClient.cs b/src/LibHac/Os/OsClient.cs new file mode 100644 index 00000000..ec9b6e0e --- /dev/null +++ b/src/LibHac/Os/OsClient.cs @@ -0,0 +1,17 @@ +namespace LibHac.Os +{ + public class OsClient + { + private HorizonClient Hos { get; } + + internal OsClient(HorizonClient horizonClient) + { + Hos = horizonClient; + } + + public ProcessId GetCurrentProcessId() + { + return Hos.ProcessId; + } + } +} diff --git a/src/LibHac/Os/ProcessId.cs b/src/LibHac/Os/ProcessId.cs new file mode 100644 index 00000000..6f77a0d4 --- /dev/null +++ b/src/LibHac/Os/ProcessId.cs @@ -0,0 +1,11 @@ +namespace LibHac.Os +{ + public readonly struct ProcessId + { + public static ProcessId InvalidId => new ProcessId(ulong.MaxValue); + + public readonly ulong Value; + + public ProcessId(ulong value) => Value = value; + } +} diff --git a/src/LibHac/Sm/IServiceObject.cs b/src/LibHac/Sm/IServiceObject.cs new file mode 100644 index 00000000..34cf7969 --- /dev/null +++ b/src/LibHac/Sm/IServiceObject.cs @@ -0,0 +1,9 @@ +namespace LibHac.Sm +{ + // This interface is being used as a stop-gap solution so we can + // have at least some sort of service system for now + public interface IServiceObject + { + Result GetServiceObject(out object serviceObject); + } +} diff --git a/src/LibHac/Sm/ServiceManager.cs b/src/LibHac/Sm/ServiceManager.cs index 0ecd38f4..2bd5ae31 100644 --- a/src/LibHac/Sm/ServiceManager.cs +++ b/src/LibHac/Sm/ServiceManager.cs @@ -11,14 +11,7 @@ namespace LibHac.Sm // isn't blocked waiting for something better. internal class ServiceManager { - // ReSharper disable once UnusedAutoPropertyAccessor.Local - private Horizon Horizon { get; } - private Dictionary Services { get; } = new Dictionary(); - - public ServiceManager(Horizon horizon) - { - Horizon = horizon; - } + private Dictionary Services { get; } = new Dictionary(); internal Result GetService(out object serviceObject, ServiceName serviceName) { @@ -27,20 +20,20 @@ namespace LibHac.Sm Result rc = ValidateServiceName(serviceName); if (rc.IsFailure()) return rc; - if (!Services.TryGetValue(serviceName, out serviceObject)) + if (!Services.TryGetValue(serviceName, out IServiceObject service)) { return ResultSf.RequestDeferredByUser.Log(); } - return Result.Success; + return service.GetServiceObject(out serviceObject); } - internal Result RegisterService(object serviceObject, ServiceName serviceName) + internal Result RegisterService(IServiceObject service, ServiceName serviceName) { Result rc = ValidateServiceName(serviceName); if (rc.IsFailure()) return rc; - if (!Services.TryAdd(serviceName, serviceObject)) + if (!Services.TryAdd(serviceName, service)) { return ResultSm.AlreadyRegistered.Log(); } @@ -53,11 +46,12 @@ namespace LibHac.Sm Result rc = ValidateServiceName(serviceName); if (rc.IsFailure()) return rc; - if (!Services.Remove(serviceName, out object service)) + if (!Services.Remove(serviceName, out IServiceObject service)) { return ResultSm.NotRegistered.Log(); } + // ReSharper disable once SuspiciousTypeConversion.Global if (service is IDisposable disposable) { disposable.Dispose(); diff --git a/src/LibHac/Sm/ServiceManagerClient.cs b/src/LibHac/Sm/ServiceManagerClient.cs index 0930e6ed..b5f77934 100644 --- a/src/LibHac/Sm/ServiceManagerClient.cs +++ b/src/LibHac/Sm/ServiceManagerClient.cs @@ -29,7 +29,7 @@ namespace LibHac.Sm throw new InvalidCastException("The service object is not of the specified type."); } - public Result RegisterService(object serviceObject, ReadOnlySpan name) + public Result RegisterService(IServiceObject serviceObject, ReadOnlySpan name) { return Server.RegisterService(serviceObject, ServiceName.Encode(name)); } diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs index ee815fc5..7f4c8135 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs @@ -6,7 +6,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests { public static class FileSystemServerFactory { - public static FileSystemServer CreateServer(bool sdCardInserted, out IFileSystem rootFs) + private static FileSystemClient CreateClientImpl(bool sdCardInserted, out IFileSystem rootFs) { rootFs = new InMemoryFileSystem(); @@ -19,23 +19,21 @@ namespace LibHac.Tests.Fs.FileSystemClientTests config.DeviceOperator = defaultObjects.DeviceOperator; config.ExternalKeySet = new ExternalKeySet(); - var fsServer = new FileSystemServer(config); + var horizon = new Horizon(new StopWatchTimeSpanGenerator(), config); - return fsServer; + HorizonClient horizonClient = horizon.CreateHorizonClient(); + + return horizonClient.Fs; } public static FileSystemClient CreateClient(bool sdCardInserted) { - FileSystemServer fsServer = CreateServer(sdCardInserted, out _); - - return fsServer.CreateFileSystemClient(); + return CreateClientImpl(sdCardInserted, out _); } public static FileSystemClient CreateClient(out IFileSystem rootFs) { - FileSystemServer fsServer = CreateServer(false, out rootFs); - - return fsServer.CreateFileSystemClient(); + return CreateClientImpl(false, out rootFs); } } }