From dd2b9fe2ca81bc2ecefcf5696ffb8def8952c053 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 1 May 2024 21:56:40 -0700 Subject: [PATCH] Implement NpdmVerificationFileSystem --- src/LibHac/Fs/ReadOnlyFileSystem.cs | 4 +- .../FsSrv/Impl/NpdmVerificationFileSystem.cs | 213 +++++++++--------- src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs | 4 +- .../FsSrv/SaveDataFileSystemServiceImpl.cs | 2 +- 4 files changed, 115 insertions(+), 108 deletions(-) diff --git a/src/LibHac/Fs/ReadOnlyFileSystem.cs b/src/LibHac/Fs/ReadOnlyFileSystem.cs index 60469a1b..d3937954 100644 --- a/src/LibHac/Fs/ReadOnlyFileSystem.cs +++ b/src/LibHac/Fs/ReadOnlyFileSystem.cs @@ -76,9 +76,9 @@ public class ReadOnlyFileSystem : IFileSystem { private SharedRef _baseFileSystem; - public ReadOnlyFileSystem(ref SharedRef baseFileSystem) + public ReadOnlyFileSystem(ref readonly SharedRef baseFileSystem) { - _baseFileSystem = SharedRef.CreateMove(ref baseFileSystem); + _baseFileSystem = SharedRef.CreateCopy(in baseFileSystem); Assert.SdkRequires(_baseFileSystem.HasValue); } diff --git a/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs b/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs index edfc6914..d3c26984 100644 --- a/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs +++ b/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Crypto; using LibHac.Fs; using LibHac.Fs.Fsa; @@ -12,14 +13,20 @@ public struct NpdmHash private byte _data; } +/// +/// Wraps an . When reading, calculates a hash of the entire file and compares it to the file's +/// original hash to ensure the file hasn't been modified. +/// +/// Based on nnSdk 17.5.0 (FS 17.0.0) public class NpdmVerificationFile : IFile { - private NpdmHash _hash; + private readonly NpdmHash _hash; private UniqueRef _baseFile; public NpdmVerificationFile(ref UniqueRef baseFile, NpdmHash hash) { - throw new NotImplementedException(); + _hash = hash; + _baseFile = UniqueRef.Create(ref baseFile); } public override void Dispose() @@ -30,143 +37,143 @@ public class NpdmVerificationFile : IFile protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out bytesRead); + + Result res = DryRead(out long readableSize, offset, destination.Length, in option, OpenMode.Read); + if (res.IsFailure()) return res.Miss(); + + byte[] hashData; + byte[] npdmData = null; + + res = ReadAndCalculateNpdmHash(); + if (res.IsFailure()) return res.Miss(); + + if (!CryptoUtil.IsSameBytes(hashData.AsSpan(), _hash, Unsafe.SizeOf())) + return ResultFs.InvalidNspdVerificationData.Log(); + + npdmData.AsSpan((int)offset, (int)readableSize).CopyTo(destination); + return Result.Success; + + Result ReadAndCalculateNpdmHash() + { + hashData = new byte[0x20]; + Result res2 = _baseFile.Get.GetSize(out long npdmDataSize); + if (res2.IsFailure()) return res2.Miss(); + + npdmData = new byte[npdmDataSize]; + res2 = _baseFile.Get.Read(out long npdmReadSize, 0, npdmData, ReadOption.None); + if (res2.IsFailure()) return res2.Miss(); + + Sha256.GenerateSha256Hash(npdmData.AsSpan(0, (int)npdmReadSize), hashData); + return Result.Success; + } } - protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option) - { - throw new NotImplementedException(); - } + protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option) => + _baseFile.Get.Write(offset, source, in option).Ret(); - protected override Result DoFlush() - { - throw new NotImplementedException(); - } + protected override Result DoFlush() => _baseFile.Get.Flush().Ret(); + protected override Result DoSetSize(long size) => _baseFile.Get.SetSize(size).Ret(); + protected override Result DoGetSize(out long size) => _baseFile.Get.GetSize(out size).Ret(); - protected override Result DoSetSize(long size) - { - throw new NotImplementedException(); - } - - protected override Result DoGetSize(out long size) - { - throw new NotImplementedException(); - } - - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) - { - throw new NotImplementedException(); - } + protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) => + _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer).Ret(); } +/// +/// Wraps a code . Contains the hash of the code filesystem's .npdm file. When opening the npdm +/// file, a is used to ensure that the npdm file hasn't been modified. +/// +/// Based on nnSdk 17.5.0 (FS 17.0.0) public class NpdmVerificationFileSystem : IFileSystem { - private NpdmHash _hash; - private ReadOnlyFileSystem _baseFileSystem; + private static ReadOnlySpan NpdmFilePath => "/main.npdm"u8; - public NpdmVerificationFileSystem(in SharedRef baseFileSystem, NpdmHash hash) + private readonly NpdmHash _hash; + private readonly ReadOnlyFileSystem _baseFileSystem; + + public NpdmVerificationFileSystem(ref readonly SharedRef baseFileSystem, NpdmHash hash) { - + _hash = hash; + _baseFileSystem = new ReadOnlyFileSystem(in baseFileSystem); } public override void Dispose() { - _baseFileSystem.Dispose(); + _baseFileSystem?.Dispose(); base.Dispose(); } protected override Result DoOpenFile(ref UniqueRef outFile, ref readonly Path path, OpenMode mode) { - throw new NotImplementedException(); + using var file = new UniqueRef(); + Result res = _baseFileSystem.OpenFile(ref file.Ref, in path, mode); + if (res.IsFailure()) return res.Miss(); + + if (path != NpdmFilePath) + { + outFile.Set(ref file.Ref); + return Result.Success; + } + + using var npdmFile = new UniqueRef(new NpdmVerificationFile(ref file.Ref, _hash)); + + outFile.Set(ref npdmFile.Ref); + return Result.Success; } - protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option) - { - throw new NotImplementedException(); - } + protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option) => + _baseFileSystem.CreateFile(in path, size, option).Ret(); - protected override Result DoDeleteFile(ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoDeleteFile(ref readonly Path path) => + _baseFileSystem.DeleteFile(in path).Ret(); - protected override Result DoCreateDirectory(ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoCreateDirectory(ref readonly Path path) => + _baseFileSystem.CreateDirectory(in path).Ret(); - protected override Result DoDeleteDirectory(ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoDeleteDirectory(ref readonly Path path) => + _baseFileSystem.DeleteDirectory(in path).Ret(); - protected override Result DoDeleteDirectoryRecursively(ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoDeleteDirectoryRecursively(ref readonly Path path) => + _baseFileSystem.DeleteDirectoryRecursively(in path).Ret(); - protected override Result DoCleanDirectoryRecursively(ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoCleanDirectoryRecursively(ref readonly Path path) => + _baseFileSystem.CleanDirectoryRecursively(in path).Ret(); - protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath) - { - throw new NotImplementedException(); - } + protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath) => + _baseFileSystem.RenameFile(in currentPath, in newPath).Ret(); - protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath) - { - throw new NotImplementedException(); - } + protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath) => + _baseFileSystem.RenameDirectory(in currentPath, in newPath).Ret(); - protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path) => + _baseFileSystem.GetEntryType(out entryType, in path).Ret(); - protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path) => + _baseFileSystem.GetFreeSpaceSize(out freeSpace, in path).Ret(); - protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path) => + _baseFileSystem.GetFreeSpaceSize(out totalSpace, in path).Ret(); - protected override Result DoOpenDirectory(ref UniqueRef outDirectory, ref readonly Path path, OpenDirectoryMode mode) - { - throw new NotImplementedException(); - } + protected override Result DoOpenDirectory(ref UniqueRef outDirectory, ref readonly Path path, OpenDirectoryMode mode) => + _baseFileSystem.OpenDirectory(ref outDirectory, in path, mode).Ret(); - protected override Result DoCommit() - { - throw new NotImplementedException(); - } + protected override Result DoCommit() => + _baseFileSystem.Commit().Ret(); - protected override Result DoCommitProvisionally(long counter) - { - throw new NotImplementedException(); - } + protected override Result DoCommitProvisionally(long counter) => + _baseFileSystem.CommitProvisionally(counter).Ret(); - protected override Result DoRollback() - { - throw new NotImplementedException(); - } + protected override Result DoRollback() => + _baseFileSystem.Rollback().Ret(); - protected override Result DoFlush() - { - throw new NotImplementedException(); - } + protected override Result DoFlush() => + _baseFileSystem.Flush().Ret(); - protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, ref readonly Path path) => + _baseFileSystem.GetFileTimeStampRaw(out timeStamp, in path).Ret(); - protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, ref readonly Path path) - { - throw new NotImplementedException(); - } + protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, ref readonly Path path) => + _baseFileSystem.QueryEntry(outBuffer, inBuffer, queryId, in path).Ret(); } \ No newline at end of file diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs index 275386a5..2b92af54 100644 --- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs @@ -312,7 +312,7 @@ public class NcaFileSystemServiceImpl : IDisposable res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType); if (res.IsFailure()) return res.Miss(); - using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(ref hostFileSystem.Ref)); + using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(in hostFileSystem)); outFileSystem.SetByMove(ref readOnlyFileSystem.Ref); return Result.Success; @@ -444,7 +444,7 @@ public class NcaFileSystemServiceImpl : IDisposable res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType); if (res.IsFailure()) return res.Miss(); - using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(ref hostFileSystem.Ref)); + using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(in hostFileSystem)); outFileSystem.SetByMove(ref readOnlyFileSystem.Ref); return Result.Success; diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs index 90ec04e2..79358735 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs @@ -321,7 +321,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable if (openReadOnly) { using SharedRef tempFs = SharedRef.CreateMove(ref registerFs.Ref); - using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(ref tempFs.Ref)); + using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(in tempFs)); if (!readOnlyFileSystem.HasValue) return ResultFs.AllocationMemoryFailedInSaveDataFileSystemServiceImplB.Log();