mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-07-17 14:22:13 +02:00
fssystem: implement Sha256PartitionFileSystem
This commit is contained in:
parent
15229b61a4
commit
faf57bffbf
@ -67,5 +67,6 @@ namespace ams::fssystem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
|
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||||
|
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,29 @@ namespace ams::fssystem {
|
|||||||
using ResultSignatureVerificationFailed = fs::ResultPartitionSignatureVerificationFailed;
|
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>
|
template<typename Format>
|
||||||
@ -81,4 +104,10 @@ namespace ams::fssystem {
|
|||||||
|
|
||||||
using PartitionFileSystemMeta = PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
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();
|
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>
|
template <typename MetaType>
|
||||||
class PartitionFileSystemCore<MetaType>::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable {
|
class PartitionFileSystemCore<MetaType>::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable {
|
||||||
private:
|
private:
|
||||||
@ -357,5 +445,6 @@ namespace ams::fssystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template class PartitionFileSystemCore<PartitionFileSystemMeta>;
|
template class PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||||
|
template class PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -160,5 +160,60 @@ namespace ams::fssystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template class PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
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…
Reference in New Issue
Block a user