mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add BIS shim functions with tests
This commit is contained in:
parent
405bbeff9e
commit
7bcb09b714
3 changed files with 283 additions and 3 deletions
178
src/LibHac/Fs/Shim/Bis.cs
Normal file
178
src/LibHac/Fs/Shim/Bis.cs
Normal file
|
@ -0,0 +1,178 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsService;
|
||||
using LibHac.FsSystem;
|
||||
using static LibHac.Fs.CommonMountNames;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class Bis
|
||||
{
|
||||
private class BisCommonMountNameGenerator : ICommonMountNameGenerator
|
||||
{
|
||||
private BisPartitionId PartitionId { get; }
|
||||
|
||||
public BisCommonMountNameGenerator(BisPartitionId partitionId)
|
||||
{
|
||||
PartitionId = partitionId;
|
||||
}
|
||||
|
||||
public Result GenerateCommonMountName(Span<byte> nameBuffer)
|
||||
{
|
||||
U8Span mountName = GetBisMountName(PartitionId);
|
||||
|
||||
// Add 2 for the mount name separator and null terminator
|
||||
// ReSharper disable once RedundantAssignment
|
||||
int requiredNameBufferSize = StringUtils.GetLength(mountName, PathTools.MountNameLengthMax) + 2;
|
||||
|
||||
Debug.Assert(nameBuffer.Length >= requiredNameBufferSize);
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
int size = new U8StringBuilder(nameBuffer).Append(mountName).Append(StringTraits.DriveSeparator).Length;
|
||||
Debug.Assert(size == requiredNameBufferSize - 1);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result MountBis(this FileSystemClient fs, U8Span mountName, BisPartitionId partitionId)
|
||||
{
|
||||
return MountBis(fs, mountName, partitionId, default);
|
||||
}
|
||||
|
||||
public static Result MountBis(this FileSystemClient fs, BisPartitionId partitionId, U8Span rootPath)
|
||||
{
|
||||
return MountBis(fs, GetBisMountName(partitionId), partitionId, rootPath);
|
||||
}
|
||||
|
||||
// nn::fs::detail::MountBis
|
||||
private static Result MountBis(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = MountBisImpl(fs, mountName, partitionId, rootPath);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
string logMessage = $", name: \"{mountName.ToString()}\", bispartitionid: {partitionId}, path: \"{rootPath.ToString()}\"";
|
||||
|
||||
fs.OutputAccessLog(rc, startTime, endTime, logMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MountBisImpl(fs, mountName, partitionId, rootPath);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
fs.EnableFileSystemAccessorAccessLog(mountName);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath)
|
||||
{
|
||||
Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FsPath sfPath;
|
||||
unsafe { _ = &sfPath; } // workaround for CS0165
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
// Nintendo doesn't use the provided rootPath
|
||||
sfPath.Str[0] = 0;
|
||||
|
||||
rc = fsProxy.OpenBisFileSystem(out IFileSystem fileSystem, ref sfPath, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
|
||||
|
||||
return fs.Register(mountName, fileSystem, nameGenerator);
|
||||
}
|
||||
|
||||
public static U8Span GetBisMountName(BisPartitionId partitionId)
|
||||
{
|
||||
switch (partitionId)
|
||||
{
|
||||
case BisPartitionId.BootPartition1Root:
|
||||
case BisPartitionId.BootPartition2Root:
|
||||
case BisPartitionId.UserDataRoot:
|
||||
case BisPartitionId.BootConfigAndPackage2Part1:
|
||||
case BisPartitionId.BootConfigAndPackage2Part2:
|
||||
case BisPartitionId.BootConfigAndPackage2Part3:
|
||||
case BisPartitionId.BootConfigAndPackage2Part4:
|
||||
case BisPartitionId.BootConfigAndPackage2Part5:
|
||||
case BisPartitionId.BootConfigAndPackage2Part6:
|
||||
case BisPartitionId.CalibrationBinary:
|
||||
throw new HorizonResultException(default, "The partition specified is not mountable.");
|
||||
|
||||
case BisPartitionId.CalibrationFile:
|
||||
return BisCalibrationFilePartitionMountName;
|
||||
case BisPartitionId.SafeMode:
|
||||
return BisSafeModePartitionMountName;
|
||||
case BisPartitionId.User:
|
||||
return BisUserPartitionMountName;
|
||||
case BisPartitionId.System:
|
||||
return BisSystemPartitionMountName;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(partitionId), partitionId, null);
|
||||
}
|
||||
}
|
||||
|
||||
// todo: Decide how to handle SetBisRootForHost since it allows mounting any directory on the user's computer
|
||||
public static Result SetBisRootForHost(this FileSystemClient fs, BisPartitionId partitionId, U8Span rootPath)
|
||||
{
|
||||
FsPath sfPath;
|
||||
unsafe { _ = &sfPath; } // workaround for CS0165
|
||||
|
||||
int pathLen = StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1);
|
||||
if (pathLen > PathTools.MaxPathLength)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
if (pathLen > 0)
|
||||
{
|
||||
byte endingSeparator = PathTool.IsSeparator(rootPath[pathLen - 1])
|
||||
? StringTraits.NullTerminator
|
||||
: StringTraits.DirectorySeparator;
|
||||
|
||||
Result rc = new U8StringBuilder(sfPath.Str).Append(rootPath).Append(endingSeparator).ToSfPath();
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
sfPath.Str[0] = StringTraits.NullTerminator;
|
||||
}
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
return fsProxy.SetBisRootForHost(partitionId, ref sfPath);
|
||||
}
|
||||
|
||||
public static Result OpenBisPartition(this FileSystemClient fs, out IStorage partitionStorage, BisPartitionId partitionId)
|
||||
{
|
||||
partitionStorage = default;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
Result rc = fsProxy.OpenBisStorage(out IStorage storage, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
partitionStorage = storage;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result InvalidateBisCache(this FileSystemClient fs)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
return fsProxy.InvalidateBisCache();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,9 +5,9 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
|
|||
{
|
||||
public static class FileSystemServerFactory
|
||||
{
|
||||
public static FileSystemServer CreateServer(bool sdCardInserted)
|
||||
public static FileSystemServer CreateServer(bool sdCardInserted, out IFileSystem rootFs)
|
||||
{
|
||||
var rootFs = new InMemoryFileSystem();
|
||||
rootFs = new InMemoryFileSystem();
|
||||
|
||||
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new Keyset());
|
||||
|
||||
|
@ -25,7 +25,14 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
|
|||
|
||||
public static FileSystemClient CreateClient(bool sdCardInserted)
|
||||
{
|
||||
FileSystemServer fsServer = CreateServer(sdCardInserted);
|
||||
FileSystemServer fsServer = CreateServer(sdCardInserted, out _);
|
||||
|
||||
return fsServer.CreateFileSystemClient();
|
||||
}
|
||||
|
||||
public static FileSystemClient CreateClient(out IFileSystem rootFs)
|
||||
{
|
||||
FileSystemServer fsServer = CreateServer(false, out rootFs);
|
||||
|
||||
return fsServer.CreateFileSystemClient();
|
||||
}
|
||||
|
|
95
tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/Bis.cs
Normal file
95
tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/Bis.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||
{
|
||||
public class Bis
|
||||
{
|
||||
[Fact]
|
||||
public void MountBis_MountCalibrationPartition_OpensCorrectDirectory()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
||||
|
||||
Assert.Success(fs.MountBis("calib".ToU8Span(), BisPartitionId.CalibrationFile));
|
||||
|
||||
// Create a file in the opened file system
|
||||
Assert.Success(fs.CreateFile("calib:/file".ToU8Span(), 0));
|
||||
|
||||
// Make sure the file exists on the root file system
|
||||
Assert.Success(rootFs.GetEntryType(out DirectoryEntryType type, "/bis/cal/file".ToU8Span()));
|
||||
Assert.Equal(DirectoryEntryType.File, type);
|
||||
}
|
||||
[Fact]
|
||||
public void MountBis_MountSafePartition_OpensCorrectDirectory()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
||||
|
||||
Assert.Success(fs.MountBis("safe".ToU8Span(), BisPartitionId.SafeMode));
|
||||
|
||||
// Create a file in the opened file system
|
||||
Assert.Success(fs.CreateFile("safe:/file".ToU8Span(), 0));
|
||||
|
||||
// Make sure the file exists on the root file system
|
||||
Assert.Success(rootFs.GetEntryType(out DirectoryEntryType type, "/bis/safe/file".ToU8Span()));
|
||||
Assert.Equal(DirectoryEntryType.File, type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountBis_MountSystemPartition_OpensCorrectDirectory()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
||||
|
||||
Assert.Success(fs.MountBis("system".ToU8Span(), BisPartitionId.System));
|
||||
|
||||
// Create a file in the opened file system
|
||||
Assert.Success(fs.CreateFile("system:/file".ToU8Span(), 0));
|
||||
|
||||
// Make sure the file exists on the root file system
|
||||
Assert.Success(rootFs.GetEntryType(out DirectoryEntryType type, "/bis/system/file".ToU8Span()));
|
||||
Assert.Equal(DirectoryEntryType.File, type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountBis_MountUserPartition_OpensCorrectDirectory()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
||||
|
||||
Assert.Success(fs.MountBis("user".ToU8Span(), BisPartitionId.User));
|
||||
|
||||
// Create a file in the opened file system
|
||||
Assert.Success(fs.CreateFile("user:/file".ToU8Span(), 0));
|
||||
|
||||
// Make sure the file exists on the root file system
|
||||
Assert.Success(rootFs.GetEntryType(out DirectoryEntryType type, "/bis/user/file".ToU8Span()));
|
||||
Assert.Equal(DirectoryEntryType.File, type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountBis_WithRootPath_IgnoresRootPath()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
||||
|
||||
Assert.Success(fs.MountBis(BisPartitionId.User, "/sub".ToU8Span()));
|
||||
|
||||
// Create a file in the opened file system
|
||||
Assert.Success(fs.CreateFile("@User:/file".ToU8Span(), 0));
|
||||
|
||||
// Make sure the file wasn't created in the sub path
|
||||
Assert.Result(ResultFs.PathNotFound, rootFs.GetEntryType(out _, "/bis/user/sub/file".ToU8Span()));
|
||||
|
||||
// Make sure the file was created in the main path
|
||||
Assert.Success(rootFs.GetEntryType(out DirectoryEntryType type, "/bis/user/file".ToU8Span()));
|
||||
Assert.Equal(DirectoryEntryType.File, type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountBis_InvalidPartition_ReturnsInvalidArgument()
|
||||
{
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
||||
|
||||
Assert.Result(ResultFs.InvalidArgument, fs.MountBis("boot1".ToU8Span(), BisPartitionId.BootPartition1Root));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue