fs: implement Mount(System)Data

This commit is contained in:
Michael Scire 2020-03-05 19:02:19 -08:00
parent d52c1ee850
commit c7d9cd24a7
19 changed files with 2224 additions and 28 deletions

View file

@ -23,6 +23,7 @@
#include "fs/fs_remote_filesystem.hpp"
#include "fs/fs_istorage.hpp"
#include "fs/fs_substorage.hpp"
#include "fs/fs_memory_storage.hpp"
#include "fs/fs_remote_storage.hpp"
#include "fs/fs_file_storage.hpp"
#include "fs/fs_query_range.hpp"
@ -30,7 +31,9 @@
#include "fs/fs_mount.hpp"
#include "fs/fs_path_tool.hpp"
#include "fs/fs_path_utils.hpp"
#include "fs/fs_rom_path_tool.hpp"
#include "fs/fs_romfs_filesystem.hpp"
#include "fs/fs_data.hpp"
#include "fs/fs_system_data.hpp"
#include "fs/fs_content_storage.hpp"
#include "fs/fs_game_card.hpp"
#include "fs/fs_sd_card.hpp"

View file

@ -19,3 +19,30 @@
#include "../ncm.hpp"
#include "../sf.hpp"
namespace ams::fs {
struct Int64 {
u32 low;
u32 high;
constexpr ALWAYS_INLINE void Set(s64 v) {
this->low = static_cast<u32>((v & static_cast<u64>(0x00000000FFFFFFFFul)) >> 0);
this->high = static_cast<u32>((v & static_cast<u64>(0xFFFFFFFF00000000ul)) >> 32);
}
constexpr ALWAYS_INLINE s64 Get() const {
return (static_cast<s64>(this->high) << 32) | (static_cast<s64>(this->low));
}
constexpr ALWAYS_INLINE Int64 &operator=(s64 v) {
this->Set(v);
return *this;
}
constexpr ALWAYS_INLINE operator s64() const {
return this->Get();
}
};
static_assert(std::is_pod<Int64>::value);
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_common.hpp"
namespace ams::fs::impl {
Result QueryMountDataCacheSize(size_t *out, ncm::ProgramId data_id, ncm::StorageId storage_id);
Result MountData(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id);
Result MountData(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size);
Result MountData(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_data_cache, bool use_path_cache);
}

View file

@ -0,0 +1,200 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_dbm_rom_types.hpp"
#include "fs_dbm_rom_path_tool.hpp"
#include "fs_dbm_rom_key_value_storage.hpp"
namespace ams::fs {
class HierarchicalRomFileTable {
public:
using Position = u32;
struct FindPosition {
Position next_dir;
Position next_file;
};
static_assert(std::is_pod<FindPosition>::value);
using DirectoryInfo = RomDirectoryInfo;
using FileInfo = RomFileInfo;
static constexpr RomFileId ConvertToFileId(Position pos) {
return static_cast<RomFileId>(pos);
}
private:
static constexpr inline Position InvalidPosition = ~Position();
static constexpr inline Position RootPosition = 0;
static constexpr inline size_t ReservedDirectoryCount = 1;
static constexpr RomDirectoryId ConvertToDirectoryId(Position pos) {
return static_cast<RomDirectoryId>(pos);
}
static constexpr Position ConvertToPosition(RomDirectoryId id) {
return static_cast<Position>(id);
}
static_assert(std::is_same<RomDirectoryId, RomFileId>::value);
struct RomDirectoryEntry {
Position next;
Position dir;
Position file;
};
static_assert(std::is_pod<RomDirectoryEntry>::value);
struct RomFileEntry {
Position next;
FileInfo info;
};
static_assert(std::is_pod<RomFileEntry>::value);
static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength;
template<typename ImplKeyType, typename ClientKeyType, typename ValueType>
class EntryMapTable : public RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength> {
public:
using ImplKey = ImplKeyType;
using ClientKey = ClientKeyType;
using Value = ValueType;
using Position = HierarchicalRomFileTable::Position;
using Base = RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength>;
public:
Result Add(Position *out, const ClientKeyType &key, const Value &value) {
return Base::AddImpl(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value);
}
Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) {
return Base::GetImpl(out_pos, out_val, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar));
}
Result GetByPosition(ImplKey *out_key, Value *out_val, Position pos) {
return Base::GetByPosition(out_key, out_val, pos);
}
Result GetByPosition(ImplKey *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) {
return Base::GetByPosition(out_key, out_val, out_aux, out_aux_size, pos);
}
Result SetByPosition(Position pos, const Value &value) {
return Base::SetByPosition(pos, value);
}
};
struct RomEntryKey {
Position parent;
bool IsEqual(const RomEntryKey &rhs, const void *aux_lhs, size_t aux_lhs_size, const void *aux_rhs, size_t aux_rhs_size) const {
if (this->parent != rhs.parent) {
return false;
}
if (aux_lhs_size != aux_rhs_size) {
return false;
}
return RomPathTool::IsEqualPath(reinterpret_cast<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar));
}
};
static_assert(std::is_pod<RomEntryKey>::value);
struct EntryKey {
RomEntryKey key;
RomPathTool::RomEntryName name;
constexpr u32 Hash() const {
u32 hash = this->key.parent ^ 123456789;
const RomPathChar *name = this->name.path;
const RomPathChar *end = name + this->name.length;
while (name < end) {
const u32 cur = static_cast<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++)));
hash = ((hash >> 5) | (hash << 27)) ^ cur;
}
return hash;
}
};
static_assert(std::is_pod<EntryKey>::value);
using DirectoryEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomDirectoryEntry>;
using FileEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomFileEntry>;
private:
DirectoryEntryMapTable dir_table;
FileEntryMapTable file_table;
public:
static s64 QueryDirectoryEntryStorageSize(u32 count);
static s64 QueryDirectoryEntryBucketStorageSize(s64 count);
static s64 QueryFileEntryStorageSize(u32 count);
static s64 QueryFileEntryBucketStorageSize(s64 count);
static Result Format(SubStorage dir_bucket, SubStorage file_bucket);
public:
HierarchicalRomFileTable();
constexpr u32 GetDirectoryEntryCount() const {
return this->dir_table.GetEntryCount();
}
constexpr u32 GetFileEntryCount() const {
return this->file_table.GetEntryCount();
}
Result Initialize(SubStorage dir_bucket, SubStorage dir_entry, SubStorage file_bucket, SubStorage file_entry);
void Finalize();
Result CreateRootDirectory();
Result CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info);
Result CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info);
Result ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path);
Result ConvertPathToFileId(RomFileId *out, const RomPathChar *path);
Result GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path);
Result GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id);
Result OpenFile(FileInfo *out, const RomPathChar *path);
Result OpenFile(FileInfo *out, RomFileId id);
Result FindOpen(FindPosition *out, const RomPathChar *path);
Result FindOpen(FindPosition *out, RomDirectoryId id);
Result FindNextDirectory(RomPathChar *out, FindPosition *find, size_t length);
Result FindNextFile(RomPathChar *out, FindPosition *find, size_t length);
Result QueryRomFileSystemSize(s64 *out_dir_entry_size, s64 *out_file_entry_size);
private:
Result GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path);
Result FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser *parser, const RomPathChar *path);
Result FindPathRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, bool is_dir, const RomPathChar *path);
Result FindDirectoryRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path);
Result FindFileRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path);
Result CheckSameEntryExists(const EntryKey &key, Result if_exists);
Result GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key);
Result GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id);
Result GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key);
Result GetFileEntry(RomFileEntry *out_entry, RomFileId id);
Result GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key);
Result OpenFile(FileInfo *out, const EntryKey &key);
Result FindOpen(FindPosition *out, const EntryKey &key);
};
}

View file

