From 036e04820825f05ac6e0dfeb00669ad25e4464af Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 10 Mar 2020 15:07:06 -0700 Subject: [PATCH] Finish OpenFileSystemWithId enough to work with most content --- build/CodeGen/results.csv | 2 + src/LibHac/Fs/FileStorage2.cs | 4 +- src/LibHac/Fs/FileStorageBasedFileSystem.cs | 5 +- src/LibHac/Fs/ResultFs.cs | 4 + .../Creators/RomFileSystemCreator.cs | 8 +- .../FsService/Creators/StorageOnNcaCreator.cs | 51 ++++++- .../FsService/DefaultFsServerObjects.cs | 2 +- src/LibHac/FsService/FileSystemProxy.cs | 19 ++- src/LibHac/FsService/FileSystemProxyCore.cs | 142 +++++++++++++++++- 9 files changed, 219 insertions(+), 18 deletions(-) diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index 6eab7333..992e05a0 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -81,6 +81,8 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,4464,,AllocationTableIteratedRangeEntry, 2,4501,4599,NcaCorrupted, +2,4512,,InvalidNcaFsType, +2,4527,,InvalidNcaProgramId, 2,4601,4639,IntegrityVerificationStorageCorrupted, 2,4602,,InvalidIvfcMagic, diff --git a/src/LibHac/Fs/FileStorage2.cs b/src/LibHac/Fs/FileStorage2.cs index 75f665e3..1d242583 100644 --- a/src/LibHac/Fs/FileStorage2.cs +++ b/src/LibHac/Fs/FileStorage2.cs @@ -6,10 +6,10 @@ namespace LibHac.Fs { public class FileStorage2 : StorageBase { - private const long InvalidSize = -1; + protected const long InvalidSize = -1; private IFile BaseFile { get; set; } - private long FileSize { get; set; } + protected long FileSize { get; set; } public FileStorage2(IFile baseFile) { diff --git a/src/LibHac/Fs/FileStorageBasedFileSystem.cs b/src/LibHac/Fs/FileStorageBasedFileSystem.cs index cc27d1bb..ce43fa53 100644 --- a/src/LibHac/Fs/FileStorageBasedFileSystem.cs +++ b/src/LibHac/Fs/FileStorageBasedFileSystem.cs @@ -9,7 +9,10 @@ namespace LibHac.Fs private IFileSystem BaseFileSystem { get; set; } private IFile BaseFile { get; set; } - private FileStorageBasedFileSystem() { } + private FileStorageBasedFileSystem() + { + FileSize = InvalidSize; + } public static Result CreateNew(out FileStorageBasedFileSystem created, IFileSystem baseFileSystem, U8Span path, OpenMode mode) diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index 2b7e7a45..cb4f4bd0 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -171,6 +171,10 @@ namespace LibHac.Fs /// Error code: 2002-4501; Range: 4501-4599; Inner value: 0x232a02 public static Result.Base NcaCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4501, 4599); } + /// Error code: 2002-4512; Inner value: 0x234002 + public static Result.Base InvalidNcaFsType => new Result.Base(ModuleFs, 4512); + /// Error code: 2002-4527; Inner value: 0x235e02 + public static Result.Base InvalidNcaProgramId => new Result.Base(ModuleFs, 4527); /// Error code: 2002-4601; Range: 4601-4639; Inner value: 0x23f202 public static Result.Base IntegrityVerificationStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4601, 4639); } diff --git a/src/LibHac/FsService/Creators/RomFileSystemCreator.cs b/src/LibHac/FsService/Creators/RomFileSystemCreator.cs index 8994e8e4..a1309b51 100644 --- a/src/LibHac/FsService/Creators/RomFileSystemCreator.cs +++ b/src/LibHac/FsService/Creators/RomFileSystemCreator.cs @@ -1,13 +1,15 @@ -using System; -using LibHac.Fs; +using LibHac.Fs; +using LibHac.FsSystem.RomFs; namespace LibHac.FsService.Creators { public class RomFileSystemCreator : IRomFileSystemCreator { + // todo: Implement properly public Result Create(out IFileSystem fileSystem, IStorage romFsStorage) { - throw new NotImplementedException(); + fileSystem = new RomFsFileSystem(romFsStorage); + return Result.Success; } } } diff --git a/src/LibHac/FsService/Creators/StorageOnNcaCreator.cs b/src/LibHac/FsService/Creators/StorageOnNcaCreator.cs index e651289b..bf90ce1e 100644 --- a/src/LibHac/FsService/Creators/StorageOnNcaCreator.cs +++ b/src/LibHac/FsService/Creators/StorageOnNcaCreator.cs @@ -1,14 +1,46 @@ using System; using LibHac.Fs; +using LibHac.FsSystem; +using LibHac.FsSystem.Detail; using LibHac.FsSystem.NcaUtils; namespace LibHac.FsService.Creators { public class StorageOnNcaCreator : IStorageOnNcaCreator { + private bool IsEnabledProgramVerification { get; set; } + private Keyset Keyset { get; } + + public StorageOnNcaCreator(Keyset keyset) + { + Keyset = keyset; + } + + // todo: Implement NcaReader and other Nca classes public Result Create(out IStorage storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs) { - throw new NotImplementedException(); + storage = default; + fsHeader = default; + + Result rc = OpenStorage(out IStorage storageTemp, nca, fsIndex); + if (rc.IsFailure()) return rc; + + if (isCodeFs) + { + using (var codeFs = new PartitionFileSystemCore()) + { + rc = codeFs.Initialize(storageTemp); + if (rc.IsFailure()) return rc; + + rc = VerifyAcidSignature(codeFs, nca); + if (rc.IsFailure()) return rc; + } + } + + storage = storageTemp; + fsHeader = nca.Header.GetFsHeader(fsIndex); + + return Result.Success; } public Result CreateWithPatch(out IStorage storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs) @@ -18,12 +50,25 @@ namespace LibHac.FsService.Creators public Result OpenNca(out Nca nca, IStorage ncaStorage) { - throw new NotImplementedException(); + nca = new Nca(Keyset, ncaStorage); + return Result.Success; } public Result VerifyAcidSignature(IFileSystem codeFileSystem, Nca nca) { - throw new NotImplementedException(); + // todo + return Result.Success; + } + + private Result OpenStorage(out IStorage storage, Nca nca, int fsIndex) + { + storage = default; + + if (!nca.SectionExists(fsIndex)) + return ResultFs.PartitionNotFound.Log(); + + storage = nca.OpenStorage(fsIndex, IntegrityCheckLevel.ErrorOnInvalid); + return Result.Success; } } } diff --git a/src/LibHac/FsService/DefaultFsServerObjects.cs b/src/LibHac/FsService/DefaultFsServerObjects.cs index 86732891..c71c406a 100644 --- a/src/LibHac/FsService/DefaultFsServerObjects.cs +++ b/src/LibHac/FsService/DefaultFsServerObjects.cs @@ -20,7 +20,7 @@ namespace LibHac.FsService creators.RomFileSystemCreator = new RomFileSystemCreator(); creators.PartitionFileSystemCreator = new PartitionFileSystemCreator(); - creators.StorageOnNcaCreator = new StorageOnNcaCreator(); + creators.StorageOnNcaCreator = new StorageOnNcaCreator(keyset); creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator(); creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator(); creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keyset); diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index cb9b6047..ed5e493a 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -40,11 +40,22 @@ namespace LibHac.FsService bool canMountSystemDataPrivate = false; - Result rc = PathTools.Normalize(out U8Span normalizedPath, path); - if (rc.IsFailure()) return rc; - + var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path)); + if (normalizer.Result.IsFailure()) return normalizer.Result; + // ReSharper disable once ConditionIsAlwaysTrueOrFalse - return FsProxyCore.OpenFileSystem(out fileSystem, normalizedPath, type, canMountSystemDataPrivate, titleId); + return FsProxyCore.OpenFileSystem(out fileSystem, normalizer.Path, type, canMountSystemDataPrivate, titleId); + } + + private PathNormalizer.Option GetPathNormalizerOptions(U8Span path) + { + int hostMountLength = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName, + PathTools.MountNameLengthMax); + + bool isHostPath = StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountLength) == 0; + + PathNormalizer.Option hostOption = isHostPath ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None; + return PathNormalizer.Option.HasMountName | PathNormalizer.Option.PreserveTailSeparator | hostOption; } public Result OpenFileSystemWithPatch(out IFileSystem fileSystem, TitleId titleId, FileSystemProxyType type) diff --git a/src/LibHac/FsService/FileSystemProxyCore.cs b/src/LibHac/FsService/FileSystemProxyCore.cs index 341f1fca..b6a0ec6b 100644 --- a/src/LibHac/FsService/FileSystemProxyCore.cs +++ b/src/LibHac/FsService/FileSystemProxyCore.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Shim; @@ -96,9 +97,19 @@ namespace LibHac.FsService rc = TryOpenNca(ref path2, out Nca nca, baseFileSystem, openTitleId); if (rc.IsFailure()) return rc; + rc = OpenNcaStorage(out IStorage ncaSectionStorage, nca, out NcaFormatType fsType, type, + mountNameInfo.IsGameCard, canMountSystemDataPrivate); + if (rc.IsFailure()) return rc; - - throw new NotImplementedException(); + switch (fsType) + { + case NcaFormatType.Romfs: + return FsCreators.RomFileSystemCreator.Create(out fileSystem, ncaSectionStorage); + case NcaFormatType.Pfs0: + return FsCreators.PartitionFileSystemCreator.Create(out fileSystem, ncaSectionStorage); + default: + return ResultFs.InvalidNcaFsType.Log(); + } } /// @@ -340,9 +351,132 @@ namespace LibHac.FsService return rc; } - private Result TryOpenNca(ref U8Span path, out Nca nca, IFileSystem baseFileSystem, TitleId titleId) + private Result TryOpenNca(ref U8Span path, out Nca nca, IFileSystem baseFileSystem, TitleId programId) { - throw new NotImplementedException(); + nca = default; + + Result rc = FileStorageBasedFileSystem.CreateNew(out FileStorageBasedFileSystem ncaFileStorage, + baseFileSystem, path, OpenMode.Read); + if (rc.IsFailure()) return rc; + + rc = FsCreators.StorageOnNcaCreator.OpenNca(out Nca ncaTemp, ncaFileStorage); + if (rc.IsFailure()) return rc; + + if (programId.Value == ulong.MaxValue) + { + ulong ncaProgramId = ncaTemp.Header.TitleId; + + if (ncaProgramId != ulong.MaxValue && programId.Value != ncaProgramId) + { + return ResultFs.InvalidNcaProgramId.Log(); + } + } + + nca = ncaTemp; + return Result.Success; + } + + private Result OpenNcaStorage(out IStorage ncaStorage, Nca nca, out NcaFormatType fsType, + FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate) + { + ncaStorage = default; + fsType = default; + + NcaContentType contentType = nca.Header.ContentType; + + switch (fsProxyType) + { + case FileSystemProxyType.Code: + case FileSystemProxyType.Rom: + case FileSystemProxyType.Logo: + case FileSystemProxyType.RegisteredUpdate: + if (contentType != NcaContentType.Program) + return ResultFs.PreconditionViolation.Log(); + + break; + + case FileSystemProxyType.Control: + if (contentType != NcaContentType.Control) + return ResultFs.PreconditionViolation.Log(); + + break; + case FileSystemProxyType.Manual: + if (contentType != NcaContentType.Manual) + return ResultFs.PreconditionViolation.Log(); + + break; + case FileSystemProxyType.Meta: + if (contentType != NcaContentType.Meta) + return ResultFs.PreconditionViolation.Log(); + + break; + case FileSystemProxyType.Data: + if (contentType != NcaContentType.Data && contentType != NcaContentType.PublicData) + return ResultFs.PreconditionViolation.Log(); + + if (contentType == NcaContentType.Data && !canMountSystemDataPrivate) + return ResultFs.PermissionDenied.Log(); + + break; + default: + return ResultFs.InvalidArgument.Log(); + } + + if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard) + return ResultFs.PermissionDenied.Log(); + + Result rc = SetNcaExternalKey(nca); + if (rc.IsFailure()) return rc; + + rc = GetNcaSectionIndex(out int sectionIndex, fsProxyType); + if (rc.IsFailure()) return rc; + + rc = FsCreators.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca, + sectionIndex, fsProxyType == FileSystemProxyType.Code); + if (rc.IsFailure()) return rc; + + fsType = fsHeader.FormatType; + return Result.Success; + } + + private Result SetNcaExternalKey(Nca nca) + { + var rightsId = new RightsId(nca.Header.RightsId); + var zero = new RightsId(0, 0); + + if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf())) + return Result.Success; + + Result rc = ExternalKeys.Get(rightsId, out AccessKey accessKey); + if (rc.IsFailure()) return rc; + + // todo: Set key in nca reader + + return Result.Success; + } + + private Result GetNcaSectionIndex(out int index, FileSystemProxyType fspType) + { + switch (fspType) + { + case FileSystemProxyType.Code: + case FileSystemProxyType.Control: + case FileSystemProxyType.Manual: + case FileSystemProxyType.Meta: + case FileSystemProxyType.Data: + index = 0; + return Result.Success; + case FileSystemProxyType.Rom: + case FileSystemProxyType.RegisteredUpdate: + index = 1; + return Result.Success; + case FileSystemProxyType.Logo: + index = 2; + return Result.Success; + default: + index = default; + return ResultFs.InvalidArgument.Log(); + } } public Result OpenBisFileSystem(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId)