fssystem: add RomFsFileSystem

This commit is contained in:
Michael Scire 2020-04-04 02:37:21 -07:00
parent a50d6a2696
commit 8d1ada2a1b
17 changed files with 2057 additions and 21 deletions

View file

@ -115,12 +115,11 @@ namespace ams::fs {
return this->GetImpl(std::addressof(pos), out, key, hash_key, aux, aux_size);
}
Result FindOpen(FindIndex *out) const {
void FindOpen(FindIndex *out) const {
AMS_ASSERT(out != nullptr);
out->ind = static_cast<BucketIndex>(-1);
out->pos = InvalidPosition;
return ResultSuccess();
}
Result FindNext(Key *out_key, Value *out_val, FindIndex *find) {

View file

@ -82,7 +82,7 @@ namespace ams::fs {
private:
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
/* Only allow opening files with mode = read. */
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
std::unique_ptr<fsa::IFile> base_file;
R_TRY(this->base_fs->OpenFile(std::addressof(base_file), path, mode));

View file

@ -83,16 +83,16 @@ namespace ams::fs::fsa {
Result OpenFile(std::unique_ptr<IFile> *out_file, const char *path, OpenMode mode) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument());
R_UNLESS((mode & OpenMode_ReadWrite) != 0, fs::ResultInvalidArgument());
R_UNLESS((mode & ~OpenMode_All) == 0, fs::ResultInvalidArgument());
R_UNLESS((mode & OpenMode_ReadWrite) != 0, fs::ResultInvalidOpenMode());
R_UNLESS((mode & ~OpenMode_All) == 0, fs::ResultInvalidOpenMode());
return this->OpenFileImpl(out_file, path, mode);
}
Result OpenDirectory(std::unique_ptr<IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument());
R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidArgument());
R_UNLESS((mode & ~(OpenDirectoryMode_All | OpenDirectoryMode_NotRequireFileSize)) == 0, fs::ResultInvalidArgument());
R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidOpenMode());
R_UNLESS((mode & ~(OpenDirectoryMode_All | OpenDirectoryMode_NotRequireFileSize)) == 0, fs::ResultInvalidOpenMode());
return this->OpenDirectoryImpl(out_dir, path, mode);
}

View file

@ -15,11 +15,13 @@
*/
#pragma once
#include "fssystem/fssystem_utility.hpp"
#include "fssystem/fssystem_external_code.hpp"
#include "fssystem/fssystem_partition_file_system.hpp"
#include "fssystem/fssystem_partition_file_system_meta.hpp"
#include "fssystem/fssystem_path_tool.hpp"
#include "fssystem/fssystem_subdirectory_filesystem.hpp"
#include "fssystem/fssystem_directory_redirection_filesystem.hpp"
#include "fssystem/fssystem_directory_savedata_filesystem.hpp"
#include <stratosphere/fssystem/fssystem_utility.hpp>
#include <stratosphere/fssystem/fssystem_external_code.hpp>
#include <stratosphere/fssystem/fssystem_partition_file_system.hpp>
#include <stratosphere/fssystem/fssystem_partition_file_system_meta.hpp>
#include <stratosphere/fssystem/fssystem_path_tool.hpp>
#include <stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp>
#include <stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp>
#include <stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp>
#include <stratosphere/fssystem/fssystem_romfs_file_system.hpp>
#include <stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp>

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os.hpp>
namespace ams::fssystem::buffers {
namespace impl {
constexpr inline auto RetryWait = TimeSpan::FromMilliSeconds(10);
}
template<typename F, typename OnFailure>
Result DoContinuouslyUntilBufferIsAllocated(F f, OnFailure on_failure, const char *function_name) {
constexpr auto BufferAllocationRetryLogCountMax = 10;
constexpr auto BufferAllocationRetryLogInterval = 100;
for (auto count = 1; true; count++) {
R_TRY_CATCH(f()) {
R_CATCH(fs::ResultBufferAllocationFailed) {
if ((1 <= count && count <= BufferAllocationRetryLogCountMax) || ((count % BufferAllocationRetryLogInterval) == 0)) {
/* TODO: Log */
}
R_TRY(on_failure());
/* TODO: os::SleepThread */
svc::SleepThread(impl::RetryWait.GetNanoSeconds());
continue;
}
} R_END_TRY_CATCH;
return ResultSuccess();
}
}
template<typename F>
Result DoContinuouslyUntilBufferIsAllocated(F f, const char *function_name) {
R_TRY(DoContinuouslyUntilBufferIsAllocated(f, []() ALWAYS_INLINE_LAMBDA { return ResultSuccess(); }, function_name));
return ResultSuccess();
}
class BufferManagerContext {
private:
bool needs_blocking;
public:
constexpr BufferManagerContext() : needs_blocking(false) { /* ... */ }
public:
bool IsNeedBlocking() const { return this->needs_blocking; }
void SetNeedBlocking(bool need) { this->needs_blocking = need; }
};
void RegisterBufferManagerContext(const BufferManagerContext *context);
BufferManagerContext *GetBufferManagerContext();
void EnableBlockingBufferManagerAllocation();
class ScopedBufferManagerContextRegistration {
private:
BufferManagerContext cur_context;
const BufferManagerContext *old_context;
public:
ALWAYS_INLINE explicit ScopedBufferManagerContextRegistration() {
this->old_context = GetBufferManagerContext();
if (this->old_context != nullptr) {
this->cur_context = *this->old_context;
}
RegisterBufferManagerContext(std::addressof(this->cur_context));
}
ALWAYS_INLINE ~ScopedBufferManagerContextRegistration() {
RegisterBufferManagerContext(this->old_context);
}
};
}

View file