@ -0,0 +1,380 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_dbm_rom_types.hpp"
#include "fs_substorage.hpp"
namespace ams::fs {
template<typename KeyType, typename ValueType, size_t MaxAuxiliarySize>
class RomKeyValueStorage {
public:
using Key = KeyType;
using Value = ValueType;
using Position = u32;
using BucketIndex = s64;
struct FindIndex {
BucketIndex ind;
Position pos;
};
static_assert(std::is_pod<FindIndex>::value);
private:
static constexpr inline Position InvalidPosition = ~Position();
struct Element {
Key key;
Value value;
Position next;
u32 size;
};
static_assert(std::is_pod<Element>::value);
private:
s64 bucket_count;
SubStorage bucket_storage;
SubStorage kv_storage;
s64 total_entry_size;
u32 entry_count;
public:
static constexpr s64 QueryBucketStorageSize(s64 num) {
return num * sizeof(Position);
}
static constexpr s64 QueryBucketCount(s64 size) {
return size / sizeof(Position);
}
static constexpr s64 QueryKeyValueStorageSize(u32 num) {
return num * sizeof(Element);
}
static Result Format(SubStorage bucket, s64 count) {
const Position pos = InvalidPosition;
for (s64 i = 0; i < count; i++) {
R_TRY(bucket.Write(i * sizeof(pos), std::addressof(pos), sizeof(pos)));
}
return ResultSuccess();
}
public:
RomKeyValueStorage() : bucket_count(), bucket_storage(), kv_storage(), total_entry_size(), entry_count() { /* ... */ }
Result Initialize(const SubStorage &bucket, s64 count, const SubStorage &kv) {
AMS_ASSERT(count > 0);
this->bucket_storage = bucket;
this->kv_storage = kv;
this->bucket_count = count;
return ResultSuccess();
}
void Finalize() {
this->bucket_storage = SubStorage();
this->kv_storage = SubStorage();
this->bucket_count = 0;
}
s64 GetTotalEntrySize() const {
return this->total_entry_size;
}
Result GetFreeSize(s64 *out) {
AMS_ASSERT(out != nullptr);
s64 kv_size = 0;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
*out = kv_size - this->total_entry_size;
return ResultSuccess();
}
constexpr u32 GetEntryCount() const {
return this->entry_count;
}
Result Add(const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
Position pos;
return this->AddImpl(std::addressof(pos), key, hash_key, aux, aux_size, value);
}
Result Get(Value *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
Position pos;
return this->GetImpl(std::addressof(pos), out, key, hash_key, aux, aux_size);
}
Result FindOpen(FindIndex *out) const {
AMS_ASSERT(out != nullptr);
out->ind = static_cast<BucketIndex>(-1);
out->pos = InvalidPosition;
return ResultSuccess();
}
Result FindNext(Key *out_key, Value *out_val, FindIndex *find) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(find != nullptr);
BucketIndex ind = find->ind;
R_UNLESS((ind < this->bucket_count) || ind == static_cast<BucketIndex>(-1), fs::ResultDbmFindKeyFinished());
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
while (true) {
if (find->pos != InvalidPosition) {
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), find->pos));
AMS_ASSERT(elem.next == InvalidPosition || elem.next < kv_size);
find->pos = elem.next;
*out_key = elem.key;
*out_val = elem.val;
return ResultSuccess();
}
while (true) {
ind++;
if (ind == this->bucket_count) {
find->ind = ind;
find->pos = InvalidPosition;
return fs::ResultDbmFindKeyFinished();
}
Position pos;
R_TRY(this->ReadBucket(std::addressof(pos), ind));
AMS_ASSERT(pos == InvalidPosition || pos < kv_size);
if (pos != InvalidPosition) {
find->ind = ind;
find->pos = pos;
break;
}
}
}
}
protected:
Result AddImpl(Position *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(this->bucket_count > 0);
{
Position pos, prev_pos;
Element elem;
const Result find_res = this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size);
R_UNLESS(R_FAILED(find_res), fs::ResultDbmAlreadyExists());
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(find_res), find_res);
}
Position pos;
R_TRY(this->AllocateEntry(std::addressof(pos), aux_size));
Position next_pos;
R_TRY(this->LinkEntry(std::addressof(next_pos), pos, hash_key));
const Element elem = { key, value, next_pos, static_cast<u32>(aux_size) };
R_TRY(this->WriteKeyValue(std::addressof(elem), pos, aux, aux_size));
*out = pos;
this->entry_count++;
return ResultSuccess();
}
Result GetImpl(Position *out_pos, Value *out_val, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(aux != nullptr);
Position pos, prev_pos;
Element elem;
R_TRY(this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size));
*out_pos = pos;
*out_val = elem.value;
return ResultSuccess();
}
Result GetByPosition(Key *out_key, Value *out_val, Position pos) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), pos));
*out_key = elem.key;
*out_val = elem.value;
return ResultSuccess();
}
Result GetByPosition(Key *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(out_aux != nullptr);
AMS_ASSERT(out_aux_size != nullptr);
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), out_aux, out_aux_size, pos));
*out_key = elem.key;
*out_val = elem.value;
return ResultSuccess();
}
Result SetByPosition(Position pos, const Value &value) {
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), pos));
elem.value = value;
return this->WriteKeyValue(std::addressof(elem), pos, nullptr, 0);
}
private:
BucketIndex HashToBucket(u32 hash_key) const {
return hash_key % this->bucket_count;
}
Result FindImpl(Position *out_pos, Position *out_prev, Element *out_elem, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_prev != nullptr);
AMS_ASSERT(out_elem != nullptr);
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(this->bucket_count > 0);
*out_pos = 0;
*out_prev = 0;
const BucketIndex ind = HashToBucket(hash_key);
Position cur;
R_TRY(this->ReadBucket(std::addressof(cur), ind));
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
AMS_ASSERT(cur == InvalidPosition || cur < kv_size);
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
u8 *buf = static_cast<u8 *>(::ams::fs::impl::Allocate(MaxAuxiliarySize));
R_UNLESS(buf != nullptr, fs::ResultAllocationFailureInDbmRomKeyValueStorage());
ON_SCOPE_EXIT { ::ams::fs::impl::Deallocate(buf, MaxAuxiliarySize); };
while (true) {
size_t cur_aux_size;
R_TRY(this->ReadKeyValue(out_elem, buf, std::addressof(cur_aux_size), cur));
if (key.IsEqual(out_elem->key, aux, aux_size, buf, cur_aux_size)) {
*out_pos = cur;
return ResultSuccess();
}
*out_prev = cur;
cur = out_elem->next;
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
}
}
Result AllocateEntry(Position *out, size_t aux_size) {
AMS_ASSERT(out != nullptr);
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
const size_t end_pos = this->total_entry_size + sizeof(Element) + aux_size;
R_UNLESS(end_pos <= static_cast<size_t>(kv_size), fs::ResultDbmKeyFull());
*out = static_cast<Position>(this->total_entry_size);
this->total_entry_size = util::AlignUp(static_cast<s64>(end_pos), s64(4));
return ResultSuccess();
}
Result LinkEntry(Position *out, Position pos, u32 hash_key) {
AMS_ASSERT(out != nullptr);
const BucketIndex ind = HashToBucket(hash_key);
Position next;
R_TRY(this->ReadBucket(std::addressof(next), ind));
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
AMS_ASSERT(next == InvalidPosition || next < kv_size);
R_TRY(this->WriteBucket(pos, ind));
*out = next;
return ResultSuccess();
}
Result ReadBucket(Position *out, BucketIndex ind) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(ind < this->bucket_count);
const s64 offset = ind * sizeof(Position);
return this->bucket_storage.Read(offset, out, sizeof(*out));
}
Result WriteBucket(Position pos, BucketIndex ind) {
AMS_ASSERT(ind < this->bucket_count);
const s64 offset = ind * sizeof(Position);
return this->bucket_storage.Write(offset, std::addressof(pos), sizeof(pos));
}
Result ReadKeyValue(Element *out, Position pos) {
AMS_ASSERT(out != nullptr);
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
AMS_ASSERT(pos < kv_size);
return this->kv_storage.Read(pos, out, sizeof(*out));
}
Result ReadKeyValue(Element *out, void *out_aux, size_t *out_aux_size, Position pos) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(out_aux != nullptr);
AMS_ASSERT(out_aux_size != nullptr);
R_TRY(this->ReadKeyValue(out, pos));
*out_aux_size = out->size;
if (out->size > 0) {
R_TRY(this->kv_storage.Read(pos + sizeof(*out), out_aux, out->size));
}
return ResultSuccess();
}
Result WriteKeyValue(const Element *elem, Position pos, const void *aux, size_t aux_size) {
AMS_ASSERT(elem != nullptr);
AMS_ASSERT(aux != nullptr);
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
AMS_ASSERT(pos < kv_size);
R_TRY(this->kv_storage.Write(pos, elem, sizeof(*elem)));
if (aux != nullptr && aux_size > 0) {
R_TRY(this->kv_storage.Write(pos + sizeof(*elem), aux, aux_size));
}
return ResultSuccess();
}
};
}

View file

