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 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());
|
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new Keyset());
|
||||||
|
|
||||||
|
@ -25,7 +25,14 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
|
||||||
|
|
||||||
public static FileSystemClient CreateClient(bool sdCardInserted)
|
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();
|
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