Add RegisterProgramIndexMapInfo shim and tests

This commit is contained in:
Alex Barney 2020-08-25 16:50:20 -07:00
parent 6496a2c1bc
commit 1f25b87d20
4 changed files with 249 additions and 1 deletions

View File

@ -0,0 +1,29 @@
using System;
using System.Runtime.InteropServices;
using LibHac.FsSrv;
namespace LibHac.Fs.Shim
{
public static class ProgramIndexMapInfoShim
{
/// <summary>
/// Unregisters any previously registered program index map info and registers the provided map info.
/// </summary>
/// <param name="fs">The <see cref="FileSystemClient"/> to use.</param>
/// <param name="mapInfo">The program index map info entries to register.</param>
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
/// <see cref="ResultFs.PermissionDenied"/>: Insufficient permissions.</returns>
public static Result RegisterProgramIndexMapInfo(this FileSystemClient fs,
ReadOnlySpan<ProgramIndexMapInfo> mapInfo)
{
if (mapInfo.IsEmpty)
return Result.Success;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
ReadOnlySpan<byte> mapInfoBuffer = MemoryMarshal.Cast<ProgramIndexMapInfo, byte>(mapInfo);
return fsProxy.RegisterProgramIndexMapInfo(mapInfoBuffer, mapInfo.Length);
}
}
}

View File

@ -512,7 +512,7 @@ namespace LibHac.FsSrv.Impl
public bool CanWrite => ((_value >> 1) & 1) == 1;
}
internal readonly struct AccessControlBits
public readonly struct AccessControlBits
{
public readonly ulong Value;

View File

@ -1,7 +1,11 @@
using System.Threading;
using LibHac.Bcat;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Shim;
using LibHac.FsSrv;
using LibHac.FsSrv.Impl;
using LibHac.Ncm;
using LibHac.Os;
using LibHac.Sm;
@ -12,6 +16,7 @@ namespace LibHac
private const int InitialProcessCountMax = 0x50;
internal ITimeSpanGenerator Time { get; }
internal ServiceManager ServiceManager { get; }
private HorizonClient LoaderClient { get; }
// long instead of ulong because the ulong Interlocked.Increment overload
// wasn't added until .NET 5
@ -29,6 +34,8 @@ namespace LibHac
new FileSystemServer(CreatePrivilegedHorizonClient(), fsServerConfig);
new BcatServer(CreateHorizonClient());
// ReSharper restore ObjectCreationAsStatement
LoaderClient = CreatePrivilegedHorizonClient();
}
public HorizonClient CreatePrivilegedHorizonClient()
@ -50,5 +57,25 @@ namespace LibHac
return new HorizonClient(this, new ProcessId(processId));
}
public HorizonClient CreateHorizonClient(ProgramId programId, AccessControlBits.Bits fsPermissions)
{
HorizonClient client = CreateHorizonClient();
var dataHeader = new AccessControlDataHeader();
var descriptor = new AccessControlDescriptor();
descriptor.Version = 1;
dataHeader.Version = 1;
descriptor.AccessFlags = (ulong)fsPermissions;
dataHeader.AccessFlags = (ulong)fsPermissions;
LoaderClient.Fs.RegisterProgram(client.ProcessId.Value, programId, StorageId.BuiltInUser,
SpanHelpers.AsReadOnlyByteSpan(in dataHeader), SpanHelpers.AsReadOnlyByteSpan(in descriptor))
.ThrowIfFailure();
return client;
}
}
}

View File

@ -0,0 +1,192 @@
using System;
using LibHac.Fs;
using LibHac.Fs.Shim;
using LibHac.FsSrv;
using LibHac.FsSrv.Impl;
using LibHac.Ncm;
using Xunit;
namespace LibHac.Tests.FsSrv
{
public class ProgramIndexMapInfoTests
{
[Fact]
public void GetProgramIndexForAccessLog_IsMultiProgram_ReturnsCorrectIndex()
{
const int count = 7;
Horizon hos = HorizonFactory.CreateBasicHorizon();
var programs = new HorizonClient[count];
programs[0] = hos.CreateHorizonClient(new ProgramId(1), AccessControlBits.Bits.RegisterProgramIndexMapInfo);
for (int i = 1; i < programs.Length; i++)
{
programs[i] = hos.CreateHorizonClient(new ProgramId((ulong)(i + 1)), AccessControlBits.Bits.None);
}
var map = new ProgramIndexMapInfo[count];
for (int i = 0; i < map.Length; i++)
{
map[i].MainProgramId = new ProgramId(1);
map[i].ProgramId = new ProgramId((ulong)(i + 1));
map[i].ProgramIndex = (byte)i;
}
Assert.Success(programs[0].Fs.RegisterProgramIndexMapInfo(map));
for (int i = 0; i < programs.Length; i++)
{
Assert.Success(programs[i].Fs.GetFileSystemProxyServiceObject()
.GetProgramIndexForAccessLog(out int programIndex, out int programCount));
Assert.Equal(i, programIndex);
Assert.Equal(count, programCount);
}
}
private ProgramIndexMapInfoManager CreatePopulatedManager(int count, Func<int, long> idCreator)
{
var manager = new ProgramIndexMapInfoManager();
var map = new ProgramIndexMapInfo[count];
for (int i = 0; i < map.Length; i++)
{
map[i].MainProgramId = new ProgramId((ulong)idCreator(0));
map[i].ProgramId = new ProgramId((ulong)idCreator(i));
map[i].ProgramIndex = (byte)i;
}
Assert.Success(manager.Reset(map));
return manager;
}
[Fact]
public void Get_IdDoesNotExist_ReturnsNull()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => x + 3);
Assert.Null(manager.Get(new ProgramId(0)));
Assert.Null(manager.Get(new ProgramId(2)));
Assert.Null(manager.Get(new ProgramId(8)));
Assert.Null(manager.Get(new ProgramId(9001)));
}
[Fact]
public void Get_IdExists_ReturnsCorrectMapInfo()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => x + 1);
// ReSharper disable PossibleInvalidOperationException
ProgramIndexMapInfo? map = manager.Get(new ProgramId(1));
Assert.NotNull(map);
Assert.Equal(new ProgramId(1), map.Value.MainProgramId);
Assert.Equal(new ProgramId(1), map.Value.ProgramId);
Assert.Equal(0, map.Value.ProgramIndex);
map = manager.Get(new ProgramId(4));
Assert.NotNull(map);
Assert.Equal(new ProgramId(1), map.Value.MainProgramId);
Assert.Equal(new ProgramId(4), map.Value.ProgramId);
Assert.Equal(3, map.Value.ProgramIndex);
// ReSharper restore PossibleInvalidOperationException
}
[Fact]
public void GetProgramId_WithIndex_ReturnsProgramIdOfSpecifiedIndex()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => (x + 1) * 5);
// Check that the program ID can be retrieved using any program in the set
Assert.Equal(new ProgramId(20), manager.GetProgramId(new ProgramId(5), 3));
Assert.Equal(new ProgramId(20), manager.GetProgramId(new ProgramId(10), 3));
Assert.Equal(new ProgramId(20), manager.GetProgramId(new ProgramId(15), 3));
Assert.Equal(new ProgramId(20), manager.GetProgramId(new ProgramId(20), 3));
Assert.Equal(new ProgramId(20), manager.GetProgramId(new ProgramId(25), 3));
// Check that any index can be used
Assert.Equal(new ProgramId(5), manager.GetProgramId(new ProgramId(10), 0));
Assert.Equal(new ProgramId(10), manager.GetProgramId(new ProgramId(10), 1));
Assert.Equal(new ProgramId(15), manager.GetProgramId(new ProgramId(10), 2));
Assert.Equal(new ProgramId(20), manager.GetProgramId(new ProgramId(10), 3));
Assert.Equal(new ProgramId(25), manager.GetProgramId(new ProgramId(10), 4));
}
[Fact]
public void GetProgramId_InputIdDoesNotExist_ReturnsInvalidId()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => x + 1);
Assert.Equal(ProgramId.InvalidId, manager.GetProgramId(new ProgramId(0), 3));
Assert.Equal(ProgramId.InvalidId, manager.GetProgramId(new ProgramId(666), 3));
Assert.Equal(ProgramId.InvalidId, manager.GetProgramId(new ProgramId(777), 3));
}
[Fact]
public void GetProgramId_InputIndexDoesNotExist_ReturnsInvalidId()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => x + 1);
Assert.Equal(ProgramId.InvalidId, manager.GetProgramId(new ProgramId(2), 5));
Assert.Equal(ProgramId.InvalidId, manager.GetProgramId(new ProgramId(2), 12));
Assert.Equal(ProgramId.InvalidId, manager.GetProgramId(new ProgramId(2), 255));
}
[Fact]
public void GetProgramCount_MapIsEmpty_ReturnsZero()
{
var manager = new ProgramIndexMapInfoManager();
Assert.Equal(0, manager.GetProgramCount());
}
[Fact]
public void GetProgramCount_MapHasEntries_ReturnsCorrectCount()
{
ProgramIndexMapInfoManager manager = CreatePopulatedManager(1, x => x + 1);
Assert.Equal(1, manager.GetProgramCount());
manager = CreatePopulatedManager(10, x => x + 1);
Assert.Equal(10, manager.GetProgramCount());
manager = CreatePopulatedManager(255, x => x + 1);
Assert.Equal(255, manager.GetProgramCount());
}
[Fact]
public void Clear_MapHasEntries_CountIsZero()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => x + 1);
manager.Clear();
Assert.Equal(0, manager.GetProgramCount());
}
[Fact]
public void Clear_MapHasEntries_CannotGetOldEntry()
{
const int count = 5;
ProgramIndexMapInfoManager manager = CreatePopulatedManager(count, x => x + 1);
manager.Clear();
Assert.Null(manager.Get(new ProgramId(2)));
}
}
}