@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_rom_types.hpp"
#include "fs_dbm_rom_types.hpp"
namespace ams::fs {

View file

@ -43,8 +43,8 @@ namespace ams::fs {
static_assert(std::is_pod<RomDirectoryInfo>::value);
struct RomFileInfo {
s64 offset;
s64 size;
Int64 offset;
Int64 size;
};
static_assert(std::is_pod<RomFileInfo>::value);

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "impl/fs_newable.hpp"
#include "fs_istorage.hpp"
#include "fs_query_range.hpp"
namespace ams::fs {
class MemoryStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
private:
u8 * const buf;
const s64 size;
public:
MemoryStorage(void *b, s64 sz) : buf(static_cast<u8 *>(b)), size(sz) { /* .. */ }
public:
virtual Result Read(s64 offset, void *buffer, size_t size) override {
R_UNLESS(size != 0, ResultSuccess());
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
std::memcpy(buffer, this->buf + offset, size);
return ResultSuccess();
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override{
R_UNLESS(size != 0, ResultSuccess());
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
std::memcpy(this->buf + offset, buffer, size);
return ResultSuccess();
}
virtual Result Flush() override {
return ResultSuccess();
}
virtual Result GetSize(s64 *out) override {
*out = this->size;
return ResultSuccess();
}
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
switch (op_id) {
case OperationId::InvalidateCache:
return ResultSuccess();
case OperationId::QueryRange:
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
reinterpret_cast<QueryRangeInfo *>(dst)->Clear();
return ResultSuccess();
default:
return fs::ResultUnsupportedOperation();
}
}
};
}

View file

@ -16,10 +16,11 @@
#pragma once
#include "fs_common.hpp"
#include "fs_istorage.hpp"
#include "impl/fs_newable.hpp"
namespace ams::fs {
class RemoteStorage : public IStorage {
class RemoteStorage : public IStorage, public impl::Newable {
private:
std::unique_ptr<::FsStorage, impl::Deleter> base_storage;
public:

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_common.hpp"
#include "impl/fs_newable.hpp"
#include "fsa/fs_ifile.hpp"
#include "fsa/fs_idirectory.hpp"
#include "fsa/fs_ifilesystem.hpp"
#include "fs_dbm_hierarchical_rom_file_table.hpp"
namespace ams::fs {
class RomFsFileSystem : public fsa::IFileSystem, public impl::Newable {
NON_COPYABLE(RomFsFileSystem);
public:
using RomFileTable = HierarchicalRomFileTable;
private:
RomFileTable rom_file_table;
IStorage *base_storage;
std::unique_ptr<IStorage> unique_storage;
std::unique_ptr<IStorage> dir_bucket_storage;
std::unique_ptr<IStorage> dir_entry_storage;
std::unique_ptr<IStorage> file_bucket_storage;
std::unique_ptr<IStorage> file_entry_storage;
s64 entry_size;
private:
Result GetFileInfo(RomFileTable::FileInfo *out, const char *path);
public:
static Result GetRequiredWorkingMemorySize(size_t *out, IStorage *storage);
public:
RomFsFileSystem();
virtual ~RomFsFileSystem() override;
Result Initialize(IStorage *base, void *work, size_t work_size, bool use_cache);
Result Initialize(std::unique_ptr<IStorage>&& base, void *work, size_t work_size, bool use_cache);
IStorage *GetBaseStorage();
RomFileTable *GetRomFileTable();
Result GetFileBaseOffset(s64 *out, const char *path);
public:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override;
virtual Result DeleteFileImpl(const char *path) override;
virtual Result CreateDirectoryImpl(const char *path) override;
virtual Result DeleteDirectoryImpl(const char *path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
virtual Result CommitImpl() override;
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override;
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override;
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override;
/* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override;
virtual Result RollbackImpl() override;
};
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_common.hpp"
namespace ams::fs {
Result QueryMountSystemDataCacheSize(size_t *out, ncm::ProgramId data_id);
Result MountSystemData(const char *name, ncm::ProgramId data_id);
Result MountSystemData(const char *name, ncm::ProgramId data_id, void *cache_buffer, size_t cache_size);
}

View file

@ -16,6 +16,7 @@
#pragma once
#include "../fs_common.hpp"
#include "../fs_file.hpp"
#include "../fs_filesystem.hpp"
#include "../fs_operate_range.hpp"
namespace ams::fs::fsa {
@ -82,7 +83,25 @@ namespace ams::fs::fsa {
/* TODO: This is a hack to allow the mitm API to work. Find a better way? */
virtual sf::cmif::DomainObjectId GetDomainObjectId() const = 0;
protected:
/* ...? */
Result DryRead(size_t *out, s64 offset, size_t size, const ReadOption &option, fs::OpenMode mode) {
R_UNLESS((mode & fs::OpenMode_Read) != 0, fs::ResultInvalidOperationForOpenMode());
s64 file_size = 0;
R_TRY(this->GetSize(std::addressof(file_size)));
R_UNLESS(offset <= file_size, fs::ResultOutOfRange());
const size_t readable_size = file_size - offset;
*out = std::min(readable_size, size);
return ResultSuccess();
}
Result DrySetSize(s64 size, fs::OpenMode mode) {
R_UNLESS((mode & fs::OpenMode_Write) != 0, fs::ResultInvalidOperationForOpenMode());
AMS_ASSERT(size >= 0);
return ResultSuccess();
}
private:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0;
virtual Result GetSizeImpl(s64 *out) = 0;

View file

@ -29,7 +29,7 @@ namespace ams::fs::fsa {
Result Register(const char *name, std::unique_ptr<IFileSystem> &&fs);
Result Register(const char *name, std::unique_ptr<IFileSystem> &&fs, std::unique_ptr<ICommonMountNameGenerator> &&generator);
/* TODO: Register with cache settings */
Result Register(const char *name, std::unique_ptr<IFileSystem> &&fs, std::unique_ptr<ICommonMountNameGenerator> &&generator, bool use_data_cache, bool use_path_cache, bool multi_commit_supported);
void Unregister(const char *name);
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
namespace ams::fs::impl {
namespace {
Result OpenDataStorageByDataId(std::unique_ptr<ams::fs::IStorage> *out, ncm::ProgramId data_id, ncm::StorageId storage_id) {
/* Open storage using libnx bindings. */
::FsStorage s;
R_TRY_CATCH(fsOpenDataStorageByDataId(std::addressof(s), static_cast<u64>(data_id), static_cast<::NcmStorageId>(storage_id))) {
R_CONVERT(ncm::ResultContentMetaNotFound, fs::ResultTargetNotFound());
} R_END_TRY_CATCH;
std::unique_ptr<fs::IStorage> storage(new RemoteStorage(s));
R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInDataA());
*out = std::move(storage);
return ResultSuccess();
}
Result MountDataImpl(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_cache, bool use_data_cache, bool use_path_cache) {
std::unique_ptr<fs::IStorage> storage;
R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id));
std::unique_ptr<RomFsFileSystem> fs(new RomFsFileSystem());
R_UNLESS(fs != nullptr, fs::ResultAllocationFailureInDataB());
R_TRY(fs->Initialize(std::move(storage), cache_buffer, cache_size, use_cache));
return fsa::Register(name, std::move(fs), nullptr, use_data_cache, use_path_cache, false);
}
}
Result QueryMountDataCacheSize(size_t *out, ncm::ProgramId data_id, ncm::StorageId storage_id) {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
std::unique_ptr<fs::IStorage> storage;
R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id));
size_t size = 0;
R_TRY(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(size), storage.get()));
constexpr size_t MinimumCacheSize = 32;
*out = std::max(size, MinimumCacheSize);
return ResultSuccess();
}
Result MountData(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
return MountDataImpl(name, data_id, storage_id, nullptr, 0, false, false, false);
}
Result MountData(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, false, false);
}
Result MountData(const char *name, ncm::ProgramId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_data_cache, bool use_path_cache) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, use_data_cache, use_path_cache);
}
}

View file

