mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 12:34:47 +00:00
fssystem: implement Sha256PartitionFileSystem
This commit is contained in:
parent
15229b61a4
commit
faf57bffbf
4 changed files with 174 additions and 0 deletions
|
@ -67,5 +67,6 @@ namespace ams::fssystem {
|
|||
};
|
||||
|
||||
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,29 @@ namespace ams::fssystem {
|
|||
using ResultSignatureVerificationFailed = fs::ResultPartitionSignatureVerificationFailed;
|
||||
};
|
||||
|
||||
struct Sha256PartitionFileSystemFormat {
|
||||
static constexpr size_t HashSize = ::ams::crypto::Sha256Generator::HashSize;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PartitionEntry {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 name_offset;
|
||||
u32 hash_target_size;
|
||||
u64 hash_target_offset;
|
||||
char hash[HashSize];
|
||||
};
|
||||
static_assert(std::is_pod<PartitionEntry>::value);
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr const char VersionSignature[] = { 'H', 'F', 'S', '0' };
|
||||
|
||||
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
|
||||
static constexpr size_t FileDataAlignmentSize = 0x200;
|
||||
|
||||
using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename Format>
|
||||
|
@ -81,4 +104,10 @@ namespace ams::fssystem {
|
|||
|
||||
using PartitionFileSystemMeta = PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
||||
|
||||
class Sha256PartitionFileSystemMeta : public PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat> {
|
||||
public:
|
||||
using PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>::Initialize;
|
||||
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix = std::nullopt);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -126,6 +126,94 @@ namespace ams::fssystem {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template<>
|
||||
Result PartitionFileSystemCore<Sha256PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
|
||||
/* Perform a dry read. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
|
||||
|
||||
const s64 entry_start = this->parent->meta_data_size + this->partition_entry->offset;
|
||||
const s64 read_end = static_cast<s64>(offset + read_size);
|
||||
const s64 hash_start = static_cast<s64>(this->partition_entry->hash_target_offset);
|
||||
const s64 hash_end = hash_start + this->partition_entry->hash_target_size;
|
||||
|
||||
if (read_end <= hash_start || hash_end <= offset) {
|
||||
/* We aren't reading hashed data, so we can just read from the base storage. */
|
||||
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
|
||||
} else {
|
||||
/* Only hash target offset == 0 is supported. */
|
||||
R_UNLESS(hash_start == 0, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Ensure that the hash region is valid. */
|
||||
R_UNLESS(this->partition_entry->hash_target_offset + this->partition_entry->hash_target_size <= this->partition_entry->size, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Validate our read offset. */
|
||||
const s64 read_offset = entry_start + offset;
|
||||
R_UNLESS(read_offset >= offset, fs::ResultOutOfRange());
|
||||
|
||||
/* Prepare a buffer for our calculated hash. */
|
||||
char hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::Sha256Generator generator;
|
||||
|
||||
/* Ensure we can perform our read. */
|
||||
const bool hash_in_read = offset <= hash_start && hash_end <= read_end;
|
||||
const bool read_in_hash = hash_start <= offset && read_end <= hash_end;
|
||||
R_UNLESS(hash_in_read || read_in_hash, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Initialize the generator. */
|
||||
generator.Initialize();
|
||||
|
||||
if (hash_in_read) {
|
||||
/* Easy case: hash region is contained within the bounds. */
|
||||
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
|
||||
generator.Update(static_cast<u8 *>(dst) + hash_start - offset, this->partition_entry->hash_target_size);
|
||||
} else /* if (read_in_hash) */ {
|
||||
/* We're reading a portion of what's hashed. */
|
||||
s64 remaining_hash_size = this->partition_entry->hash_target_size;
|
||||
s64 hash_offset = entry_start + hash_start;
|
||||
s64 remaining_size = read_size;
|
||||
s64 copy_offset = 0;
|
||||
while (remaining_hash_size > 0) {
|
||||
/* Read some portion of data into the buffer. */
|
||||
constexpr size_t HashBufferSize = 0x200;
|
||||
char hash_buffer[HashBufferSize];
|
||||
size_t cur_size = static_cast<size_t>(std::min(static_cast<s64>(HashBufferSize), remaining_hash_size));
|
||||
R_TRY(this->parent->base_storage->Read(hash_offset, hash_buffer, cur_size));
|
||||
|
||||
/* Update the hash. */
|
||||
generator.Update(hash_buffer, cur_size);
|
||||
|
||||
/* If we need to copy, do so. */
|
||||
if (read_offset <= (hash_offset + static_cast<s64>(cur_size)) && remaining_size > 0) {
|
||||
const s64 hash_buffer_offset = std::max<s64>(read_offset - hash_offset, 0);
|
||||
const size_t copy_size = static_cast<size_t>(std::min<s64>(cur_size - hash_buffer_offset, remaining_size));
|
||||
std::memcpy(static_cast<u8 *>(dst) + copy_offset, hash_buffer + hash_buffer_offset, copy_size);
|
||||
remaining_size -= copy_size;
|
||||
copy_offset += copy_size;
|
||||
}
|
||||
|
||||
/* Update offsets. */
|
||||
remaining_hash_size -= cur_size;
|
||||
hash_offset += cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the hash. */
|
||||
generator.GetHash(hash, sizeof(hash));
|
||||
|
||||
/* Validate the hash. */
|
||||
auto hash_guard = SCOPE_GUARD { std::memset(dst, 0, read_size); };
|
||||
R_UNLESS(crypto::IsSameBytes(this->partition_entry->hash, hash, sizeof(hash)), fs::ResultSha256PartitionHashVerificationFailed());
|
||||
|
||||
/* We successfully completed our read. */
|
||||
hash_guard.Cancel();
|
||||
}
|
||||
|
||||
/* Set output size. */
|
||||
*out = read_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
class PartitionFileSystemCore<MetaType>::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable {
|
||||
private:
|
||||
|
@ -357,5 +445,6 @@ namespace ams::fssystem {
|
|||
}
|
||||
|
||||
template class PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||
template class PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||
|
||||
}
|
||||
|
|
|
@ -160,5 +160,60 @@ namespace ams::fssystem {
|
|||
}
|
||||
|
||||
template class PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
||||
template class PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>;
|
||||
|
||||
Result Sha256PartitionFileSystemMeta::Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix) {
|
||||
/* Ensure preconditions. */
|
||||
R_UNLESS(hash_size == crypto::Sha256Generator::HashSize, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Get metadata size. */
|
||||
R_TRY(QueryMetaDataSize(std::addressof(this->meta_data_size), base_storage));
|
||||
|
||||
/* Ensure we have no buffer. */
|
||||
this->DeallocateBuffer();
|
||||
|
||||
/* Set allocator and allocate buffer. */
|
||||
this->allocator = allocator;
|
||||
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
|
||||
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaB());
|
||||
|
||||
/* Read metadata. */
|
||||
R_TRY(base_storage->Read(0, this->buffer, this->meta_data_size));
|
||||
|
||||
/* Calculate hash. */
|
||||
char calc_hash[crypto::Sha256Generator::HashSize];
|
||||
{
|
||||
crypto::Sha256Generator generator;
|
||||
generator.Initialize();
|
||||
generator.Update(this->buffer, this->meta_data_size);
|
||||
if (suffix) {
|
||||
u8 suffix_val = *suffix;
|
||||
generator.Update(std::addressof(suffix_val), 1);
|
||||
}
|
||||
generator.GetHash(calc_hash, sizeof(calc_hash));
|
||||
}
|
||||
|
||||
/* Ensure hash is valid. */
|
||||
R_UNLESS(crypto::IsSameBytes(hash, calc_hash, sizeof(calc_hash)), fs::ResultSha256PartitionHashVerificationFailed());
|
||||
|
||||
/* Give access to Format */
|
||||
using Format = impl::Sha256PartitionFileSystemFormat;
|
||||
|
||||
/* Set header. */
|
||||
this->header = reinterpret_cast<PartitionFileSystemHeader *>(this->buffer);
|
||||
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Validate size for entries and name table. */
|
||||
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
|
||||
R_UNLESS(this->meta_data_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSha256PartitionMetaDataSize());
|
||||
|
||||
/* Set entries and name table. */
|
||||
this->entries = reinterpret_cast<PartitionEntry *>(this->buffer + sizeof(PartitionFileSystemHeader));
|
||||
this->name_table = this->buffer + sizeof(PartitionFileSystemHeader) + entries_size;
|
||||
|
||||
/* We initialized. */
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue