From 9a97e5ef3e73d80c448fb64d9cafcd8f28b8f66a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 2 Aug 2021 13:08:13 -0700 Subject: [PATCH] Update lr client and LocationResolverSet --- src/LibHac/FsSrv/Impl/LocationResolverSet.cs | 201 ++++++++++++------ src/LibHac/Lr/AddOnContentLocationResolver.cs | 15 +- src/LibHac/Lr/LocationResolver.cs | 15 +- src/LibHac/Lr/LrClient.cs | 108 ++-------- src/LibHac/Lr/LrService.cs | 104 +++++++++ src/LibHac/Lr/RegisteredLocationResolver.cs | 15 +- 6 files changed, 286 insertions(+), 172 deletions(-) create mode 100644 src/LibHac/Lr/LrService.cs diff --git a/src/LibHac/FsSrv/Impl/LocationResolverSet.cs b/src/LibHac/FsSrv/Impl/LocationResolverSet.cs index 2c81d309..56855ab5 100644 --- a/src/LibHac/FsSrv/Impl/LocationResolverSet.cs +++ b/src/LibHac/FsSrv/Impl/LocationResolverSet.cs @@ -5,14 +5,44 @@ using LibHac.Fs; using LibHac.Lr; using LibHac.Ncm; using LibHac.Os; -using Path = LibHac.Lr.Path; namespace LibHac.FsSrv.Impl { + public static class LocationResolverSetGlobalMethods + { + public static void InitializeLocationResolverSet(this FileSystemServer fsSrv) + { + ref LocationResolverSetGlobals globals = ref fsSrv.Globals.LocationResolverSet; + using ScopedLock scopedLock = ScopedLock.Lock(ref globals.Mutex); + + if (!globals.IsLrInitialized) + { + fsSrv.Hos.Lr.Initialize(); + globals.IsLrInitialized = true; + } + } + } + + internal struct LocationResolverSetGlobals + { + public SdkMutexType Mutex; + public bool IsLrInitialized; + + public void Initialize() + { + Mutex.Initialize(); + } + } + + /// + /// Manages resolving the location of NCAs via the lr service. + /// + /// Based on FS 12.0.3 (nnSdk 12.3.1) internal class LocationResolverSet : IDisposable { private const int LocationResolverCount = 5; + // Todo: Use Optional private LocationResolver[] _resolvers; private AddOnContentLocationResolver _aocResolver; private SdkMutexType _mutex; @@ -27,16 +57,49 @@ namespace LibHac.FsSrv.Impl _fsServer = fsServer; } + public void Dispose() + { + foreach (LocationResolver resolver in _resolvers) + { + resolver?.Dispose(); + } + + _aocResolver?.Dispose(); + } + + private static Result SetUpFsPath(ref Fs.Path outPath, in Lr.Path lrPath) + { + var pathFlags = new PathFlags(); + pathFlags.AllowMountName(); + + if (Utility.IsHostFsMountName(lrPath.Str)) + pathFlags.AllowWindowsPath(); + + Result rc = outPath.InitializeWithReplaceUnc(lrPath.Str); + if (rc.IsFailure()) return rc; + + rc = outPath.Normalize(pathFlags); + if (rc.IsFailure()) return rc; + + return Result.Success; + } + private Result GetLocationResolver(out LocationResolver resolver, StorageId storageId) { UnsafeHelpers.SkipParamInit(out resolver); + _fsServer.InitializeLocationResolverSet(); + if (!IsValidStorageId(storageId)) return ResultLr.LocationResolverNotFound.Log(); using ScopedLock lk = ScopedLock.Lock(ref _mutex); int index = GetResolverIndexFromStorageId(storageId); + + if (index < 0) + return ResultLr.LocationResolverNotFound.Log(); + ref LocationResolver lr = ref _resolvers[index]; // Open the location resolver if it hasn't been already @@ -64,6 +127,8 @@ namespace LibHac.FsSrv.Impl private Result GetAddOnContentLocationResolver(out AddOnContentLocationResolver resolver) { + _fsServer.InitializeLocationResolverSet(); + using ScopedLock lk = ScopedLock.Lock(ref _mutex); if (_aocResolver is null) @@ -82,108 +147,126 @@ namespace LibHac.FsSrv.Impl return Result.Success; } - public Result ResolveApplicationControlPath(out Path path, Ncm.ApplicationId applicationId, StorageId storageId) + public Result ResolveApplicationControlPath(ref Fs.Path outPath, Ncm.ApplicationId applicationId, StorageId storageId) { - Path.InitEmpty(out path); + Result rc = GetLocationResolver(out LocationResolver resolver, storageId); + if (rc.IsFailure()) return rc; + + rc = resolver.ResolveApplicationControlPath(out Lr.Path path, applicationId); + if (rc.IsFailure()) return rc; + + return SetUpFsPath(ref outPath, in path); + } + + public Result ResolveApplicationHtmlDocumentPath(out bool isDirectory, ref Fs.Path outPath, Ncm.ApplicationId applicationId, StorageId storageId) + { + UnsafeHelpers.SkipParamInit(out isDirectory); Result rc = GetLocationResolver(out LocationResolver resolver, storageId); if (rc.IsFailure()) return rc; - rc = resolver.ResolveApplicationControlPath(out path, applicationId); + rc = resolver.ResolveApplicationHtmlDocumentPath(out Lr.Path path, applicationId); if (rc.IsFailure()) return rc; - PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/'); - return Result.Success; + isDirectory = PathUtility12.IsDirectoryPath(path.Str); + + return SetUpFsPath(ref outPath, in path); } - public Result ResolveApplicationHtmlDocumentPath(out Path path, Ncm.ApplicationId applicationId, StorageId storageId) + public Result ResolveProgramPath(out bool isDirectory, ref Fs.Path outPath, ProgramId programId, StorageId storageId) { - Path.InitEmpty(out path); + UnsafeHelpers.SkipParamInit(out isDirectory); Result rc = GetLocationResolver(out LocationResolver resolver, storageId); if (rc.IsFailure()) return rc; - rc = resolver.ResolveApplicationHtmlDocumentPath(out path, applicationId); + rc = resolver.ResolveProgramPath(out Lr.Path path, programId); if (rc.IsFailure()) return rc; - PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/'); - return Result.Success; + isDirectory = PathUtility12.IsDirectoryPath(path.Str); + + return SetUpFsPath(ref outPath, in path); } - public virtual Result ResolveProgramPath(out Path path, ulong id, StorageId storageId) + public Result ResolveRomPath(out bool isDirectory, ref Fs.Path outPath, ProgramId programId, StorageId storageId) { - Path.InitEmpty(out path); + UnsafeHelpers.SkipParamInit(out isDirectory); Result rc = GetLocationResolver(out LocationResolver resolver, storageId); if (rc.IsFailure()) return rc; - rc = resolver.ResolveProgramPath(out path, new ProgramId(id)); + rc = resolver.ResolveProgramPathForDebug(out Lr.Path path, programId); if (rc.IsFailure()) return rc; - PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/'); - return Result.Success; + isDirectory = PathUtility12.IsDirectoryPath(path.Str); + + return SetUpFsPath(ref outPath, in path); } - public Result ResolveRomPath(out Path path, ulong id, StorageId storageId) + public Result ResolveAddOnContentPath(ref Fs.Path outPath, DataId dataId) { - Path.InitEmpty(out path); - - Result rc = GetLocationResolver(out LocationResolver resolver, storageId); - if (rc.IsFailure()) return rc; - - rc = resolver.ResolveProgramPath(out path, new ProgramId(id)); - if (rc.IsFailure()) return rc; - - PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/'); - return Result.Success; - } - - public Result ResolveAddOnContentPath(out Path path, DataId dataId) - { - Path.InitEmpty(out path); - Result rc = GetAddOnContentLocationResolver(out AddOnContentLocationResolver resolver); if (rc.IsFailure()) return rc; - return resolver.ResolveAddOnContentPath(out path, dataId); + rc = resolver.ResolveAddOnContentPath(out Lr.Path path, dataId); + if (rc.IsFailure()) return rc; + + return SetUpFsPath(ref outPath, in path); } - public Result ResolveDataPath(out Path path, DataId dataId, StorageId storageId) + public Result ResolveDataPath(ref Fs.Path outPath, DataId dataId, StorageId storageId) { - Path.InitEmpty(out path); - if (storageId == StorageId.None) return ResultFs.InvalidAlignment.Log(); Result rc = GetLocationResolver(out LocationResolver resolver, storageId); if (rc.IsFailure()) return rc; - return resolver.ResolveDataPath(out path, dataId); - } - - public Result ResolveRegisteredProgramPath(out Path path, ulong id) - { - Path.InitEmpty(out path); - - Result rc = GetRegisteredLocationResolver(out RegisteredLocationResolver resolver); + rc = resolver.ResolveDataPath(out Lr.Path path, dataId); if (rc.IsFailure()) return rc; - using (resolver) + return SetUpFsPath(ref outPath, in path); + } + + public Result ResolveRegisteredProgramPath(ref Fs.Path outPath, ulong id) + { + _fsServer.InitializeLocationResolverSet(); + + RegisteredLocationResolver resolver = null; + try { - return resolver.ResolveProgramPath(out path, new ProgramId(id)); + Result rc = GetRegisteredLocationResolver(out resolver); + if (rc.IsFailure()) return rc; + + rc = resolver.ResolveProgramPath(out Lr.Path path, new ProgramId(id)); + if (rc.IsFailure()) return rc; + + return SetUpFsPath(ref outPath, in path); + } + finally + { + resolver?.Dispose(); } } - public Result ResolveRegisteredHtmlDocumentPath(out Path path, ulong id) + public Result ResolveRegisteredHtmlDocumentPath(ref Fs.Path outPath, ulong id) { - Path.InitEmpty(out path); + _fsServer.InitializeLocationResolverSet(); - Result rc = GetRegisteredLocationResolver(out RegisteredLocationResolver resolver); - if (rc.IsFailure()) return rc; - - using (resolver) + RegisteredLocationResolver resolver = null; + try { - return resolver.ResolveHtmlDocumentPath(out path, new ProgramId(id)); + Result rc = GetRegisteredLocationResolver(out resolver); + if (rc.IsFailure()) return rc; + + rc = resolver.ResolveHtmlDocumentPath(out Lr.Path path, new ProgramId(id)); + if (rc.IsFailure()) return rc; + + return SetUpFsPath(ref outPath, in path); + } + finally + { + resolver?.Dispose(); } } @@ -210,15 +293,5 @@ namespace LibHac.FsSrv.Impl _ => -1 }; } - - public void Dispose() - { - foreach (LocationResolver resolver in _resolvers) - { - resolver?.Dispose(); - } - - _aocResolver?.Dispose(); - } } } diff --git a/src/LibHac/Lr/AddOnContentLocationResolver.cs b/src/LibHac/Lr/AddOnContentLocationResolver.cs index fead88b6..7a6a163c 100644 --- a/src/LibHac/Lr/AddOnContentLocationResolver.cs +++ b/src/LibHac/Lr/AddOnContentLocationResolver.cs @@ -1,4 +1,5 @@ using System; +using LibHac.Common; using LibHac.Ncm; using LibHac.Sf; @@ -8,9 +9,14 @@ namespace LibHac.Lr { private ReferenceCountedDisposable _interface; - public AddOnContentLocationResolver(ReferenceCountedDisposable baseInterface) + public AddOnContentLocationResolver(ref ReferenceCountedDisposable baseInterface) { - _interface = baseInterface.AddReference(); + _interface = Shared.Move(ref baseInterface); + } + + public void Dispose() + { + _interface?.Dispose(); } public Result ResolveAddOnContentPath(out Path path, DataId id) => @@ -27,10 +33,5 @@ namespace LibHac.Lr public Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id) => _interface.Target.UnregisterApplicationAddOnContent(id); - - public void Dispose() - { - _interface?.Dispose(); - } } } diff --git a/src/LibHac/Lr/LocationResolver.cs b/src/LibHac/Lr/LocationResolver.cs index 5dce2624..1f1778b6 100644 --- a/src/LibHac/Lr/LocationResolver.cs +++ b/src/LibHac/Lr/LocationResolver.cs @@ -1,4 +1,5 @@ using System; +using LibHac.Common; using LibHac.Ncm; using LibHac.Sf; @@ -8,9 +9,14 @@ namespace LibHac.Lr { private ReferenceCountedDisposable _interface; - public LocationResolver(ReferenceCountedDisposable baseInterface) + public LocationResolver(ref ReferenceCountedDisposable baseInterface) { - _interface = baseInterface.AddReference(); + _interface = Shared.Move(ref baseInterface); + } + + public void Dispose() + { + _interface?.Dispose(); } public Result ResolveProgramPath(out Path path, ProgramId id) => @@ -72,10 +78,5 @@ namespace LibHac.Lr public Result EraseProgramRedirectionForDebug(ProgramId id) => _interface.Target.EraseProgramRedirectionForDebug(id); - - public void Dispose() - { - _interface?.Dispose(); - } } } diff --git a/src/LibHac/Lr/LrClient.cs b/src/LibHac/Lr/LrClient.cs index c27bdaaa..17e600e0 100644 --- a/src/LibHac/Lr/LrClient.cs +++ b/src/LibHac/Lr/LrClient.cs @@ -1,103 +1,37 @@ using System; -using LibHac.Common; -using LibHac.Ncm; namespace LibHac.Lr { public class LrClient : IDisposable { - private HorizonClient Hos { get; } - - private ILocationResolverManager LrManager { get; set; } - private readonly object _lrInitLocker = new object(); + internal LrClientGlobals Globals; + internal HorizonClient Hos => Globals.Hos; public LrClient(HorizonClient horizonClient) { - Hos = horizonClient; - } - - public Result OpenLocationResolver(out LocationResolver resolver, StorageId storageId) - { - UnsafeHelpers.SkipParamInit(out resolver); - EnsureInitialized(); - - Result rc = LrManager.OpenLocationResolver(out ReferenceCountedDisposable baseResolver, - storageId); - if (rc.IsFailure()) return rc; - - using (baseResolver) - { - resolver = new LocationResolver(baseResolver); - return Result.Success; - } - } - - public Result OpenRegisteredLocationResolver(out RegisteredLocationResolver resolver) - { - UnsafeHelpers.SkipParamInit(out resolver); - EnsureInitialized(); - - Result rc = LrManager.OpenRegisteredLocationResolver( - out ReferenceCountedDisposable baseResolver); - if (rc.IsFailure()) return rc; - - using (baseResolver) - { - resolver = new RegisteredLocationResolver(baseResolver); - return Result.Success; - } - } - - public Result OpenAddOnContentLocationResolver(out AddOnContentLocationResolver resolver) - { - UnsafeHelpers.SkipParamInit(out resolver); - EnsureInitialized(); - - Result rc = LrManager.OpenAddOnContentLocationResolver( - out ReferenceCountedDisposable baseResolver); - if (rc.IsFailure()) return rc; - - using (baseResolver) - { - resolver = new AddOnContentLocationResolver(baseResolver); - return Result.Success; - } - } - - public Result RefreshLocationResolver(StorageId storageId) - { - EnsureInitialized(); - - Result rc = LrManager.RefreshLocationResolver(storageId); - if (rc.IsFailure()) return rc; - - return Result.Success; - } - - private void EnsureInitialized() - { - if (LrManager != null) - return; - - lock (_lrInitLocker) - { - if (LrManager != null) - return; - - Result rc = Hos.Sm.GetService(out ILocationResolverManager manager, "lr"); - - if (rc.IsFailure()) - { - throw new HorizonResultException(rc, "Failed to initialize lr client."); - } - - LrManager = manager; - } + Globals.Initialize(this, horizonClient); } public void Dispose() { - LrManager?.Dispose(); + Globals.Dispose(); + } + } + + internal struct LrClientGlobals + { + public HorizonClient Hos; + public LrServiceGlobals LrService; + + public void Initialize(LrClient lrClient, HorizonClient horizonClient) + { + Hos = horizonClient; + LrService.Initialize(); + } + + public void Dispose() + { + LrService.Dispose(); } } } diff --git a/src/LibHac/Lr/LrService.cs b/src/LibHac/Lr/LrService.cs new file mode 100644 index 00000000..e7aeeccb --- /dev/null +++ b/src/LibHac/Lr/LrService.cs @@ -0,0 +1,104 @@ +using LibHac.Common; +using LibHac.Diag; +using LibHac.Ncm; +using LibHac.Os; + +namespace LibHac.Lr +{ + internal struct LrServiceGlobals + { + public ILocationResolverManager LocationResolver; + public SdkMutex InitializationMutex; + + public void Initialize() + { + LocationResolver = null; + InitializationMutex.Initialize(); + } + + public void Dispose() + { + if (LocationResolver is not null) + { + LocationResolver.Dispose(); + LocationResolver = null; + } + } + } + + public static class LrService + { + public static void Initialize(this LrClient lr) + { + ref LrServiceGlobals globals = ref lr.Globals.LrService; + Assert.SdkRequiresNotNull(globals.LocationResolver); + + // The lock over getting the service object is a LibHac addition. + using ScopedLock scopedLock = ScopedLock.Lock(ref lr.Globals.LrService.InitializationMutex); + + if (globals.LocationResolver is not null) + return; + + ILocationResolverManager serviceObject = lr.GetLocationResolverManagerServiceObject(); + globals.LocationResolver = serviceObject; + } + + public static Result OpenLocationResolver(this LrClient lr, out LocationResolver resolver, StorageId storageId) + { + UnsafeHelpers.SkipParamInit(out resolver); + + Result rc = lr.Globals.LrService.LocationResolver.OpenLocationResolver( + out ReferenceCountedDisposable baseResolver, storageId); + if (rc.IsFailure()) return rc; + + resolver = new LocationResolver(ref baseResolver); + return Result.Success; + } + + public static Result OpenRegisteredLocationResolver(this LrClient lr, out RegisteredLocationResolver resolver) + { + UnsafeHelpers.SkipParamInit(out resolver); + + Result rc = lr.Globals.LrService.LocationResolver.OpenRegisteredLocationResolver( + out ReferenceCountedDisposable baseResolver); + if (rc.IsFailure()) return rc; + + resolver = new RegisteredLocationResolver(ref baseResolver); + return Result.Success; + } + + public static Result OpenAddOnContentLocationResolver(this LrClient lr, out AddOnContentLocationResolver resolver) + { + UnsafeHelpers.SkipParamInit(out resolver); + + Result rc = lr.Globals.LrService.LocationResolver.OpenAddOnContentLocationResolver( + out ReferenceCountedDisposable baseResolver); + if (rc.IsFailure()) return rc; + + resolver = new AddOnContentLocationResolver(ref baseResolver); + return Result.Success; + } + + public static Result RefreshLocationResolver(this LrClient lr, StorageId storageId) + { + Result rc = lr.Globals.LrService.LocationResolver.RefreshLocationResolver(storageId); + if (rc.IsFailure()) return rc; + + return Result.Success; + } + + // Official lr puts this function along with memory allocation for + // lr IPC objects into a separate file, LocationResolverManagerFactory. + private static ILocationResolverManager GetLocationResolverManagerServiceObject(this LrClient lr) + { + Result rc = lr.Hos.Sm.GetService(out ILocationResolverManager manager, "lr"); + + if (rc.IsFailure()) + { + throw new HorizonResultException(rc, "Failed to get lr service object."); + } + + return manager; + } + } +} diff --git a/src/LibHac/Lr/RegisteredLocationResolver.cs b/src/LibHac/Lr/RegisteredLocationResolver.cs index 904b268a..6f4855d0 100644 --- a/src/LibHac/Lr/RegisteredLocationResolver.cs +++ b/src/LibHac/Lr/RegisteredLocationResolver.cs @@ -1,4 +1,5 @@ using System; +using LibHac.Common; using LibHac.Ncm; namespace LibHac.Lr @@ -7,9 +8,14 @@ namespace LibHac.Lr { private ReferenceCountedDisposable _interface; - public RegisteredLocationResolver(ReferenceCountedDisposable baseInterface) + public RegisteredLocationResolver(ref ReferenceCountedDisposable baseInterface) { - _interface = baseInterface.AddReference(); + _interface = Shared.Move(ref baseInterface); + } + + public void Dispose() + { + _interface?.Dispose(); } public Result ResolveProgramPath(out Path path, ProgramId id) => @@ -41,10 +47,5 @@ namespace LibHac.Lr public Result RefreshExcluding(ReadOnlySpan ids) => _interface.Target.RefreshExcluding(ids); - - public void Dispose() - { - _interface?.Dispose(); - } } }