diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index d19df40b3..7ecf5abae 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -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" diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp index 5a50e5704..0ebd91b37 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp @@ -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((v & static_cast(0x00000000FFFFFFFFul)) >> 0); + this->high = static_cast((v & static_cast(0xFFFFFFFF00000000ul)) >> 32); + } + + constexpr ALWAYS_INLINE s64 Get() const { + return (static_cast(this->high) << 32) | (static_cast(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::value); + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_data.hpp new file mode 100644 index 000000000..0fb4d4068 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_data.hpp @@ -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 . + */ +#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); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp new file mode 100644 index 000000000..d2306154b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp @@ -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 . + */ +#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::value); + + using DirectoryInfo = RomDirectoryInfo; + using FileInfo = RomFileInfo; + + static constexpr RomFileId ConvertToFileId(Position pos) { + return static_cast(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(pos); + } + + static constexpr Position ConvertToPosition(RomDirectoryId id) { + return static_cast(id); + } + + static_assert(std::is_same::value); + + struct RomDirectoryEntry { + Position next; + Position dir; + Position file; + }; + static_assert(std::is_pod::value); + + struct RomFileEntry { + Position next; + FileInfo info; + }; + static_assert(std::is_pod::value); + + static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength; + + template + class EntryMapTable : public RomKeyValueStorage { + public: + using ImplKey = ImplKeyType; + using ClientKey = ClientKeyType; + using Value = ValueType; + using Position = HierarchicalRomFileTable::Position; + using Base = RomKeyValueStorage; + 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(aux_lhs), reinterpret_cast(aux_rhs), aux_lhs_size / sizeof(RomPathChar)); + } + }; + static_assert(std::is_pod::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(static_cast::type>(*(name++))); + hash = ((hash >> 5) | (hash << 27)) ^ cur; + } + return hash; + } + }; + static_assert(std::is_pod::value); + + using DirectoryEntryMapTable = EntryMapTable; + using FileEntryMapTable = EntryMapTable; + 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); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp new file mode 100644 index 000000000..e52b260e5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp @@ -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 . + */ +#pragma once +#include "fs_dbm_rom_types.hpp" +#include "fs_substorage.hpp" + +namespace ams::fs { + + template + 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::value); + private: + static constexpr inline Position InvalidPosition = ~Position(); + + struct Element { + Key key; + Value value; + Position next; + u32 size; + }; + static_assert(std::is_pod::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(-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(-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(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(::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(kv_size), fs::ResultDbmKeyFull()); + + *out = static_cast(this->total_entry_size); + + this->total_entry_size = util::AlignUp(static_cast(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(); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_rom_path_tool.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp similarity index 99% rename from libraries/libstratosphere/include/stratosphere/fs/fs_rom_path_tool.hpp rename to libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp index dd57aba5f..c57b1fe9d 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_rom_path_tool.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp @@ -14,7 +14,7 @@ * along with this program. If not, see . */ #pragma once -#include "fs_rom_types.hpp" +#include "fs_dbm_rom_types.hpp" namespace ams::fs { diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_rom_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp similarity index 97% rename from libraries/libstratosphere/include/stratosphere/fs/fs_rom_types.hpp rename to libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp index cfffd658f..86a646e45 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_rom_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp @@ -43,8 +43,8 @@ namespace ams::fs { static_assert(std::is_pod::value); struct RomFileInfo { - s64 offset; - s64 size; + Int64 offset; + Int64 size; }; static_assert(std::is_pod::value); diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp new file mode 100644 index 000000000..60529972e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp @@ -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 . + */ +#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(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(dst)->Clear(); + return ResultSuccess(); + default: + return fs::ResultUnsupportedOperation(); + } + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp index ef195d2f7..554dcdfef 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp @@ -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: diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp new file mode 100644 index 000000000..36ae47186 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp @@ -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 . + */ +#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 unique_storage; + std::unique_ptr dir_bucket_storage; + std::unique_ptr dir_entry_storage; + std::unique_ptr file_bucket_storage; + std::unique_ptr 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&& 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 *out_file, const char *path, fs::OpenMode mode) override; + virtual Result OpenDirectoryImpl(std::unique_ptr *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; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp new file mode 100644 index 000000000..43cba17ee --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp @@ -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 . + */ +#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); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp index 77e59d6f9..931a63bd5 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp @@ -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; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp index 5135f0f22..36d283407 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp @@ -29,7 +29,7 @@ namespace ams::fs::fsa { Result Register(const char *name, std::unique_ptr &&fs); Result Register(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator); - /* TODO: Register with cache settings */ + Result Register(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator, bool use_data_cache, bool use_path_cache, bool multi_commit_supported); void Unregister(const char *name); } diff --git a/libraries/libstratosphere/source/fs/fs_data.cpp b/libraries/libstratosphere/source/fs/fs_data.cpp new file mode 100644 index 000000000..b1d78d58f --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_data.cpp @@ -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 . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs::impl { + + namespace { + + Result OpenDataStorageByDataId(std::unique_ptr *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(data_id), static_cast<::NcmStorageId>(storage_id))) { + R_CONVERT(ncm::ResultContentMetaNotFound, fs::ResultTargetNotFound()); + } R_END_TRY_CATCH; + + std::unique_ptr 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 storage; + R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id)); + + std::unique_ptr 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 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); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp b/libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp new file mode 100644 index 000000000..b0067efcd --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp @@ -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 . + */ +#include + +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(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/fs/fs_rom_path_tool.cpp b/libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp similarity index 86% rename from libraries/libstratosphere/source/fs/fs_rom_path_tool.cpp rename to libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp index c758c86dc..a5f10379c 100644 --- a/libraries/libstratosphere/source/fs/fs_rom_path_tool.cpp +++ b/libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp @@ -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)) { diff --git a/libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp b/libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp new file mode 100644 index 000000000..42a4165ea --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp @@ -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 . + */ +#include + +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(::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(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&& 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 *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 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 *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 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(); + } +} diff --git a/libraries/libstratosphere/source/fs/fs_system_data.cpp b/libraries/libstratosphere/source/fs/fs_system_data.cpp new file mode 100644 index 000000000..6f7db7b71 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_system_data.cpp @@ -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 . + */ +#include +#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); + } + +} diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index e7a72155a..db9a5f86d 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -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); }