Implement NpdmVerificationFileSystem

This commit is contained in:
Alex Barney 2024-05-01 21:56:40 -07:00
parent 4a7b67bf5a
commit dd2b9fe2ca
4 changed files with 115 additions and 108 deletions

View file

@ -76,9 +76,9 @@ public class ReadOnlyFileSystem : IFileSystem
{ {
private SharedRef<IFileSystem> _baseFileSystem; private SharedRef<IFileSystem> _baseFileSystem;
public ReadOnlyFileSystem(ref SharedRef<IFileSystem> baseFileSystem) public ReadOnlyFileSystem(ref readonly SharedRef<IFileSystem> baseFileSystem)
{ {
_baseFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem); _baseFileSystem = SharedRef<IFileSystem>.CreateCopy(in baseFileSystem);
Assert.SdkRequires(_baseFileSystem.HasValue); Assert.SdkRequires(_baseFileSystem.HasValue);
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Crypto;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
@ -12,14 +13,20 @@ public struct NpdmHash
private byte _data; private byte _data;
} }
/// <summary>
/// Wraps an <see cref="IFile"/>. 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.
/// </summary>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class NpdmVerificationFile : IFile public class NpdmVerificationFile : IFile
{ {
private NpdmHash _hash; private readonly NpdmHash _hash;
private UniqueRef<IFile> _baseFile; private UniqueRef<IFile> _baseFile;
public NpdmVerificationFile(ref UniqueRef<IFile> baseFile, NpdmHash hash) public NpdmVerificationFile(ref UniqueRef<IFile> baseFile, NpdmHash hash)
{ {
throw new NotImplementedException(); _hash = hash;
_baseFile = UniqueRef<IFile>.Create(ref baseFile);
} }
public override void Dispose() public override void Dispose()
@ -30,143 +37,143 @@ public class NpdmVerificationFile : IFile
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option) protected override Result DoRead(out long bytesRead, long offset, Span<byte> 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<NpdmHash>()))
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<byte> source, in WriteOption option) protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option) =>
{ _baseFile.Get.Write(offset, source, in option).Ret();
throw new NotImplementedException();
}
protected override Result DoFlush() protected override Result DoFlush() => _baseFile.Get.Flush().Ret();
{ protected override Result DoSetSize(long size) => _baseFile.Get.SetSize(size).Ret();
throw new NotImplementedException(); protected override Result DoGetSize(out long size) => _baseFile.Get.GetSize(out size).Ret();
}
protected override Result DoSetSize(long size) protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
{ ReadOnlySpan<byte> inBuffer) =>
throw new NotImplementedException(); _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer).Ret();
}
protected override Result DoGetSize(out long size)
{
throw new NotImplementedException();
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
throw new NotImplementedException();
}
} }
/// <summary>
/// Wraps a code <see cref="IFileSystem"/>. Contains the hash of the code filesystem's .npdm file. When opening the npdm
/// file, a <see cref="NpdmVerificationFile"/> is used to ensure that the npdm file hasn't been modified.
/// </summary>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class NpdmVerificationFileSystem : IFileSystem public class NpdmVerificationFileSystem : IFileSystem
{ {
private NpdmHash _hash; private static ReadOnlySpan<byte> NpdmFilePath => "/main.npdm"u8;
private ReadOnlyFileSystem _baseFileSystem;
public NpdmVerificationFileSystem(in SharedRef<IFileSystem> baseFileSystem, NpdmHash hash) private readonly NpdmHash _hash;
private readonly ReadOnlyFileSystem _baseFileSystem;
public NpdmVerificationFileSystem(ref readonly SharedRef<IFileSystem> baseFileSystem, NpdmHash hash)
{ {
_hash = hash;
_baseFileSystem = new ReadOnlyFileSystem(in baseFileSystem);
} }
public override void Dispose() public override void Dispose()
{ {
_baseFileSystem.Dispose(); _baseFileSystem?.Dispose();
base.Dispose(); base.Dispose();
} }
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode)
{ {
throw new NotImplementedException(); using var file = new UniqueRef<IFile>();
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<IFile>(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) protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option) =>
{ _baseFileSystem.CreateFile(in path, size, option).Ret();
throw new NotImplementedException();
}
protected override Result DoDeleteFile(ref readonly Path path) protected override Result DoDeleteFile(ref readonly Path path) =>
{ _baseFileSystem.DeleteFile(in path).Ret();
throw new NotImplementedException();
}
protected override Result DoCreateDirectory(ref readonly Path path) protected override Result DoCreateDirectory(ref readonly Path path) =>
{ _baseFileSystem.CreateDirectory(in path).Ret();
throw new NotImplementedException();
}
protected override Result DoDeleteDirectory(ref readonly Path path) protected override Result DoDeleteDirectory(ref readonly Path path) =>
{ _baseFileSystem.DeleteDirectory(in path).Ret();
throw new NotImplementedException();
}
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path) protected override Result DoDeleteDirectoryRecursively(ref readonly Path path) =>
{ _baseFileSystem.DeleteDirectoryRecursively(in path).Ret();
throw new NotImplementedException();
}
protected override Result DoCleanDirectoryRecursively(ref readonly Path path) protected override Result DoCleanDirectoryRecursively(ref readonly Path path) =>
{ _baseFileSystem.CleanDirectoryRecursively(in path).Ret();
throw new NotImplementedException();
}
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath) protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath) =>
{ _baseFileSystem.RenameFile(in currentPath, in newPath).Ret();
throw new NotImplementedException();
}
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath) protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath) =>
{ _baseFileSystem.RenameDirectory(in currentPath, in newPath).Ret();
throw new NotImplementedException();
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path) protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path) =>
{ _baseFileSystem.GetEntryType(out entryType, in path).Ret();
throw new NotImplementedException();
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path) protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path) =>
{ _baseFileSystem.GetFreeSpaceSize(out freeSpace, in path).Ret();
throw new NotImplementedException();
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path) protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path) =>
{ _baseFileSystem.GetFreeSpaceSize(out totalSpace, in path).Ret();
throw new NotImplementedException();
}
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path, OpenDirectoryMode mode) =>
{ _baseFileSystem.OpenDirectory(ref outDirectory, in path, mode).Ret();
throw new NotImplementedException();
}
protected override Result DoCommit() protected override Result DoCommit() =>
{ _baseFileSystem.Commit().Ret();
throw new NotImplementedException();
}
protected override Result DoCommitProvisionally(long counter) protected override Result DoCommitProvisionally(long counter) =>
{ _baseFileSystem.CommitProvisionally(counter).Ret();
throw new NotImplementedException();
}
protected override Result DoRollback() protected override Result DoRollback() =>
{ _baseFileSystem.Rollback().Ret();
throw new NotImplementedException();
}
protected override Result DoFlush() protected override Result DoFlush() =>
{ _baseFileSystem.Flush().Ret();
throw new NotImplementedException();
}
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, ref readonly Path path) protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, ref readonly Path path) =>
{ _baseFileSystem.GetFileTimeStampRaw(out timeStamp, in path).Ret();
throw new NotImplementedException();
}
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, ref readonly Path path) protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, ref readonly Path path) =>
{ _baseFileSystem.QueryEntry(outBuffer, inBuffer, queryId, in path).Ret();
throw new NotImplementedException();
}
} }

