Update lr client and LocationResolverSet

This commit is contained in:
Alex Barney 2021-08-02 13:08:13 -07:00
parent 6db134cae4
commit 9a97e5ef3e
6 changed files with 286 additions and 172 deletions

View file

@ -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<SdkMutexType> 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();
}
}
/// <summary>
/// Manages resolving the location of NCAs via the <c>lr</c> service.
/// </summary>
/// <remarks>Based on FS 12.0.3 (nnSdk 12.3.1)</remarks>
internal class LocationResolverSet : IDisposable
{
private const int LocationResolverCount = 5;
// Todo: Use Optional<T>
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<SdkMutexType> 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<SdkMutexType> 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();
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using LibHac.Common;
using LibHac.Ncm;
using LibHac.Sf;
@ -8,9 +9,14 @@ namespace LibHac.Lr
{
private ReferenceCountedDisposable<IAddOnContentLocationResolver> _interface;
public AddOnContentLocationResolver(ReferenceCountedDisposable<IAddOnContentLocationResolver> baseInterface)
public AddOnContentLocationResolver(ref ReferenceCountedDisposable<IAddOnContentLocationResolver> 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();
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using LibHac.Common;
using LibHac.Ncm;
using LibHac.Sf;
@ -8,9 +9,14 @@ namespace LibHac.Lr
{
private ReferenceCountedDisposable<ILocationResolver> _interface;
public LocationResolver(ReferenceCountedDisposable<ILocationResolver> baseInterface)
public LocationResolver(ref ReferenceCountedDisposable<ILocationResolver> 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();
}
}
}

View file

@ -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<ILocationResolver> 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<IRegisteredLocationResolver> 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<IAddOnContentLocationResolver> 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();
}
}
}

104
src/LibHac/Lr/LrService.cs Normal file
View file

@ -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<SdkMutex> 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<ILocationResolver> 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<IRegisteredLocationResolver> 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<IAddOnContentLocationResolver> 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;
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using LibHac.Common;
using LibHac.Ncm;
namespace LibHac.Lr
@ -7,9 +8,14 @@ namespace LibHac.Lr
{
private ReferenceCountedDisposable<IRegisteredLocationResolver> _interface;
public RegisteredLocationResolver(ReferenceCountedDisposable<IRegisteredLocationResolver> baseInterface)
public RegisteredLocationResolver(ref ReferenceCountedDisposable<IRegisteredLocationResolver> 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<ProgramId> ids) =>
_interface.Target.RefreshExcluding(ids);
public void Dispose()
{
_interface?.Dispose();
}
}
}