@ -0,0 +1,684 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_types.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_path_tool.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_key_value_storage.hpp>
namespace ams::fssystem {
template<typename DBS, typename DES, typename FBS, typename FES>
class HierarchicalRomFileTable {
private:
using DirectoryBucketStorage = DBS;
using DirectoryEntryStorage = DES;
using FileBucketStorage = FBS;
using FileEntryStorage = FES;
public:
using Position = u32;
struct FindPosition {
Position next_dir;
Position next_file;
};
static_assert(std::is_pod<FindPosition>::value);
using DirectoryInfo = RomDirectoryInfo;
using FileInfo = RomFileInfo;
static constexpr RomFileId ConvertToFileId(Position pos) {
return static_cast<RomFileId>(pos);
}
private:
static constexpr inline Position InvalidPosition = ~Position();
static constexpr inline Position RootPosition = 0;
static constexpr inline size_t ReservedDirectoryCount = 1;
static constexpr RomDirectoryId ConvertToDirectoryId(Position pos) {
return static_cast<RomDirectoryId>(pos);
}
static constexpr Position ConvertToPosition(RomDirectoryId id) {
return static_cast<Position>(id);
}
static_assert(std::is_same<RomDirectoryId, RomFileId>::value);
struct RomDirectoryEntry {
Position next;
Position dir;
Position file;
};
static_assert(std::is_pod<RomDirectoryEntry>::value);
struct RomFileEntry {
Position next;
FileInfo info;
};
static_assert(std::is_pod<RomFileEntry>::value);
static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength;
template<typename BucketStorageType, typename EntryStorageType, typename ImplKeyType, typename ClientKeyType, typename ValueType>
class EntryMapTable : public RomKeyValueStorage<BucketStorageType, EntryStorageType, ImplKeyType, ValueType, MaxKeyLength> {
private:
using BucketStorage = BucketStorageType;
using EntryStorage = EntryStorageType;
public:
using ImplKey = ImplKeyType;
using ClientKey = ClientKeyType;
using Value = ValueType;
using Position = HierarchicalRomFileTable::Position;
using Base = RomKeyValueStorage<BucketStorageType, EntryStorageType, ImplKeyType, ValueType, MaxKeyLength>;
public:
Result Add(Position *out, const ClientKeyType &key, const Value &value) {
return Base::AddImpl(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value);
}
Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) const {
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, void *out_aux, size_t *out_aux_size, Position pos) const {
return Base::GetByPosition(out_key, out_val, out_aux, out_aux_size, pos);
}
Result SetByPosition(Position pos, const Value &value, fs::WriteOption option) const {
return Base::SetByPosition(pos, value, option);
}
};
struct RomEntryKey {
Position parent;
bool IsEqual(const RomEntryKey &rhs, const void *aux_lhs, size_t aux_lhs_size, const void *aux_rhs, size_t aux_rhs_size) const {
if (this->parent != rhs.parent) {
return false;
}
if (aux_lhs_size != aux_rhs_size) {
return false;
}
return RomPathTool::IsEqualPath(reinterpret_cast<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar));
}
};
static_assert(std::is_pod<RomEntryKey>::value);
struct EntryKey {
RomEntryKey key;
RomPathTool::RomEntryName name;
constexpr u32 Hash() const {
u32 hash = this->key.parent ^ 123456789;
const RomPathChar *name = this->name.path;
const RomPathChar *end = name + this->name.length;
while (name < end) {
const u32 cur = static_cast<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++)));
hash = ((hash >> 5) | (hash << 27)) ^ cur;
}
return hash;
}
};
static_assert(std::is_pod<EntryKey>::value);
using DirectoryEntryMapTable = EntryMapTable<DirectoryBucketStorage, DirectoryEntryStorage, RomEntryKey, EntryKey, RomDirectoryEntry>;
using FileEntryMapTable = EntryMapTable<FileBucketStorage, FileEntryStorage, RomEntryKey, EntryKey, RomFileEntry>;
private:
DirectoryEntryMapTable dir_table;
FileEntryMapTable file_table;
public:
static u32 QueryDirectoryEntrySize(u32 name_len) {
AMS_ABORT_UNLESS(name_len <= RomPathTool::MaxPathLength);
return DirectoryEntryMapTable::QueryEntrySize(name_len * sizeof(RomPathChar));
}
static u32 QueryFileEntrySize(u32 name_len) {
AMS_ABORT_UNLESS(name_len <= RomPathTool::MaxPathLength);
return FileEntryMapTable::QueryEntrySize(name_len * sizeof(RomPathChar));
}
static u32 QueryDirectoryEntryBucketStorageSize(u32 count) { return DirectoryEntryMapTable::QueryBucketStorageSize(count); }
static u32 QueryFileEntryBucketStorageSize(u32 count) { return FileEntryMapTable::QueryBucketStorageSize(count); }
static Result Format(DirectoryBucketStorage *dir_bucket, s64 dir_bucket_ofs, u32 dir_bucket_size, DirectoryEntryStorage *dir_entry, s64 dir_entry_ofs, u32 dir_entry_size, FileBucketStorage *file_bucket, s64 file_bucket_ofs, u32 file_bucket_size, FileEntryStorage *file_entry, s64 file_entry_ofs, u32 file_entry_size) {
R_TRY(DirectoryEntryMapTable::Format(dir_bucket, dir_bucket_ofs, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size), dir_entry, dir_entry_ofs, dir_entry_size));
R_TRY(FileEntryMapTable::Format(file_bucket, file_bucket_ofs, FileEntryMapTable::QueryBucketCount(file_bucket_size), file_entry, file_entry_ofs, file_entry_size));
return ResultSuccess();
}
public:
HierarchicalRomFileTable() { /* ... */ }
constexpr u32 GetDirectoryEntryCount() const {
return this->dir_table.GetEntryCount();
}
constexpr u32 GetFileEntryCount() const {
return this->file_table.GetEntryCount();
}
Result Initialize(DirectoryBucketStorage *dir_bucket, s64 dir_bucket_ofs, u32 dir_bucket_size, DirectoryEntryStorage *dir_entry, s64 dir_entry_ofs, u32 dir_entry_size, FileBucketStorage *file_bucket, s64 file_bucket_ofs, u32 file_bucket_size, FileEntryStorage *file_entry, s64 file_entry_ofs, u32 file_entry_size) {
AMS_ASSERT(dir_bucket != nullptr);
AMS_ASSERT(dir_entry != nullptr);
AMS_ASSERT(file_bucket != nullptr);
AMS_ASSERT(file_entry != nullptr);
R_TRY(this->dir_table.Initialize(dir_bucket, dir_bucket_ofs, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size), dir_entry, dir_entry_ofs, dir_entry_size));
R_TRY(this->file_table.Initialize(file_bucket, file_bucket_ofs, FileEntryMapTable::QueryBucketCount(file_bucket_size), file_entry, file_entry_ofs, file_entry_size));
return ResultSuccess();
}
void Finalize() {
this->dir_table.Finalize();
this->file_table.Finalize();
}
Result 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 CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey new_key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(new_key), 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, fs::WriteOption::None));
} 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), nullptr, nullptr, cur_pos));
if (cur_entry.next == InvalidPosition) {
cur_entry.next = new_pos;
R_TRY(this->dir_table.SetByPosition(cur_pos, cur_entry, fs::WriteOption::None));
break;
}
cur_pos = cur_entry.next;
}
}
return ResultSuccess();
}
Result CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey new_key = {};
R_TRY(this->FindFileRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(new_key), 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, fs::WriteOption::None));
} 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), nullptr, nullptr, cur_pos));
if (cur_entry.next == InvalidPosition) {
cur_entry.next = new_pos;
R_TRY(this->file_table.SetByPosition(cur_pos, cur_entry, fs::WriteOption::None));
break;
}
cur_pos = cur_entry.next;
}
}
return ResultSuccess();
}
Result ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToDirectoryId(pos);
return ResultSuccess();
}
Result ConvertPathToFileId(RomFileId *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
Position pos = 0;
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToFileId(pos);
return ResultSuccess();
}
Result GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
return this->GetDirectoryInformation(out, key);
}
Result GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id) const {
AMS_ASSERT(out != nullptr);
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(entry), id));
return ResultSuccess();
}
Result OpenFile(FileInfo *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindFileRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
return this->OpenFile(out, key);
}
Result OpenFile(FileInfo *out, RomFileId id) const {
AMS_ASSERT(out != nullptr);
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(entry), id));
*out = entry.info;
return ResultSuccess();
}
Result FindOpen(FindPosition *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
return this->FindOpen(out, key);
}
Result FindOpen(FindPosition *out, RomDirectoryId id) const {
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 FindNextDirectory(RomPathChar *out, FindPosition *find) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(find != nullptr);
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));
out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator;
find->next_dir = entry.next;
return ResultSuccess();
}
Result FindNextFile(RomPathChar *out, FindPosition *find) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(find != nullptr);
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));
out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator;
find->next_file = entry.next;
return ResultSuccess();
}
Result QueryRomFileSystemSize(u32 *out_dir_entry_size, u32 *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();
}
private:
Result GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_dir_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
RomEntryKey gp_key = {};
RomDirectoryEntry gp_entry = {};
R_TRY(this->dir_table.GetByPosition(std::addressof(gp_key), std::addressof(gp_entry), nullptr, nullptr, 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 FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser &parser, const RomPathChar *path) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_dir_key != nullptr);
AMS_ASSERT(out_dir_entry != 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::ResultDbmInvalidOperation());
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(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key));
parent_pos = dir_pos;
}
}
*out_pos = parent_pos;
*out_dir_key = dir_key;
*out_dir_entry = dir_entry;
return ResultSuccess();
}
Result FindPathRecursive(EntryKey *out_parent_key, RomDirectoryEntry *out_parent_dir_entry, EntryKey *out_key, bool is_dir, const RomPathChar *path) const {
AMS_ASSERT(out_parent_key != nullptr);
AMS_ASSERT(out_parent_dir_entry != nullptr);
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(path != nullptr);
RomPathTool::PathParser parser;
R_TRY(parser.Initialize(path));
Position parent_pos = 0;
R_TRY(this->FindParentDirectoryRecursive(std::addressof(parent_pos), out_parent_key, out_parent_dir_entry, parser, path));
if (is_dir) {
RomPathTool::RomEntryName name = {};
R_TRY(parser.GetAsDirectoryName(std::addressof(name)));
if (RomPathTool::IsCurrentDirectory(name)) {
*out_key = *out_parent_key;
if (out_key->key.parent != RootPosition) {
Position pos = 0;
R_TRY(this->GetGrandParent(std::addressof(pos), out_parent_key, out_parent_dir_entry, out_key->key.parent, out_key->name, path));
}
} else if (RomPathTool::IsParentDirectory(name)) {
R_UNLESS(parent_pos != RootPosition, fs::ResultDbmInvalidOperation());
Position pos = 0;
RomDirectoryEntry cur_entry = {};
R_TRY(this->GetGrandParent(std::addressof(pos), out_key, std::addressof(cur_entry), out_parent_key->key.parent, out_parent_key->name, path));
if (out_key->key.parent != RootPosition) {
R_TRY(this->GetGrandParent(std::addressof(pos), out_parent_key, out_parent_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 {
R_UNLESS(!parser.IsDirectoryPath(), fs::ResultDbmInvalidOperation());
out_key->key.parent = parent_pos;
R_TRY(parser.GetAsFileName(std::addressof(out_key->name)));
}
return ResultSuccess();
}
Result FindDirectoryRecursive(EntryKey *out_parent_key, RomDirectoryEntry *out_parent_dir_entry, EntryKey *out_key, const RomPathChar *path) const {
return this->FindPathRecursive(out_parent_key, out_parent_dir_entry, out_key, true, path);
}
Result FindFileRecursive(EntryKey *out_parent_key, RomDirectoryEntry *out_parent_dir_entry, EntryKey *out_key, const RomPathChar *path) const {
return this->FindPathRecursive(out_parent_key, out_parent_dir_entry, out_key, false, path);
}
Result CheckSameEntryExists(const EntryKey &key, Result if_exists) const {
/* 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 GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key) const {
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 GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id) const {
AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id);
RomEntryKey key = {};
const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), out_entry, nullptr, nullptr, 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), nullptr, nullptr, pos);
R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound());
return file_res;
}
Result GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key) const {
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 GetFileEntry(RomFileEntry *out_entry, RomFileId id) const {
AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id);
RomEntryKey key = {};
const Result file_res = this->file_table.GetByPosition(std::addressof(key), out_entry, nullptr, nullptr, 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), nullptr, nullptr, pos);
R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound());
return dir_res;
}
Result GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key) const {
AMS_ASSERT(out != nullptr);
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
return ResultSuccess();
}
Result OpenFile(FileInfo *out, const EntryKey &key) const {
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 FindOpen(FindPosition *out, const EntryKey &key) const {
AMS_ASSERT(out != nullptr);
out->next_dir = InvalidPosition;
out->next_file = InvalidPosition;
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
out->next_dir = entry.dir;
out->next_file = entry.file;
return ResultSuccess();
}
};
}

View file

@ -0,0 +1,367 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_types.hpp>
namespace ams::fssystem {
constexpr ALWAYS_INLINE u32 AlignRomAddress(u32 addr) {
return util::AlignUp(addr, sizeof(addr));
}
template<typename BucketStorageType, typename EntryStorageType, typename KeyType, typename ValueType, size_t MaxAuxiliarySize>
class RomKeyValueStorage {
public:
using BucketStorage = BucketStorageType;
using EntryStorage = EntryStorageType;
using Key = KeyType;
using Value = ValueType;
using Position = u32;
using BucketIndex = u32;
struct FindIndex {
BucketIndex ind;
Position pos;
};
static_assert(std::is_pod<FindIndex>::value);
private:
static constexpr inline Position InvalidPosition = ~Position();
struct Element {
Key key;
Value value;
Position next;
u32 size;
};
static_assert(std::is_pod<Element>::value);
private:
s64 bucket_offset;
u32 bucket_count;
BucketStorage *bucket_storage;
s64 kv_offset;
u32 kv_size;
EntryStorage *kv_storage;
u32 total_entry_size;
u32 entry_count;
public:
static constexpr u32 QueryEntrySize(u32 aux_size) {
return AlignRomAddress(sizeof(Element) + aux_size);
}
static constexpr u32 QueryBucketStorageSize(u32 num) {
return num * sizeof(Position);
}
static constexpr u32 QueryBucketCount(u32 size) {
return size / sizeof(Position);
}
static constexpr u32 QueryKeyValueStorageSize(u32 num) {
return num * sizeof(Element);
}
static Result Format(BucketStorage *bucket, s64 bucket_ofs, u32 bucket_count, EntryStorage *kv, s64 kv_ofs, u32 kv_size) {
AMS_ASSERT(bucket != nullptr);
AMS_ASSERT(kv != nullptr);
AMS_ASSERT(kv_size >= 0);
const Position pos = InvalidPosition;
for (s64 i = 0; i < bucket_count; i++) {
R_TRY(bucket->Write(bucket_ofs + i * sizeof(pos), std::addressof(pos), sizeof(pos)));
}
return ResultSuccess();
}
public:
RomKeyValueStorage() : bucket_offset(), bucket_count(), bucket_storage(), kv_offset(), kv_size(), kv_storage(), total_entry_size(), entry_count() { /* ... */ }
Result Initialize(BucketStorage *bucket, s64 bucket_ofs, u32 bucket_count, EntryStorage *kv, s64 kv_ofs, u32 kv_size) {
AMS_ASSERT(bucket != nullptr);
AMS_ASSERT(kv != nullptr);
AMS_ASSERT(bucket_count > 0);
this->bucket_storage = bucket;
this->bucket_offset = bucket_ofs;
this->bucket_count = bucket_count;
this->kv_storage = kv;
this->kv_offset = kv_ofs;
this->kv_size = kv_size;
return ResultSuccess();
}
void Finalize() {
this->bucket_storage = nullptr;
this->bucket_offset = 0;
this->bucket_count = 0;
this->kv_storage = nullptr;
this->kv_offset = 0;
this->kv_size = 0;
}
constexpr u32 GetTotalEntrySize() const {
return this->total_entry_size;
}
constexpr u32 GetFreeSize() const {
return (this->kv_size - this->total_entry_size);
}
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);
}
void FindOpen(FindIndex *out) const {
AMS_ASSERT(out != nullptr);
out->ind = static_cast<BucketIndex>(-1);
out->pos = InvalidPosition;
}
Result FindNext(Key *out_key, Value *out_val, FindIndex *find) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(find != nullptr);
Element elem;
BucketIndex ind = find->ind;
R_UNLESS((ind < this->bucket_count) || ind == static_cast<BucketIndex>(-1), fs::ResultDbmFindKeyFinished());
while (true) {
if (find->pos != InvalidPosition) {
R_TRY(this->ReadKeyValue(std::addressof(elem), nullptr, nullptr, find->pos));
AMS_ASSERT(elem.next == InvalidPosition || elem.next < this->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 < this->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) {
Position pos, prev_pos;
Element elem;
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->bucket_count > 0);
AMS_ASSERT(this->kv_size >= 0);
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);
R_TRY(this->AllocateEntry(std::addressof(pos), aux_size));
Position next_pos;
R_TRY(this->LinkEntry(std::addressof(next_pos), pos, hash_key));
elem = { key, value, next_pos, static_cast<u32>(aux_size) };
*out = pos;
R_TRY(this->WriteKeyValue(std::addressof(elem), pos, aux, aux_size, fs::WriteOption::None));
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) const {
Position pos, prev_pos;
Element elem;
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_val != nullptr);
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, void *out_aux, size_t *out_aux_size, Position pos) const {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != 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, fs::WriteOption option) const {
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), nullptr, nullptr, pos));
elem.value = value;
return this->WriteKeyValue(std::addressof(elem), pos, nullptr, 0, option);
}
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) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_prev != nullptr);
AMS_ASSERT(out_elem != nullptr);
AMS_ASSERT(this->bucket_count > 0);
AMS_ASSERT(this->kv_size >= 0);
*out_pos = 0;
*out_prev = 0;
const BucketIndex ind = HashToBucket(hash_key);
Position cur;
R_TRY(this->ReadBucket(std::addressof(cur), ind));
AMS_ASSERT(cur == InvalidPosition || cur < this->kv_size);
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
u8 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);
R_UNLESS(this->total_entry_size + sizeof(Element) + aux_size <= this->kv_size, fs::ResultDbmKeyFull());
*out = static_cast<Position>(this->total_entry_size);
this->total_entry_size = AlignRomAddress(this->total_entry_size + sizeof(Element) + static_cast<u32>(aux_size));
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));
AMS_ASSERT(next == InvalidPosition || next < this->kv_size);
R_TRY(this->WriteBucket(pos, ind, fs::WriteOption::None));
*out = next;
return ResultSuccess();
}
Result ReadBucket(Position *out, BucketIndex ind) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->bucket_storage != nullptr);
AMS_ASSERT(ind < this->bucket_count);
const s64 offset = this->bucket_offset + ind * sizeof(Position);
return this->bucket_storage->Read(offset, out, sizeof(*out));
}
Result WriteBucket(Position pos, BucketIndex ind, fs::WriteOption option) const {
AMS_ASSERT(this->bucket_storage != nullptr);
AMS_ASSERT(ind < this->bucket_count);
const s64 offset = this->bucket_offset + ind * sizeof(Position);
return this->bucket_storage.Write(offset, std::addressof(pos), sizeof(pos));
}
Result ReadKeyValue(Element *out, void *out_aux, size_t *out_aux_size, Position pos) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->kv_storage != nullptr);
AMS_ASSERT(pos < this->kv_size);
const s64 offset = this->kv_offset + pos;
R_TRY(this->kv_storage->Read(offset, out, sizeof(*out)));
if (out_aux != nullptr && out_aux_size != nullptr) {
*out_aux_size = out->size;
if (out->size > 0) {
R_TRY(this->kv_storage->Read(offset + sizeof(*out), out_aux, out->size));
}
}
return ResultSuccess();
}
Result WriteKeyValue(const Element *elem, Position pos, const void *aux, size_t aux_size, fs::WriteOption option) const {
AMS_ASSERT(elem != nullptr);
AMS_ASSERT(this->kv_storage != nullptr);
AMS_ASSERT(pos < this->kv_size);
const s64 offset = this->kv_offset + pos;
R_TRY(this->kv_storage->Write(offset, elem, sizeof(*elem)));
if (aux != nullptr && aux_size > 0) {
R_TRY(this->kv_storage->Write(offset + sizeof(*elem), aux, aux_size));
}
return ResultSuccess();
}
};
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fssystem/fssystem_dbm_rom_types.hpp>
namespace ams::fssystem {
namespace RomPathTool {
constexpr inline u32 MaxPathLength = 0x300;
struct RomEntryName {
size_t length;
const RomPathChar *path;
};
static_assert(std::is_pod<RomEntryName>::value);
constexpr void InitializeRomEntryName(RomEntryName *entry) {
entry->length = 0;
}
constexpr inline bool IsSeparator(RomPathChar c) {
return c == RomStringTraits::DirectorySeparator;
}
constexpr inline bool IsNullTerminator(RomPathChar c) {
return c == RomStringTraits::NullTerminator;
}
constexpr inline bool IsDot(RomPathChar c) {
return c == RomStringTraits::Dot;
}
constexpr inline bool IsCurrentDirectory(const RomEntryName &name) {
return name.length == 1 && IsDot(name.path[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p, size_t length) {
return length == 1 && IsDot(p[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p) {
return IsDot(p[0]) && IsNullTerminator(p[1]);
}
constexpr inline bool IsParentDirectory(const RomEntryName &name) {
return name.length == 2 && IsDot(name.path[0]) && IsDot(name.path[1]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p) {
return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) {
return length == 2 && IsDot(p[0]) && IsDot(p[1]);
}
constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) {
return std::strncmp(lhs, rhs, length) == 0;
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomPathChar *rhs) {
if (strnlen(rhs, MaxPathLength) != lhs.length) {
return false;
}
return IsEqualPath(lhs.path, rhs, lhs.length);
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomEntryName &rhs) {
if (lhs.length != rhs.length) {
return false;
}
return IsEqualPath(lhs.path, rhs.path, lhs.length);
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p);
class PathParser {
private:
const RomPathChar *prev_path_start;
const RomPathChar *prev_path_end;
const RomPathChar *next_path;
bool finished;
public:
constexpr PathParser() : prev_path_start(), prev_path_end(), next_path(), finished() { /* ... */ }
Result Initialize(const RomPathChar *path);
void Finalize();
bool IsFinished() const;
bool IsDirectoryPath() const;
Result GetAsDirectoryName(RomEntryName *out) const;
Result GetAsFileName(RomEntryName *out) const;
Result GetNextDirectoryName(RomEntryName *out);
};
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fs/fs_common.hpp>
namespace ams::fssystem {
using RomPathChar = char;
using RomFileId = s32;
using RomDirectoryId = s32;
struct RomFileSystemInformation {
s64 size;
s64 directory_bucket_offset;
s64 directory_bucket_size;
s64 directory_entry_offset;
s64 directory_entry_size;
s64 file_bucket_offset;
s64 file_bucket_size;
s64 file_entry_offset;
s64 file_entry_size;
s64 body_offset;
};
static_assert(std::is_pod<RomFileSystemInformation>::value);
static_assert(sizeof(RomFileSystemInformation) == 0x50);
struct RomDirectoryInfo {
/* ... */
};
static_assert(std::is_pod<RomDirectoryInfo>::value);
struct RomFileInfo {
fs::Int64 offset;
fs::Int64 size;
};
static_assert(std::is_pod<RomFileInfo>::value);
namespace RomStringTraits {
constexpr inline char DirectorySeparator = '/';
constexpr inline char NullTerminator = '\x00';
constexpr inline char Dot = '.';
}
}

View file

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

View file

@ -202,7 +202,7 @@ namespace ams::fs {
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
/* Only allow opening files with mode = read. */
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
/* If we support redirection, we'd like to prefer a file from the sd card. */
if (this->is_redirect) {
@ -252,7 +252,7 @@ namespace ams::fs {
R_UNLESS(this->initialized, fs::ResultNotInitialized());
/* Only allow opening files with mode = read. */
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
/* First, check if there's an external code. */
{

View file

@ -233,6 +233,13 @@ namespace ams::fs {
{
R_UNLESS(offset >= 0, fs::ResultOutOfRange());
R_UNLESS(this->GetSize() >= 0, fs::ResultOutOfRange());
auto operate_size = size;
if (offset + operate_size > this->GetSize() || offset + operate_size < offset) {
operate_size = this->GetSize() - offset;
}
return this->GetStorage()->OperateRange(dst, dst_size, op_id, this->start + offset, operate_size, src, src_size);
}
default:
return fs::ResultUnsupportedOperationInRomFsFileB();
@ -481,7 +488,7 @@ namespace ams::fs {
AMS_ASSERT(out_file != nullptr);
AMS_ASSERT(path != nullptr);
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
@ -515,7 +522,8 @@ namespace ams::fs {
}
Result RomFsFileSystem::GetFreeSpaceSizeImpl(s64 *out, const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemC();
*out = 0;
return ResultSuccess();
}
Result RomFsFileSystem::GetTotalSpaceSizeImpl(s64 *out, const char *path) {

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem::buffers {
namespace {
/* TODO: os::SdkThreadLocalStorage g_buffer_manager_context_tls_slot; */
class ThreadLocalStorageWrapper {
private:
os::TlsSlot tls_slot;
public:
ThreadLocalStorageWrapper() { R_ABORT_UNLESS(os::AllocateTlsSlot(std::addressof(this->tls_slot), nullptr)); }
~ThreadLocalStorageWrapper() { os::FreeTlsSlot(this->tls_slot); }
void SetValue(uintptr_t value) { os::SetTlsValue(this->tls_slot, value); }
uintptr_t GetValue() const { return os::GetTlsValue(this->tls_slot); }
os::TlsSlot GetTlsSlot() const { return this->tls_slot; }
} g_buffer_manager_context_tls_slot;
}
void RegisterBufferManagerContext(const BufferManagerContext *context) {
g_buffer_manager_context_tls_slot.SetValue(reinterpret_cast<uintptr_t>(context));
}
BufferManagerContext *GetBufferManagerContext() {
return reinterpret_cast<BufferManagerContext *>(g_buffer_manager_context_tls_slot.GetValue());
}
void EnableBlockingBufferManagerAllocation() {
if (auto context = GetBufferManagerContext(); context != nullptr) {
context->SetNeedBlocking(true);
}
}
}

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem::RomPathTool {
Result PathParser::Initialize(const RomPathChar *path) {
AMS_ASSERT(path != nullptr);
/* Require paths start with a separator, and skip repeated separators. */
R_UNLESS(IsSeparator(path[0]), fs::ResultDbmInvalidPathFormat());
while (IsSeparator(path[1])) {
path++;
}
this->prev_path_start = path;
this->prev_path_end = path;
this->next_path = path + 1;
while (IsSeparator(this->next_path[0])) {
this->next_path++;
}
return ResultSuccess();
}
void PathParser::Finalize() {
/* ... */
}
bool PathParser::IsFinished() const {
return this->finished;
}
bool PathParser::IsDirectoryPath() const {
AMS_ASSERT(this->next_path != nullptr);
if (IsNullTerminator(this->next_path[0]) && IsSeparator(this->next_path[-1])) {
return true;
}
if (IsCurrentDirectory(this->next_path)) {
return true;
}
if (IsParentDirectory(this->next_path)) {
return true;
}
return false;
}
Result PathParser::GetAsDirectoryName(RomEntryName *out) const {
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());
out->length = len;
out->path = this->prev_path_start;
return ResultSuccess();
}
Result PathParser::GetAsFileName(RomEntryName *out) const {
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());
out->length = len;
out->path = this->prev_path_start;
return ResultSuccess();
}
Result PathParser::GetNextDirectoryName(RomEntryName *out) {
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;
out->path = this->prev_path_start;
/* Parse the next path. */
this->prev_path_start = this->next_path;
const RomPathChar *cur = this->next_path;
for (size_t name_len = 0; true; name_len++) {
if (IsSeparator(cur[name_len])) {
R_UNLESS(name_len < MaxPathLength, fs::ResultDbmDirectoryNameTooLong());
this->prev_path_end = cur + name_len;
this->next_path = this->prev_path_end + 1;
while (IsSeparator(this->next_path[0])) {
this->next_path++;
}
if (IsNullTerminator(this->next_path[0])) {
this->finished = true;
}
break;
}
if (IsNullTerminator(cur[name_len])) {
this->finished = true;
this->prev_path_end = this->next_path = cur + name_len;
break;
}
}
return ResultSuccess();
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p) {
const RomPathChar *start = cur.path;
const RomPathChar *end = cur.path + cur.length - 1;
s32 depth = 1;
if (IsParentDirectory(cur)) {
depth++;
}
if (cur.path > p) {
size_t len = 0;
const RomPathChar *head = cur.path - 1;
while (head >= p) {
if (IsSeparator(*head)) {
if (IsCurrentDirectory(head + 1, len)) {
depth++;
}
if (IsParentDirectory(head + 1, len)) {
depth += 2;
}
if (depth == 0) {
start = head + 1;
break;
}
while (IsSeparator(*head)) {
head--;
}
end = head;
len = 0;
depth--;
}
len++;
head--;
}
R_UNLESS(depth == 0, fs::ResultDbmInvalidPathFormat());
if (head == p) {
start = p + 1;
}
}
if (end <= p) {
out->path = p;
out->length = 0;
} else {
out->path = start;
out->length = end - start + 1;
}
return ResultSuccess();
}
}

View file

@ -0,0 +1,396 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem {
namespace {
constexpr size_t CalculateRequiredWorkingMemorySize(const RomFileSystemInformation &header) {
return header.directory_bucket_size + header.directory_entry_size + header.file_bucket_size + header.file_entry_size;
}
class RomFsFile : public ams::fs::fsa::IFile, public ams::fs::impl::Newable {
private:
RomFsFileSystem *parent;
s64 start;
s64 end;
private:
s64 GetSize() const {
return this->end - this->start;
}
public:
RomFsFile(RomFsFileSystem *p, s64 s, s64 e) : parent(p), start(s), end(e) { /* ... */ }
virtual ~RomFsFile() { /* ... */ }
public:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result {
size_t read_size = 0;
R_TRY(this->DryRead(std::addressof(read_size), offset, size, option, fs::OpenMode_Read));
R_TRY(this->parent->GetBaseStorage()->Read(offset + this->start, buffer, read_size));
*out = read_size;
return ResultSuccess();
}, AMS_CURRENT_FUNCTION_NAME));
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 {
bool needs_append;
R_TRY(this->DryWrite(std::addressof(needs_append), offset, size, option, fs::OpenMode_Read));
AMS_ASSERT(needs_append == false);
return fs::ResultUnsupportedOperationInRomFsFileA();
}
virtual Result SetSizeImpl(s64 size) override {
R_TRY(this->DrySetSize(size, fs::OpenMode_Read));
return fs::ResultUnsupportedOperationInRomFsFileA();
}
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 fs::OperationId::InvalidateCache:
case fs::OperationId::QueryRange:
{
R_UNLESS(offset >= 0, fs::ResultOutOfRange());
R_UNLESS(this->GetSize() >= 0, fs::ResultOutOfRange());
auto operate_size = size;
if (offset + operate_size > this->GetSize() || offset + operate_size < offset) {
operate_size = this->GetSize() - offset;
}
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result {
R_TRY(this->parent->GetBaseStorage()->OperateRange(dst, dst_size, op_id, this->start + offset, operate_size, src, src_size));
return ResultSuccess();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
}
default:
return fs::ResultUnsupportedOperationInRomFsFileB();
}
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
AMS_ABORT();
}
};
class RomFsDirectory : public ams::fs::fsa::IDirectory, public ams::fs::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, fs::DirectoryEntry *out_entries, s64 max_entries) {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result {
return this->ReadImpl(out_count, std::addressof(this->current_find), out_entries, max_entries);
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
}
virtual Result GetEntryCountImpl(s64 *out) {
FindPosition find = this->first_find;
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY(this->ReadImpl(out, std::addressof(find), nullptr, 0));
return ResultSuccess();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
}
private:
Result ReadImpl(s64 *out_count, FindPosition *find, fs::DirectoryEntry *out_entries, s64 max_entries) {
constexpr size_t NameBufferSize = fs::EntryNameLengthMax + 1;
RomPathChar name[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, find)) {
R_CATCH(fs::ResultDbmFindFinished) { break; }
} R_END_TRY_CATCH;
if (out_entries) {
R_UNLESS(strnlen(name, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
strncpy(out_entries[i].name, name, 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, find)) {
R_CATCH(fs::ResultDbmFindFinished) { break; }
} R_END_TRY_CATCH;
if (out_entries) {
R_UNLESS(strnlen(name, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
strncpy(out_entries[i].name, name, 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() {
/* ... */
}
fs::IStorage *RomFsFileSystem::GetBaseStorage() {
return this->base_storage;
}
RomFsFileSystem::RomFileTable *RomFsFileSystem::GetRomFileTable() {
return std::addressof(this->rom_file_table);
}
Result RomFsFileSystem::GetRequiredWorkingMemorySize(size_t *out, fs::IStorage *storage) {
RomFileSystemInformation header;
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY(storage->Read(0, std::addressof(header), sizeof(header)));
return ResultSuccess();
}, AMS_CURRENT_FUNCTION_NAME));
*out = CalculateRequiredWorkingMemorySize(header);
return ResultSuccess();
}
Result RomFsFileSystem::Initialize(fs::IStorage *base, void *work, size_t work_size, bool use_cache) {
AMS_ABORT_UNLESS(!use_cache || work != nullptr);
AMS_ABORT_UNLESS(base != nullptr);
/* Register blocking context for the scope. */
buffers::ScopedBufferManagerContextRegistration _sr;
buffers::EnableBlockingBufferManagerAllocation();
/* Read the header. */
RomFileSystemInformation header;
R_TRY(base->Read(0, std::addressof(header), sizeof(header)));
/* Set up our storages. */
if (use_cache) {
const size_t needed_size = CalculateRequiredWorkingMemorySize(header);
R_UNLESS(work_size >= needed_size, fs::ResultAllocationFailureInRomFsFileSystemA());
u8 *buf = static_cast<u8 *>(work);
auto dir_bucket_buf = buf; buf += header.directory_bucket_size;
auto dir_entry_buf = buf; buf += header.directory_entry_size;
auto file_bucket_buf = buf; buf += header.file_bucket_size;
auto file_entry_buf = buf; buf += header.file_entry_size;
R_TRY(base->Read(header.directory_bucket_offset, dir_bucket_buf, static_cast<size_t>(header.directory_bucket_size)));
R_TRY(base->Read(header.directory_entry_offset, dir_entry_buf, static_cast<size_t>(header.directory_entry_size)));
R_TRY(base->Read(header.file_bucket_offset, file_bucket_buf, static_cast<size_t>(header.file_bucket_size)));
R_TRY(base->Read(header.file_entry_offset, file_entry_buf, static_cast<size_t>(header.file_entry_size)));
this->dir_bucket_storage.reset(new fs::MemoryStorage(dir_bucket_buf, header.directory_bucket_size));
this->dir_entry_storage.reset(new fs::MemoryStorage(dir_entry_buf, header.directory_entry_size));
this->file_bucket_storage.reset(new fs::MemoryStorage(file_bucket_buf, header.file_bucket_size));
this->file_entry_storage.reset(new fs::MemoryStorage(file_entry_buf, header.file_entry_size));
} else {
this->dir_bucket_storage.reset(new fs::SubStorage(base, header.directory_bucket_offset, header.directory_bucket_size));
this->dir_entry_storage.reset(new fs::SubStorage(base, header.directory_entry_offset, header.directory_entry_size));
this->file_bucket_storage.reset(new fs::SubStorage(base, header.file_bucket_offset, header.file_bucket_size));
this->file_entry_storage.reset(new fs::SubStorage(base, header.file_entry_offset, header.file_entry_size));
}
/* Ensure we allocated storages successfully. */
R_UNLESS(this->dir_bucket_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
R_UNLESS(this->dir_entry_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
R_UNLESS(this->file_bucket_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
R_UNLESS(this->file_entry_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
/* Initialize the rom table. */
R_TRY(this->rom_file_table.Initialize(this->dir_bucket_storage.get(), 0, static_cast<u32>(header.directory_bucket_size),
this->dir_entry_storage.get(), 0, static_cast<u32>(header.directory_entry_size),
this->file_bucket_storage.get(), 0, static_cast<u32>(header.file_bucket_size),
this->file_entry_storage.get(), 0, static_cast<u32>(header.file_entry_size)));
/* Set members. */
this->entry_size = header.body_offset;
this->base_storage = base;
return ResultSuccess();
}
Result RomFsFileSystem::Initialize(std::shared_ptr<fs::IStorage> base, void *work, size_t work_size, bool use_cache) {
this->shared_storage = std::move(base);
return this->Initialize(this->shared_storage.get(), work, work_size, use_cache);
}
Result RomFsFileSystem::GetFileInfo(RomFileTable::FileInfo *out, const char *path) {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result {
R_TRY_CATCH(this->rom_file_table.OpenFile(out, path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound());
} R_END_TRY_CATCH;
return ResultSuccess();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
}
Result RomFsFileSystem::GetFileBaseOffset(s64 *out, const char *path) {
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::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DeleteFileImpl(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::CreateDirectoryImpl(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DeleteDirectoryImpl(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DeleteDirectoryRecursivelyImpl(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::RenameFileImpl(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::RenameDirectoryImpl(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result {
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();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
}
Result RomFsFileSystem::OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
R_UNLESS(mode == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
auto file = std::make_unique<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::ResultAllocationFailureInRomFsFileSystemC());
*out_file = std::move(file);
return ResultSuccess();
}
Result RomFsFileSystem::OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
RomFileTable::FindPosition find;
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY_CATCH(this->rom_file_table.FindOpen(std::addressof(find), path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
} R_END_TRY_CATCH;
return ResultSuccess();
}, AMS_CURRENT_FUNCTION_NAME));
auto dir = std::make_unique<RomFsDirectory>(this, find, mode);
R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInRomFsFileSystemD());
*out_dir = std::move(dir);
return ResultSuccess();
}
Result RomFsFileSystem::CommitImpl() {
return ResultSuccess();
}
Result RomFsFileSystem::GetFreeSpaceSizeImpl(s64 *out, const char *path) {
*out = 0;
return ResultSuccess();
}
Result RomFsFileSystem::CleanDirectoryRecursivelyImpl(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::CommitProvisionallyImpl(s64 counter) {
return fs::ResultUnsupportedOperationInRomFsFileSystemB();
}
}

View file

@ -61,3 +61,5 @@
#define AMS_LIKELY(expr) AMS_PREDICT_TRUE(expr, 1.0)
#define AMS_UNLIKELY(expr) AMS_PREDICT_FALSE(expr, 1.0)
#define AMS_CURRENT_FUNCTION_NAME __FUNCTION__

View file

@ -240,6 +240,8 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(ExtensionSizeTooLarge, 6066);
R_DEFINE_ERROR_RESULT(ExtensionSizeInvalid, 6067);
R_DEFINE_ERROR_RESULT(InvalidOpenMode, 6072);
R_DEFINE_ERROR_RANGE(InvalidEnumValue, 6080, 6099);
R_DEFINE_ERROR_RESULT(InvalidSaveDataState, 6081);
R_DEFINE_ERROR_RESULT(InvalidSaveDataSpaceId, 6082);
@ -286,6 +288,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RANGE(NotFound, 6600, 6699);
R_DEFINE_ERROR_RANGE(OutOfResource, 6700, 6799);
R_DEFINE_ERROR_RESULT(BufferAllocationFailed, 6705);
R_DEFINE_ERROR_RESULT(MappingTableFull, 6706);
R_DEFINE_ERROR_RESULT(OpenCountLimit, 6709);