View file

@ -312,7 +312,7 @@ public class NcaFileSystemServiceImpl : IDisposable
res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType); res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var readOnlyFileSystem = new SharedRef<IFileSystem>(new ReadOnlyFileSystem(ref hostFileSystem.Ref)); using var readOnlyFileSystem = new SharedRef<IFileSystem>(new ReadOnlyFileSystem(in hostFileSystem));
outFileSystem.SetByMove(ref readOnlyFileSystem.Ref); outFileSystem.SetByMove(ref readOnlyFileSystem.Ref);
return Result.Success; return Result.Success;
@ -444,7 +444,7 @@ public class NcaFileSystemServiceImpl : IDisposable
res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType); res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var readOnlyFileSystem = new SharedRef<ReadOnlyFileSystem>(new ReadOnlyFileSystem(ref hostFileSystem.Ref)); using var readOnlyFileSystem = new SharedRef<ReadOnlyFileSystem>(new ReadOnlyFileSystem(in hostFileSystem));
outFileSystem.SetByMove(ref readOnlyFileSystem.Ref); outFileSystem.SetByMove(ref readOnlyFileSystem.Ref);
return Result.Success; return Result.Success;

View file

@ -321,7 +321,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
if (openReadOnly) if (openReadOnly)
{ {
using SharedRef<IFileSystem> tempFs = SharedRef<IFileSystem>.CreateMove(ref registerFs.Ref); using SharedRef<IFileSystem> tempFs = SharedRef<IFileSystem>.CreateMove(ref registerFs.Ref);
using var readOnlyFileSystem = new SharedRef<ReadOnlyFileSystem>(new ReadOnlyFileSystem(ref tempFs.Ref)); using var readOnlyFileSystem = new SharedRef<ReadOnlyFileSystem>(new ReadOnlyFileSystem(in tempFs));
if (!readOnlyFileSystem.HasValue) if (!readOnlyFileSystem.HasValue)
return ResultFs.AllocationMemoryFailedInSaveDataFileSystemServiceImplB.Log(); return ResultFs.AllocationMemoryFailedInSaveDataFileSystemServiceImplB.Log();