@ -0,0 +1,578 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fs {
s64 HierarchicalRomFileTable::QueryDirectoryEntryStorageSize(u32 count) {
const size_t real_count = count + ReservedDirectoryCount;
return DirectoryEntryMapTable::QueryKeyValueStorageSize(real_count) + real_count * (RomPathTool::MaxPathLength + 1) * sizeof(RomPathChar);
}
s64 HierarchicalRomFileTable::QueryDirectoryEntryBucketStorageSize(s64 count) {
return DirectoryEntryMapTable::QueryBucketStorageSize(count);
}
s64 HierarchicalRomFileTable::QueryFileEntryStorageSize(u32 count) {
return FileEntryMapTable::QueryKeyValueStorageSize(count) + count * (RomPathTool::MaxPathLength + 1) * sizeof(RomPathChar);
}
s64 HierarchicalRomFileTable::QueryFileEntryBucketStorageSize(s64 count) {
return FileEntryMapTable::QueryBucketStorageSize(count);
}
Result HierarchicalRomFileTable::Format(SubStorage dir_bucket, SubStorage file_bucket) {
s64 dir_bucket_size;
R_TRY(dir_bucket.GetSize(std::addressof(dir_bucket_size)));
R_TRY(DirectoryEntryMapTable::Format(dir_bucket, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size)));
s64 file_bucket_size;
R_TRY(file_bucket.GetSize(std::addressof(file_bucket_size)));
R_TRY(FileEntryMapTable::Format(file_bucket, FileEntryMapTable::QueryBucketCount(file_bucket_size)));
return ResultSuccess();
}
HierarchicalRomFileTable::HierarchicalRomFileTable() { /* ... */ }
Result HierarchicalRomFileTable::Initialize(SubStorage dir_bucket, SubStorage dir_entry, SubStorage file_bucket, SubStorage file_entry) {
s64 dir_bucket_size;
R_TRY(dir_bucket.GetSize(std::addressof(dir_bucket_size)));
R_TRY(this->dir_table.Initialize(dir_bucket, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size), dir_entry));
s64 file_bucket_size;
R_TRY(file_bucket.GetSize(std::addressof(file_bucket_size)));
R_TRY(this->file_table.Initialize(file_bucket, FileEntryMapTable::QueryBucketCount(file_bucket_size), file_entry));
return ResultSuccess();
}
void HierarchicalRomFileTable::Finalize() {
this->dir_table.Finalize();
this->file_table.Finalize();
}
Result HierarchicalRomFileTable::CreateRootDirectory() {
Position root_pos = RootPosition;
EntryKey root_key = {};
root_key.key.parent = root_pos;
RomPathTool::InitializeRomEntryName(std::addressof(root_key.name));
RomDirectoryEntry root_entry = {
.next = InvalidPosition,
.dir = InvalidPosition,
.file = InvalidPosition,
};
return this->dir_table.Add(std::addressof(root_pos), root_key, root_entry);
}
Result HierarchicalRomFileTable::CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey new_key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(new_key), std::addressof(parent_entry), path));
R_TRY(this->CheckSameEntryExists(new_key, fs::ResultDbmAlreadyExists()));
RomDirectoryEntry new_entry = {
.next = InvalidPosition,
.dir = InvalidPosition,
.file = InvalidPosition,
};
Position new_pos = 0;
R_TRY_CATCH(this->dir_table.Add(std::addressof(new_pos), new_key, new_entry)) {
R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmDirectoryEntryFull())
} R_END_TRY_CATCH;
*out = ConvertToDirectoryId(new_pos);
if (parent_entry.dir == InvalidPosition) {
parent_entry.dir = new_pos;
R_TRY(this->dir_table.SetByPosition(new_key.key.parent, parent_entry));
} else {
Position cur_pos = parent_entry.dir;
while (true) {
RomEntryKey cur_key = {};
RomDirectoryEntry cur_entry = {};
R_TRY(this->dir_table.GetByPosition(std::addressof(cur_key), std::addressof(cur_entry), cur_pos));
if (cur_entry.next == InvalidPosition) {
cur_entry.next = new_pos;
R_TRY(this->dir_table.SetByPosition(cur_pos, cur_entry));
break;
}
cur_pos = cur_entry.next;
}
}
return ResultSuccess();
}
Result HierarchicalRomFileTable::CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey new_key = {};
R_TRY(this->FindFileRecursive(std::addressof(new_key), std::addressof(parent_entry), path));
R_TRY(this->CheckSameEntryExists(new_key, fs::ResultDbmAlreadyExists()));
RomFileEntry new_entry = {
.next = InvalidPosition,
.info = info,
};
Position new_pos = 0;
R_TRY_CATCH(this->file_table.Add(std::addressof(new_pos), new_key, new_entry)) {
R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmFileEntryFull())
} R_END_TRY_CATCH;
*out = ConvertToFileId(new_pos);
if (parent_entry.file == InvalidPosition) {
parent_entry.file = new_pos;
R_TRY(this->dir_table.SetByPosition(new_key.key.parent, parent_entry));
} else {
Position cur_pos = parent_entry.file;
while (true) {
RomEntryKey cur_key = {};
RomFileEntry cur_entry = {};
R_TRY(this->file_table.GetByPosition(std::addressof(cur_key), std::addressof(cur_entry), cur_pos));
if (cur_entry.next == InvalidPosition) {
cur_entry.next = new_pos;
R_TRY(this->file_table.SetByPosition(cur_pos, cur_entry));
break;
}
cur_pos = cur_entry.next;
}
}
return ResultSuccess();
}
Result HierarchicalRomFileTable::ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path));
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToDirectoryId(pos);
return ResultSuccess();
}
Result HierarchicalRomFileTable::ConvertPathToFileId(RomFileId *out, const RomPathChar *path) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path));
Position pos = 0;
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToFileId(pos);
return ResultSuccess();
}
Result HierarchicalRomFileTable::GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path));
return this->GetDirectoryInformation(out, key);
}
Result HierarchicalRomFileTable::GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id) {
AMS_ASSERT(out != nullptr);
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(entry), id));
return ResultSuccess();
}
Result HierarchicalRomFileTable::OpenFile(FileInfo *out, const RomPathChar *path) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindFileRecursive(std::addressof(key), std::addressof(parent_entry), path));
return this->OpenFile(out, key);
}
Result HierarchicalRomFileTable::OpenFile(FileInfo *out, RomFileId id) {
AMS_ASSERT(out != nullptr);
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(entry), id));
*out = entry.info;
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindOpen(FindPosition *out, const RomPathChar *path) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path));
return this->FindOpen(out, key);
}
Result HierarchicalRomFileTable::FindOpen(FindPosition *out, RomDirectoryId id) {
AMS_ASSERT(out != nullptr);
out->next_dir = InvalidPosition;
out->next_file = InvalidPosition;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(entry), id));
out->next_dir = entry.dir;
out->next_file = entry.file;
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindNextDirectory(RomPathChar *out, FindPosition *find, size_t length) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(find != nullptr);
AMS_ASSERT(length > RomPathTool::MaxPathLength);
R_UNLESS(find->next_dir != InvalidPosition, fs::ResultDbmFindFinished());
RomEntryKey key = {};
RomDirectoryEntry entry = {};
size_t aux_size = 0;
R_TRY(this->dir_table.GetByPosition(std::addressof(key), std::addressof(entry), out, std::addressof(aux_size), find->next_dir));
AMS_ASSERT(aux_size / sizeof(RomPathChar) <= RomPathTool::MaxPathLength);
out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator;
find->next_dir = entry.next;
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindNextFile(RomPathChar *out, FindPosition *find, size_t length) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(find != nullptr);
AMS_ASSERT(length > RomPathTool::MaxPathLength);
R_UNLESS(find->next_file != InvalidPosition, fs::ResultDbmFindFinished());
RomEntryKey key = {};
RomFileEntry entry = {};
size_t aux_size = 0;
R_TRY(this->file_table.GetByPosition(std::addressof(key), std::addressof(entry), out, std::addressof(aux_size), find->next_file));
AMS_ASSERT(aux_size / sizeof(RomPathChar) <= RomPathTool::MaxPathLength);
out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator;
find->next_file = entry.next;
return ResultSuccess();
}
Result HierarchicalRomFileTable::QueryRomFileSystemSize(s64 *out_dir_entry_size, s64 *out_file_entry_size) {
AMS_ASSERT(out_dir_entry_size != nullptr);
AMS_ASSERT(out_file_entry_size != nullptr);
*out_dir_entry_size = this->dir_table.GetTotalEntrySize();
*out_file_entry_size = this->file_table.GetTotalEntrySize();
return ResultSuccess();
}
Result HierarchicalRomFileTable::GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path) {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_dir_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
AMS_ASSERT(path != nullptr);
RomEntryKey gp_key = {};
RomDirectoryEntry gp_entry = {};
R_TRY(this->dir_table.GetByPosition(std::addressof(gp_key), std::addressof(gp_entry), pos));
out_dir_key->key.parent = gp_key.parent;
R_TRY(RomPathTool::GetParentDirectoryName(std::addressof(out_dir_key->name), name, path));
R_TRY(this->GetDirectoryEntry(out_pos, out_dir_entry, *out_dir_key));
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser *parser, const RomPathChar *path) {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_dir_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
AMS_ASSERT(parser != nullptr);
AMS_ASSERT(path != nullptr);
Position dir_pos = RootPosition;
EntryKey dir_key = {};
RomDirectoryEntry dir_entry = {};
dir_key.key.parent = RootPosition;
R_TRY(parser->GetNextDirectoryName(std::addressof(dir_key.name)));
R_TRY(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key));
Position parent_pos = dir_pos;
while (!parser->IsFinished()) {
EntryKey old_key = dir_key;
R_TRY(parser->GetNextDirectoryName(std::addressof(dir_key.name)));
if (RomPathTool::IsCurrentDirectory(dir_key.name)) {
dir_key = old_key;
continue;
} else if (RomPathTool::IsParentDirectory(dir_key.name)) {
R_UNLESS(parent_pos != RootPosition, fs::ResultDirectoryUnobtainable());
R_TRY(this->GetGrandParent(std::addressof(parent_pos), std::addressof(dir_key), std::addressof(dir_entry), dir_key.key.parent, dir_key.name, path));
} else {
dir_key.key.parent = parent_pos;
R_TRY_CATCH(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key)) {
R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultDbmNotFound())
} R_END_TRY_CATCH;
parent_pos = dir_pos;
}
}
*out_pos = parent_pos;
*out_dir_key = dir_key;
*out_dir_entry = dir_entry;
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindPathRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, bool is_dir, const RomPathChar *path) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
AMS_ASSERT(path != nullptr);
RomPathTool::PathParser parser;
R_TRY(parser.Initialize(path));
EntryKey parent_key = {};
Position parent_pos = 0;
R_TRY(this->FindParentDirectoryRecursive(std::addressof(parent_pos), std::addressof(parent_key), out_dir_entry, std::addressof(parser), path));
if (is_dir) {
RomPathTool::RomEntryName name = {};
R_TRY(parser.GetAsDirectoryName(std::addressof(name)));
if (RomPathTool::IsCurrentDirectory(name)) {
*out_key = parent_key;
if (out_key->key.parent != RootPosition) {
Position pos = 0;
R_TRY(this->GetGrandParent(std::addressof(pos), std::addressof(parent_key), out_dir_entry, out_key->key.parent, out_key->name, path));
}
} else if (RomPathTool::IsParentDirectory(name)) {
R_UNLESS(parent_pos != RootPosition, fs::ResultDirectoryUnobtainable());
Position pos = 0;
RomDirectoryEntry cur_entry = {};
R_TRY(this->GetGrandParent(std::addressof(pos), out_key, std::addressof(cur_entry), parent_key.key.parent, parent_key.name, path));
if (out_key->key.parent != RootPosition) {
R_TRY(this->GetGrandParent(std::addressof(pos), std::addressof(parent_key), out_dir_entry, out_key->key.parent, out_key->name, path));
}
} else {
out_key->name = name;
out_key->key.parent = (out_key->name.length > 0) ? parent_pos : RootPosition;
}
} else {
{
RomPathTool::RomEntryName name = {};
R_TRY(parser.GetAsDirectoryName(std::addressof(name)));
R_UNLESS(!RomPathTool::IsParentDirectory(name) || parent_pos != RootPosition, fs::ResultDirectoryUnobtainable());
}
R_UNLESS(!parser.IsDirectoryPath(), fs::ResultDbmInvalidOperation());
out_key->key.parent = parent_pos;
R_TRY(parser.GetAsFileName(std::addressof(out_key->name)));
}
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindDirectoryRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
AMS_ASSERT(path != nullptr);
return this->FindPathRecursive(out_key, out_dir_entry, true, path);
}
Result HierarchicalRomFileTable::FindFileRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
AMS_ASSERT(path != nullptr);
return this->FindPathRecursive(out_key, out_dir_entry, false, path);
}
Result HierarchicalRomFileTable::CheckSameEntryExists(const EntryKey &key, Result if_exists) {
/* Check dir */
{
Position pos = InvalidPosition;
RomDirectoryEntry entry = {};
const Result get_res = this->dir_table.Get(std::addressof(pos), std::addressof(entry), key);
if (!fs::ResultDbmKeyNotFound::Includes(get_res)) {
R_TRY(get_res);
return if_exists;
}
}
/* Check file */
{
Position pos = InvalidPosition;
RomFileEntry entry = {};
const Result get_res = this->file_table.Get(std::addressof(pos), std::addressof(entry), key);
if (!fs::ResultDbmKeyNotFound::Includes(get_res)) {
R_TRY(get_res);
return if_exists;
}
}
return ResultSuccess();
}
Result HierarchicalRomFileTable::GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key) {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_entry != nullptr);
const Result dir_res = this->dir_table.Get(out_pos, out_entry, key);
R_UNLESS(R_FAILED(dir_res), dir_res);
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), dir_res);
Position pos = 0;
RomFileEntry entry = {};
const Result file_res = this->file_table.Get(std::addressof(pos), std::addressof(entry), key);
R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound());
return file_res;
}
Result HierarchicalRomFileTable::GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id) {
AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id);
RomEntryKey key = {};
const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), out_entry, pos);
R_UNLESS(R_FAILED(dir_res), dir_res);
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), dir_res);
RomFileEntry entry = {};
const Result file_res = this->file_table.GetByPosition(std::addressof(key), std::addressof(entry), pos);
R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound());
return file_res;
}
Result HierarchicalRomFileTable::GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key) {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_entry != nullptr);
const Result file_res = this->file_table.Get(out_pos, out_entry, key);
R_UNLESS(R_FAILED(file_res), file_res);
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), file_res);
Position pos = 0;
RomDirectoryEntry entry = {};
const Result dir_res = this->dir_table.Get(std::addressof(pos), std::addressof(entry), key);
R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound());
return dir_res;
}
Result HierarchicalRomFileTable::GetFileEntry(RomFileEntry *out_entry, RomFileId id) {
AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id);
RomEntryKey key = {};
const Result file_res = this->file_table.GetByPosition(std::addressof(key), out_entry, pos);
R_UNLESS(R_FAILED(file_res), file_res);
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), file_res);
RomDirectoryEntry entry = {};
const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), std::addressof(entry), pos);
R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound());
return dir_res;
}
Result HierarchicalRomFileTable::GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key) {
AMS_ASSERT(out != nullptr);
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
return ResultSuccess();
}
Result HierarchicalRomFileTable::OpenFile(FileInfo *out, const EntryKey &key) {
AMS_ASSERT(out != nullptr);
Position pos = 0;
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key));
*out = entry.info;
return ResultSuccess();
}
Result HierarchicalRomFileTable::FindOpen(FindPosition *out, const EntryKey &key) {
AMS_ASSERT(out != nullptr);
out->next_dir = InvalidPosition;
out->next_file = InvalidPosition;
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
out->next_dir = entry.dir;
out->next_file = entry.file;
return ResultSuccess();
}
}

View file

@ -18,7 +18,7 @@
namespace ams::fs::RomPathTool {
Result PathParser::Initialize(const RomPathChar *path) {
AMS_ABORT_UNLESS(path != nullptr);
AMS_ASSERT(path != nullptr);
/* Require paths start with a separator, and skip repeated separators. */
R_UNLESS(IsSeparator(path[0]), fs::ResultDbmInvalidPathFormat());
@ -65,10 +65,10 @@ namespace ams::fs::RomPathTool {
}
Result PathParser::GetAsDirectoryName(RomEntryName *out) const {
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(this->prev_path_start != nullptr);
AMS_ABORT_UNLESS(this->prev_path_end != nullptr);
AMS_ABORT_UNLESS(this->next_path != nullptr);
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->prev_path_start != nullptr);
AMS_ASSERT(this->prev_path_end != nullptr);
AMS_ASSERT(this->next_path != nullptr);
const size_t len = this->prev_path_end - this->prev_path_start;
R_UNLESS(len <= MaxPathLength, fs::ResultDbmDirectoryNameTooLong());
@ -79,10 +79,10 @@ namespace ams::fs::RomPathTool {
}
Result PathParser::GetAsFileName(RomEntryName *out) const {
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(this->prev_path_start != nullptr);
AMS_ABORT_UNLESS(this->prev_path_end != nullptr);
AMS_ABORT_UNLESS(this->next_path != nullptr);
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->prev_path_start != nullptr);
AMS_ASSERT(this->prev_path_end != nullptr);
AMS_ASSERT(this->next_path != nullptr);
const size_t len = this->prev_path_end - this->prev_path_start;
R_UNLESS(len <= MaxPathLength, fs::ResultDbmFileNameTooLong());
@ -93,10 +93,10 @@ namespace ams::fs::RomPathTool {
}
Result PathParser::GetNextDirectoryName(RomEntryName *out) {
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(this->prev_path_start != nullptr);
AMS_ABORT_UNLESS(this->prev_path_end != nullptr);
AMS_ABORT_UNLESS(this->next_path != nullptr);
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->prev_path_start != nullptr);
AMS_ASSERT(this->prev_path_end != nullptr);
AMS_ASSERT(this->next_path != nullptr);
/* Set the current path to output. */
out->length = this->prev_path_end - this->prev_path_start;
@ -132,8 +132,8 @@ namespace ams::fs::RomPathTool {
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p) {
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(p != nullptr);
AMS_ASSERT(out != nullptr);
AMS_ASSERT(p != nullptr);
const RomPathChar *start = cur.path;
const RomPathChar *end = cur.path + cur.length - 1;
@ -158,6 +158,7 @@ namespace ams::fs::RomPathTool {
if (depth == 0) {
start = head + 1;
break;
}
while (IsSeparator(*head)) {

View file

@ -0,0 +1,540 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software {
} you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY {
} without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fs {
namespace {
Result ConvertNcaCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultNcaCorrupted::Includes(res));
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultInvalidNcaFileSystemType, fs::ResultInvalidRomNcaFileSystemType())
R_CONVERT(fs::ResultInvalidAcidFileSize, fs::ResultInvalidRomAcidFileSize())
R_CONVERT(fs::ResultInvalidAcidSize, fs::ResultInvalidRomAcidSize())
R_CONVERT(fs::ResultInvalidAcid, fs::ResultInvalidRomAcid())
R_CONVERT(fs::ResultAcidVerificationFailed, fs::ResultRomAcidVerificationFailed())
R_CONVERT(fs::ResultInvalidNcaSignature, fs::ResultInvalidRomNcaSignature())
R_CONVERT(fs::ResultNcaHeaderSignature1VerificationFailed, fs::ResultRomNcaHeaderSignature1VerificationFailed())
R_CONVERT(fs::ResultNcaHeaderSignature2VerificationFailed, fs::ResultRomNcaHeaderSignature2VerificationFailed())
R_CONVERT(fs::ResultNcaFsHeaderHashVerificationFailed, fs::ResultRomNcaFsHeaderHashVerificationFailed())
R_CONVERT(fs::ResultInvalidNcaKeyIndex, fs::ResultInvalidRomNcaKeyIndex())
R_CONVERT(fs::ResultInvalidNcaFsHeaderHashType, fs::ResultInvalidRomNcaFsHeaderHashType())
R_CONVERT(fs::ResultInvalidNcaFsHeaderEncryptionType, fs::ResultInvalidRomNcaFsHeaderEncryptionType())
R_CONVERT(fs::ResultInvalidHierarchicalSha256BlockSize, fs::ResultInvalidRomHierarchicalSha256BlockSize())
R_CONVERT(fs::ResultInvalidHierarchicalSha256LayerCount, fs::ResultInvalidRomHierarchicalSha256LayerCount())
R_CONVERT(fs::ResultHierarchicalSha256BaseStorageTooLarge, fs::ResultRomHierarchicalSha256BaseStorageTooLarge())
R_CONVERT(fs::ResultHierarchicalSha256HashVerificationFailed, fs::ResultRomHierarchicalSha256HashVerificationFailed())
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
AMS_ASSERT(false);
return fs::ResultNcaCorrupted();
}
Result ConvertIntegrityVerificationStorageCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultIntegrityVerificationStorageCorrupted::Includes(res));
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultIncorrectIntegrityVerificationMagic, fs::ResultIncorrectRomIntegrityVerificationMagic())
R_CONVERT(fs::ResultInvalidZeroHash, fs::ResultInvalidRomZeroHash())
R_CONVERT(fs::ResultNonRealDataVerificationFailed, fs::ResultRomNonRealDataVerificationFailed())
R_CONVERT(fs::ResultInvalidHierarchicalIntegrityVerificationLayerCount, fs::ResultInvalidRomHierarchicalIntegrityVerificationLayerCount())
R_CONVERT(fs::ResultClearedRealDataVerificationFailed, fs::ResultClearedRomRealDataVerificationFailed())
R_CONVERT(fs::ResultUnclearedRealDataVerificationFailed, fs::ResultUnclearedRomRealDataVerificationFailed())
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
AMS_ASSERT(false);
return fs::ResultIntegrityVerificationStorageCorrupted();
}
Result ConvertBuiltInStorageCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultBuiltInStorageCorrupted::Includes(res));
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultGptHeaderVerificationFailed, fs::ResultRomGptHeaderVerificationFailed())
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
AMS_ASSERT(false);
return fs::ResultBuiltInStorageCorrupted();
}
Result ConvertPartitionFileSystemCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultPartitionFileSystemCorrupted::Includes(res));
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultInvalidSha256PartitionHashTarget, fs::ResultInvalidRomSha256PartitionHashTarget())
R_CONVERT(fs::ResultSha256PartitionHashVerificationFailed, fs::ResultRomSha256PartitionHashVerificationFailed())
R_CONVERT(fs::ResultPartitionSignatureVerificationFailed, fs::ResultRomPartitionSignatureVerificationFailed())
R_CONVERT(fs::ResultSha256PartitionSignatureVerificationFailed, fs::ResultRomSha256PartitionSignatureVerificationFailed())
R_CONVERT(fs::ResultInvalidPartitionEntryOffset, fs::ResultInvalidRomPartitionEntryOffset())
R_CONVERT(fs::ResultInvalidSha256PartitionMetaDataSize, fs::ResultInvalidRomSha256PartitionMetaDataSize())
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
AMS_ASSERT(false);
return fs::ResultPartitionFileSystemCorrupted();
}
Result ConvertFatFileSystemCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultFatFileSystemCorrupted::Includes(res));
return res;
}
Result ConvertHostFileSystemCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultHostFileSystemCorrupted::Includes(res));
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultHostEntryCorrupted, fs::ResultRomHostEntryCorrupted())
R_CONVERT(fs::ResultHostFileDataCorrupted, fs::ResultRomHostFileDataCorrupted())
R_CONVERT(fs::ResultHostFileCorrupted, fs::ResultRomHostFileCorrupted())
R_CONVERT(fs::ResultInvalidHostHandle, fs::ResultInvalidRomHostHandle())
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
AMS_ASSERT(false);
return fs::ResultHostFileSystemCorrupted();
}
Result ConvertDatabaseCorruptedResult(Result res) {
AMS_ASSERT(fs::ResultDatabaseCorrupted::Includes(res));
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultInvalidAllocationTableBlock, fs::ResultInvalidRomAllocationTableBlock())
R_CONVERT(fs::ResultInvalidKeyValueListElementIndex, fs::ResultInvalidRomKeyValueListElementIndex())
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
AMS_ASSERT(false);
return fs::ResultDatabaseCorrupted();
}
Result ConvertRomFsResult(Result res) {
R_TRY_CATCH(res) {
R_CONVERT(fs::ResultUnsupportedVersion, fs::ResultUnsupportedRomVersion())
R_CONVERT(fs::ResultNcaCorrupted, ConvertNcaCorruptedResult(res))
R_CONVERT(fs::ResultIntegrityVerificationStorageCorrupted, ConvertIntegrityVerificationStorageCorruptedResult(res))
R_CONVERT(fs::ResultBuiltInStorageCorrupted, ConvertBuiltInStorageCorruptedResult(res))
R_CONVERT(fs::ResultPartitionFileSystemCorrupted, ConvertPartitionFileSystemCorruptedResult(res))
R_CONVERT(fs::ResultFatFileSystemCorrupted, ConvertFatFileSystemCorruptedResult(res))
R_CONVERT(fs::ResultHostFileSystemCorrupted, ConvertHostFileSystemCorruptedResult(res))
R_CONVERT(fs::ResultDatabaseCorrupted, ConvertDatabaseCorruptedResult(res))
R_CONVERT(fs::ResultNotFound, fs::ResultPathNotFound())
R_CONVERT(fs::ResultPermissionDenied, fs::ResultTargetLocked())
R_CONVERT(fs::ResultIncompatiblePath, fs::ResultPathNotFound())
} R_END_TRY_CATCH;
return ResultSuccess();
}
Result ReadFile(IStorage *storage, s64 offset, void *buffer, size_t size) {
AMS_ASSERT(storage != nullptr);
AMS_ASSERT(offset >= 0);
AMS_ASSERT(buffer != nullptr || size == 0);
return ConvertRomFsResult(storage->Read(offset, buffer, size));
}
Result ReadFileHeader(IStorage *storage, RomFileSystemInformation *out) {
AMS_ASSERT(storage != nullptr);
AMS_ASSERT(out != nullptr);
return ReadFile(storage, 0, out, sizeof(*out));
}
constexpr size_t CalculateRequiredWorkingMemorySize(const RomFileSystemInformation &header) {
const size_t needed_size = header.directory_bucket_size + header.directory_entry_size + header.file_bucket_size + header.file_entry_size;
return util::AlignUp(needed_size, 8);
}
class RomFsFile : public fsa::IFile, public impl::Newable {
private:
RomFsFileSystem *parent;
s64 start;
s64 end;
public:
RomFsFile(RomFsFileSystem *p, s64 s, s64 e) : parent(p), start(s), end(e) { /* ... */ }
virtual ~RomFsFile() { /* ... */ }
Result VerifyArguments(size_t *out, s64 offset, void *buf, size_t size, const fs::ReadOption &option) {
R_TRY(DryRead(out, offset, size, option, fs::OpenMode_Read));
AMS_ASSERT(this->GetStorage() != nullptr);
AMS_ASSERT(offset >= 0);
AMS_ASSERT(buf != nullptr || size == 0);
return ResultSuccess();
}
Result ConvertResult(Result res) const {
return ConvertRomFsResult(res);
}
s64 GetOffset() const {
return this->start;
}
s64 GetSize() const {
return this->end - this->start;
}
IStorage *GetStorage() {
return this->parent->GetBaseStorage();
}
public:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override {
size_t read_size = 0;
R_TRY(this->VerifyArguments(std::addressof(read_size), offset, buffer, size, option));
R_TRY(this->ConvertResult(this->GetStorage()->Read(offset + this->start, buffer, size)));
*out = read_size;
return ResultSuccess();
}
virtual Result GetSizeImpl(s64 *out) override {
*out = this->GetSize();
return ResultSuccess();
}
virtual Result FlushImpl() override {
return ResultSuccess();
}
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override {
return fs::ResultUnsupportedOperation();
}
virtual Result SetSizeImpl(s64 size) override {
return fs::ResultUnsupportedOperation();
}
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
switch (op_id) {
case OperationId::InvalidateCache:
case OperationId::QueryRange:
{
R_UNLESS(offset >= 0, fs::ResultOutOfRange());
R_UNLESS(this->GetSize() >= 0, fs::ResultOutOfRange());
}
default:
return fs::ResultUnsupportedOperation();
}
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
AMS_ABORT();
}
};
class RomFsDirectory : public fsa::IDirectory, public impl::Newable {
private:
using FindPosition = RomFsFileSystem::RomFileTable::FindPosition;
private:
RomFsFileSystem *parent;
FindPosition current_find;
FindPosition first_find;
fs::OpenDirectoryMode mode;
public:
RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : parent(p), current_find(f), first_find(f), mode(m) { /* ... */ }
virtual ~RomFsDirectory() override { /* ... */ }
public:
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) {
return this->ReadImpl(out_count, std::addressof(this->current_find), out_entries, max_entries);
}
virtual Result GetEntryCountImpl(s64 *out) {
FindPosition find = this->first_find;
return this->ReadImpl(out, std::addressof(find), nullptr, 0);
}
private:
Result ReadImpl(s64 *out_count, FindPosition *find, DirectoryEntry *out_entries, s64 max_entries) {
AMS_ASSERT(out_count != nullptr);
AMS_ASSERT(find != nullptr);
constexpr size_t NameBufferSize = fs::EntryNameLengthMax + 1;
char *name_buf = static_cast<char *>(::ams::fs::impl::Allocate(NameBufferSize));
R_UNLESS(name_buf != nullptr, fs::ResultAllocationFailureInRomFsFileSystemE());
ON_SCOPE_EXIT { ::ams::fs::impl::Deallocate(name_buf, NameBufferSize); };
s32 i = 0;
if (this->mode & fs::OpenDirectoryMode_Directory) {
while (i < max_entries || out_entries == nullptr) {
R_TRY_CATCH(this->parent->GetRomFileTable()->FindNextDirectory(name_buf, find, NameBufferSize)) {
R_CATCH(fs::ResultDbmFindFinished) { break; }
} R_END_TRY_CATCH;
if (out_entries) {
R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax);
out_entries[i].name[fs::EntryNameLengthMax] = '\x00';
out_entries[i].type = fs::DirectoryEntryType_Directory;
out_entries[i].file_size = 0;
}
i++;
}
}
if (this->mode & fs::OpenDirectoryMode_File) {
while (i < max_entries || out_entries == nullptr) {
auto file_pos = find->next_file;
R_TRY_CATCH(this->parent->GetRomFileTable()->FindNextFile(name_buf, find, NameBufferSize)) {
R_CATCH(fs::ResultDbmFindFinished) { break; }
} R_END_TRY_CATCH;
if (out_entries) {
R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax);
out_entries[i].name[fs::EntryNameLengthMax] = '\x00';
out_entries[i].type = fs::DirectoryEntryType_File;
RomFsFileSystem::RomFileTable::FileInfo file_info;
R_TRY(this->parent->GetRomFileTable()->OpenFile(std::addressof(file_info), this->parent->GetRomFileTable()->ConvertToFileId(file_pos)));
out_entries[i].file_size = file_info.size.Get();
}
i++;
}
}
*out_count = i;
return ResultSuccess();
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
AMS_ABORT();
}
};
}
RomFsFileSystem::RomFsFileSystem() : base_storage() {
/* ... */
}
RomFsFileSystem::~RomFsFileSystem() {
/* ... */
}
Result RomFsFileSystem::GetRequiredWorkingMemorySize(size_t *out, IStorage *storage) {
RomFileSystemInformation header;
R_TRY(ReadFileHeader(storage, std::addressof(header)));
*out = CalculateRequiredWorkingMemorySize(header);
return ResultSuccess();
}
Result RomFsFileSystem:: Initialize(IStorage *base, void *work, size_t work_size, bool use_cache) {
AMS_ABORT_UNLESS(!use_cache || work != nullptr);
AMS_ABORT_UNLESS(base != nullptr);
/* Read the header. */
RomFileSystemInformation header;
R_TRY(ReadFileHeader(base, std::addressof(header)));
/* Set up our storages. */
if (use_cache) {
const size_t needed_size = CalculateRequiredWorkingMemorySize(header);
R_UNLESS(work_size >= needed_size, fs::ResultPreconditionViolation());
u8 *buf = static_cast<u8 *>(work);
auto dir_bucket_buf = buf; buf += header.directory_bucket_size;
auto dir_entry_buf = buf; buf += header.directory_entry_size;
auto file_bucket_buf = buf; buf += header.file_bucket_size;
auto file_entry_buf = buf; buf += header.file_entry_size;
R_TRY(ReadFile(base, header.directory_bucket_offset, dir_bucket_buf, header.directory_bucket_size));
R_TRY(ReadFile(base, header.directory_entry_offset, dir_entry_buf, header.directory_entry_size));
R_TRY(ReadFile(base, header.file_bucket_offset, file_bucket_buf, header.file_bucket_size));
R_TRY(ReadFile(base, header.file_entry_offset, file_entry_buf, header.file_entry_size));
this->dir_bucket_storage.reset(new MemoryStorage(dir_bucket_buf, header.directory_bucket_size));
this->dir_entry_storage.reset(new MemoryStorage(dir_entry_buf, header.directory_entry_size));
this->file_bucket_storage.reset(new MemoryStorage(file_bucket_buf, header.file_bucket_size));
this->file_entry_storage.reset(new MemoryStorage(file_entry_buf, header.file_entry_size));
} else {
this->dir_bucket_storage.reset(new SubStorage(base, header.directory_bucket_offset, header.directory_bucket_size));
this->dir_entry_storage.reset(new SubStorage(base, header.directory_entry_offset, header.directory_entry_size));
this->file_bucket_storage.reset(new SubStorage(base, header.file_bucket_offset, header.file_bucket_size));
this->file_entry_storage.reset(new SubStorage(base, header.file_entry_offset, header.file_entry_size));
}
/* Ensure we allocated storages successfully. */
R_UNLESS(this->dir_bucket_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA());
R_UNLESS(this->dir_entry_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA());
R_UNLESS(this->file_bucket_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA());
R_UNLESS(this->file_entry_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA());
/* Initialize the rom table. */
{
SubStorage db(this->dir_bucket_storage.get(), 0, header.directory_bucket_size);
SubStorage de(this->dir_entry_storage.get(), 0, header.directory_entry_size);
SubStorage fb(this->file_bucket_storage.get(), 0, header.file_bucket_size);
SubStorage fe(this->file_entry_storage.get(), 0, header.file_entry_size);
R_TRY(this->rom_file_table.Initialize(db, de, fb, fe));
}
/* Set members. */
this->entry_size = header.body_offset;
this->base_storage = base;
return ResultSuccess();
}
Result RomFsFileSystem::Initialize(std::unique_ptr<IStorage>&& base, void *work, size_t work_size, bool use_cache) {
this->unique_storage = std::move(base);
return this->Initialize(this->unique_storage.get(), work, work_size, use_cache);
}
Result RomFsFileSystem::GetFileInfo(RomFileTable::FileInfo *out, const char *path) {
R_TRY_CATCH(this->rom_file_table.OpenFile(out, path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound());
R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultPathNotFound());
} R_END_TRY_CATCH;
return ResultSuccess();
}
IStorage *RomFsFileSystem::GetBaseStorage() {
return this->base_storage;
}
RomFsFileSystem::RomFileTable *RomFsFileSystem::GetRomFileTable() {
return std::addressof(this->rom_file_table);
}
Result RomFsFileSystem::GetFileBaseOffset(s64 *out, const char *path) {
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(path != nullptr);
RomFileTable::FileInfo info;
R_TRY(this->GetFileInfo(std::addressof(info), path));
*out = this->entry_size + info.offset.Get();
return ResultSuccess();
}
Result RomFsFileSystem::CreateFileImpl(const char *path, s64 size, int flags) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::DeleteFileImpl(const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::CreateDirectoryImpl(const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::DeleteDirectoryImpl(const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::DeleteDirectoryRecursivelyImpl(const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::RenameFileImpl(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::RenameDirectoryImpl(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) {
RomDirectoryInfo dir_info;
R_TRY_CATCH(this->rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
R_CATCH(fs::ResultDbmInvalidOperation) {
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
*out = fs::DirectoryEntryType_File;
return ResultSuccess();
}
} R_END_TRY_CATCH;
*out = fs::DirectoryEntryType_Directory;
return ResultSuccess();
}
Result RomFsFileSystem::OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
AMS_ASSERT(out_file != nullptr);
AMS_ASSERT(path != nullptr);
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
std::unique_ptr<RomFsFile> file(new RomFsFile(this, this->entry_size + file_info.offset.Get(), this->entry_size + file_info.offset.Get() + file_info.size.Get()));
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
*out_file = std::move(file);
return ResultSuccess();
}
Result RomFsFileSystem::OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
AMS_ASSERT(out_dir != nullptr);
AMS_ASSERT(path != nullptr);
RomFileTable::FindPosition find;
R_TRY_CATCH(this->rom_file_table.FindOpen(std::addressof(find), path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultPathNotFound())
} R_END_TRY_CATCH;
std::unique_ptr<RomFsDirectory> dir(new RomFsDirectory(this, find, mode));
R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInRomFsFileSystemC());
*out_dir = std::move(dir);
return ResultSuccess();
}
Result RomFsFileSystem::CommitImpl() {
return ResultSuccess();
}
Result RomFsFileSystem::GetFreeSpaceSizeImpl(s64 *out, const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::GetTotalSpaceSizeImpl(s64 *out, const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::CleanDirectoryRecursivelyImpl(const char *path) {
return fs::ResultUnsupportedOperation();
}
Result RomFsFileSystem::CommitProvisionallyImpl(s64 counter) {
return ResultSuccess();
}
Result RomFsFileSystem::RollbackImpl() {
return ResultSuccess();
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
namespace ams::fs {
Result QueryMountSystemDataCacheSize(size_t *out, ncm::ProgramId data_id) {
return impl::QueryMountDataCacheSize(out, data_id, ncm::StorageId::BuiltInSystem);
}
Result MountSystemData(const char *name, ncm::ProgramId data_id) {
return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem);
}
Result MountSystemData(const char *name, ncm::ProgramId data_id, void *cache_buffer, size_t cache_size) {
return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem, cache_buffer, cache_size);
}
}

View file

@ -44,8 +44,9 @@ namespace ams::fs {
R_DEFINE_ERROR_RANGE(GameCardAccessFailed, 2500, 2999);
R_DEFINE_ERROR_RESULT(NotImplemented, 3001);
R_DEFINE_ERROR_RESULT(OutOfRange, 3005);
R_DEFINE_ERROR_RESULT(NotImplemented, 3001);
R_DEFINE_ERROR_RESULT(UnsupportedVersion, 3002);
R_DEFINE_ERROR_RESULT(OutOfRange, 3005);
R_DEFINE_ERROR_RESULT(SystemPartitionNotReady, 3100);
@ -54,6 +55,8 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212);
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220);
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221);
R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222);
R_DEFINE_ERROR_RESULT(AllocationFailureInDataB, 3223);
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardA, 3225);
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226);
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227);
@ -61,11 +64,17 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244);
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245);
R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249);
R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352);
R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355);
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterB, 3366);
R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367);
R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377);
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407);
R_DEFINE_ERROR_RESULT(AllocationFailureInUserFileSystem, 3420);
@ -73,13 +82,115 @@ namespace ams::fs {
R_DEFINE_ERROR_RANGE(DataCorrupted, 4000, 4999);
R_DEFINE_ERROR_RANGE(RomCorrupted, 4001, 4299);
R_DEFINE_ERROR_RESULT(UnsupportedRomVersion, 4002);
R_DEFINE_ERROR_RANGE(RomNcaCorrupted, 4041, 4139);
R_DEFINE_ERROR_RANGE(RomNcaFileSystemCorrupted, 4051, 4069);
R_DEFINE_ERROR_RESULT(InvalidRomNcaFileSystemType, 4052);
R_DEFINE_ERROR_RESULT(InvalidRomAcidFileSize, 4053);
R_DEFINE_ERROR_RESULT(InvalidRomAcidSize, 4054);
R_DEFINE_ERROR_RESULT(InvalidRomAcid, 4055);
R_DEFINE_ERROR_RESULT(RomAcidVerificationFailed, 4056);
R_DEFINE_ERROR_RESULT(InvalidRomNcaSignature, 4057);
R_DEFINE_ERROR_RESULT(RomNcaHeaderSignature1VerificationFailed, 4058);
R_DEFINE_ERROR_RESULT(RomNcaHeaderSignature2VerificationFailed, 4059);
R_DEFINE_ERROR_RESULT(RomNcaFsHeaderHashVerificationFailed, 4060);
R_DEFINE_ERROR_RESULT(InvalidRomNcaKeyIndex, 4061);
R_DEFINE_ERROR_RESULT(InvalidRomNcaFsHeaderHashType, 4062);
R_DEFINE_ERROR_RESULT(InvalidRomNcaFsHeaderEncryptionType, 4063);
R_DEFINE_ERROR_RANGE(RomNcaHierarchicalSha256StorageCorrupted, 4071, 4079);
R_DEFINE_ERROR_RESULT(InvalidRomHierarchicalSha256BlockSize, 4072);
R_DEFINE_ERROR_RESULT(InvalidRomHierarchicalSha256LayerCount, 4073);
R_DEFINE_ERROR_RESULT(RomHierarchicalSha256BaseStorageTooLarge, 4074);
R_DEFINE_ERROR_RESULT(RomHierarchicalSha256HashVerificationFailed, 4075);
R_DEFINE_ERROR_RANGE(RomIntegrityVerificationStorageCorrupted, 4141, 4179);
R_DEFINE_ERROR_RESULT(IncorrectRomIntegrityVerificationMagic, 4142);
R_DEFINE_ERROR_RESULT(InvalidRomZeroHash, 4143);
R_DEFINE_ERROR_RESULT(RomNonRealDataVerificationFailed, 4144);
R_DEFINE_ERROR_RESULT(InvalidRomHierarchicalIntegrityVerificationLayerCount, 4145);
R_DEFINE_ERROR_RANGE(RomRealDataVerificationFailed, 4151, 4159);
R_DEFINE_ERROR_RESULT(ClearedRomRealDataVerificationFailed, 4152);
R_DEFINE_ERROR_RESULT(UnclearedRomRealDataVerificationFailed, 4153);
R_DEFINE_ERROR_RANGE(RomPartitionFileSystemCorrupted, 4181, 4199);
R_DEFINE_ERROR_RESULT(InvalidRomSha256PartitionHashTarget, 4182);
R_DEFINE_ERROR_RESULT(RomSha256PartitionHashVerificationFailed, 4183);
R_DEFINE_ERROR_RESULT(RomPartitionSignatureVerificationFailed, 4184);
R_DEFINE_ERROR_RESULT(RomSha256PartitionSignatureVerificationFailed, 4185);
R_DEFINE_ERROR_RESULT(InvalidRomPartitionEntryOffset, 4186);
R_DEFINE_ERROR_RESULT(InvalidRomSha256PartitionMetaDataSize, 4187);
R_DEFINE_ERROR_RANGE(RomBuiltInStorageCorrupted, 4201, 4219);
R_DEFINE_ERROR_RESULT(RomGptHeaderVerificationFailed, 4202);
R_DEFINE_ERROR_RANGE(RomHostFileSystemCorrupted, 4241, 4259);
R_DEFINE_ERROR_RESULT(RomHostEntryCorrupted, 4242);
R_DEFINE_ERROR_RESULT(RomHostFileDataCorrupted, 4243);
R_DEFINE_ERROR_RESULT(RomHostFileCorrupted, 4244);
R_DEFINE_ERROR_RESULT(InvalidRomHostHandle, 4245);
R_DEFINE_ERROR_RANGE(RomDatabaseCorrupted, 4261, 4279);
R_DEFINE_ERROR_RESULT(InvalidRomAllocationTableBlock, 4262);
R_DEFINE_ERROR_RESULT(InvalidRomKeyValueListElementIndex, 4263);
R_DEFINE_ERROR_RANGE(SaveDataCorrupted, 4301, 4499);
R_DEFINE_ERROR_RANGE(NcaCorrupted, 4501, 4599);
R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4511, 4529);
R_DEFINE_ERROR_RESULT(InvalidNcaFileSystemType, 4512);
R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513);
R_DEFINE_ERROR_RESULT(InvalidAcidSize, 4514);
R_DEFINE_ERROR_RESULT(InvalidAcid, 4515);
R_DEFINE_ERROR_RESULT(AcidVerificationFailed, 4516);
R_DEFINE_ERROR_RESULT(InvalidNcaSignature, 4517);
R_DEFINE_ERROR_RESULT(NcaHeaderSignature1VerificationFailed, 4518);
R_DEFINE_ERROR_RESULT(NcaHeaderSignature2VerificationFailed, 4519);
R_DEFINE_ERROR_RESULT(NcaFsHeaderHashVerificationFailed, 4520);
R_DEFINE_ERROR_RESULT(InvalidNcaKeyIndex, 4521);
R_DEFINE_ERROR_RESULT(InvalidNcaFsHeaderHashType, 4522);
R_DEFINE_ERROR_RESULT(InvalidNcaFsHeaderEncryptionType, 4523);
R_DEFINE_ERROR_RANGE(NcaHierarchicalSha256StorageCorrupted, 4531, 4539);
R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256BlockSize, 4532);
R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256LayerCount, 4533);
R_DEFINE_ERROR_RESULT(HierarchicalSha256BaseStorageTooLarge, 4534);
R_DEFINE_ERROR_RESULT(HierarchicalSha256HashVerificationFailed, 4535);
R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639);
R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602);
R_DEFINE_ERROR_RESULT(InvalidZeroHash, 4603);
R_DEFINE_ERROR_RESULT(NonRealDataVerificationFailed, 4604);
R_DEFINE_ERROR_RESULT(InvalidHierarchicalIntegrityVerificationLayerCount, 4605);
R_DEFINE_ERROR_RANGE(RealDataVerificationFailed, 4611, 4619);
R_DEFINE_ERROR_RESULT(ClearedRealDataVerificationFailed, 4612);
R_DEFINE_ERROR_RESULT(UnclearedRealDataVerificationFailed, 4613);
R_DEFINE_ERROR_RANGE(PartitionFileSystemCorrupted, 4641, 4659);
R_DEFINE_ERROR_RESULT(InvalidSha256PartitionHashTarget, 4642);
R_DEFINE_ERROR_RESULT(Sha256PartitionHashVerificationFailed, 4643);
R_DEFINE_ERROR_RESULT(PartitionSignatureVerificationFailed, 4644);
R_DEFINE_ERROR_RESULT(Sha256PartitionSignatureVerificationFailed, 4645);
R_DEFINE_ERROR_RESULT(InvalidPartitionEntryOffset, 4646);
R_DEFINE_ERROR_RESULT(InvalidSha256PartitionMetaDataSize, 4647);
R_DEFINE_ERROR_RANGE(BuiltInStorageCorrupted, 4661, 4679);
R_DEFINE_ERROR_RESULT(GptHeaderVerificationFailed, 4662);
R_DEFINE_ERROR_RANGE(FatFileSystemCorrupted, 4681, 4699);
R_DEFINE_ERROR_RANGE(HostFileSystemCorrupted, 4701, 4719);
R_DEFINE_ERROR_RESULT(HostEntryCorrupted, 4702);
R_DEFINE_ERROR_RESULT(HostFileDataCorrupted, 4703);
R_DEFINE_ERROR_RESULT(HostFileCorrupted, 4704);
R_DEFINE_ERROR_RESULT(InvalidHostHandle, 4705);
R_DEFINE_ERROR_RANGE(DatabaseCorrupted, 4721, 4739);
R_DEFINE_ERROR_RESULT(InvalidAllocationTableBlock, 4722);
R_DEFINE_ERROR_RESULT(InvalidKeyValueListElementIndex, 4723);
R_DEFINE_ERROR_RANGE(AesXtsFileSystemCorrupted, 4741, 4759);
R_DEFINE_ERROR_RANGE(SaveDataTransferDataCorrupted, 4761, 4769);
R_DEFINE_ERROR_RANGE(SignedSystemPartitionDataCorrupted, 4771, 4779);
@ -132,6 +243,8 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocatorAlignmentViolation, 6461);
R_DEFINE_ERROR_RESULT(UserNotExist, 6465);
R_DEFINE_ERROR_RANGE(NotFound, 6600, 6699);
R_DEFINE_ERROR_RANGE(OutOfResource, 6700, 6799);
R_DEFINE_ERROR_RESULT(MappingTableFull, 6706);
R_DEFINE_ERROR_RESULT(OpenCountLimit, 6709);
@ -143,9 +256,24 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(NotInitialized, 6902);
R_DEFINE_ERROR_RESULT(NotMounted, 6905);
R_DEFINE_ERROR_RESULT(DbmInvalidOperation, 7914);
R_DEFINE_ERROR_RESULT(DbmInvalidPathFormat, 7915);
R_DEFINE_ERROR_RESULT(DbmDirectoryNameTooLong, 7916);
R_DEFINE_ERROR_RESULT(DbmFileNameTooLong, 7917);
R_DEFINE_ERROR_RANGE(DbmNotFound, 7901, 7904);
R_DEFINE_ERROR_RESULT(DbmKeyNotFound, 7902);
R_DEFINE_ERROR_RESULT(DbmFileNotFound, 7903);
R_DEFINE_ERROR_RESULT(DbmDirectoryNotFound, 7904);
R_DEFINE_ERROR_RESULT(DbmAlreadyExists, 7906);
R_DEFINE_ERROR_RESULT(DbmKeyFull, 7907);
R_DEFINE_ERROR_RESULT(DbmDirectoryEntryFull, 7908);
R_DEFINE_ERROR_RESULT(DbmFileEntryFull, 7909);
R_DEFINE_ERROR_RANGE(DbmFindFinished, 7910, 7912);
R_DEFINE_ERROR_RESULT(DbmFindKeyFinished, 7911);
R_DEFINE_ERROR_RESULT(DbmIterationFinished, 7912);
R_DEFINE_ERROR_RESULT(DbmInvalidOperation, 7914);
R_DEFINE_ERROR_RESULT(DbmInvalidPathFormat, 7915);
R_DEFINE_ERROR_RESULT(DbmDirectoryNameTooLong, 7916);
R_DEFINE_ERROR_RESULT(DbmFileNameTooLong, 7917);
}