fs: add substorage, rom path tool

This commit is contained in:
Michael Scire 2020-03-04 20:49:24 -08:00
parent f39781ec11
commit a397853e3b
7 changed files with 509 additions and 2 deletions

View file

@ -22,6 +22,7 @@
#include "fs/fsa/fs_registrar.hpp"
#include "fs/fs_remote_filesystem.hpp"
#include "fs/fs_istorage.hpp"
#include "fs/fs_substorage.hpp"
#include "fs/fs_remote_storage.hpp"
#include "fs/fs_file_storage.hpp"
#include "fs/fs_query_range.hpp"
@ -29,6 +30,7 @@
#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_content_storage.hpp"
#include "fs/fs_game_card.hpp"
#include "fs/fs_sd_card.hpp"

View file

@ -26,7 +26,7 @@ namespace ams::fs {
constexpr inline char Dot = '.';
constexpr inline char NullTerminator = '\x00';
constexpr inline char UnsupportedDirectorySeparator = '/';
constexpr inline char UnsupportedDirectorySeparator = '\\';
}
class PathTool {

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_rom_types.hpp"
namespace ams::fs {
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) {
AMS_ABORT_UNLESS(entry != nullptr);
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) {
AMS_ABORT_UNLESS(p != nullptr);
return length == 1 && IsDot(p[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p) {
AMS_ABORT_UNLESS(p != nullptr);
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) {
AMS_ABORT_UNLESS(p != nullptr);
return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) {
AMS_ABORT_UNLESS(p != nullptr);
return length == 2 && IsDot(p[0]) && IsDot(p[1]);
}
constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) {
AMS_ABORT_UNLESS(lhs != nullptr);
AMS_ABORT_UNLESS(rhs != nullptr);
return std::strncmp(lhs, rhs, length) == 0;
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomPathChar *rhs) {
AMS_ABORT_UNLESS(rhs != nullptr);
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 "fs_common.hpp"
namespace ams::fs {
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 {
s64 offset;
s64 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,124 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "impl/fs_newable.hpp"
#include "fs_istorage.hpp"
namespace ams::fs {
class SubStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
private:
std::shared_ptr<IStorage> shared_base_storage;
fs::IStorage *base_storage;
s64 offset;
s64 size;
bool resizable;
private:
constexpr bool IsValid() const {
return this->base_storage != nullptr;
}
public:
SubStorage() : shared_base_storage(), base_storage(nullptr), offset(0), size(0), resizable(false) { /* ... */ }
SubStorage(const SubStorage &rhs) : shared_base_storage(), base_storage(rhs.base_storage), offset(rhs.offset), size(rhs.size), resizable(rhs.resizable) { /* ... */}
SubStorage &operator=(const SubStorage &rhs) {
if (this != std::addressof(rhs)) {
this->base_storage = rhs.base_storage;
this->offset = rhs.offset;
this->size = rhs.size;
this->resizable = rhs.resizable;
}
return *this;
}
SubStorage(IStorage *storage, s64 o, s64 sz) : shared_base_storage(), base_storage(storage), offset(o), size(sz) {
AMS_ABORT_UNLESS(this->IsValid());
AMS_ABORT_UNLESS(this->offset >= 0);
AMS_ABORT_UNLESS(this->size >= 0);
}
SubStorage(std::shared_ptr<IStorage> storage, s64 o, s64 sz) : shared_base_storage(storage), base_storage(storage.get()), offset(o), size(sz) {
AMS_ABORT_UNLESS(this->IsValid());
AMS_ABORT_UNLESS(this->offset >= 0);
AMS_ABORT_UNLESS(this->size >= 0);
}
SubStorage(SubStorage *sub, s64 o, s64 sz) : shared_base_storage(), base_storage(sub->base_storage), offset(o + sub->offset), size(sz) {
AMS_ABORT_UNLESS(this->IsValid());
AMS_ABORT_UNLESS(this->offset >= 0);
AMS_ABORT_UNLESS(this->size >= 0);
AMS_ABORT_UNLESS(sub->size >= o + sz);
}
public:
void SetResizable(bool rsz) {
this->resizable = rsz;
}
public:
virtual Result Read(s64 offset, void *buffer, size_t size) override {
R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
R_UNLESS(size != 0, ResultSuccess());
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
return this->base_storage->Read(this->offset + offset, buffer, size);
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override{
R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
R_UNLESS(size != 0, ResultSuccess());
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
return this->base_storage->Write(this->offset + offset, buffer, size);
}
virtual Result Flush() override {
R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
return this->base_storage->Flush();
}
virtual Result SetSize(s64 size) override {
R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
R_UNLESS(this->resizable, fs::ResultUnsupportedOperation());
R_UNLESS(IStorage::IsOffsetAndSizeValid(this->offset, size), fs::ResultInvalidSize());
s64 cur_size;
R_TRY(this->base_storage->GetSize(std::addressof(cur_size)));
R_UNLESS(cur_size == this->offset + this->size, fs::ResultUnsupportedOperation());
R_TRY(this->base_storage->SetSize(this->offset + size));
this->size = size;
return ResultSuccess();
}
virtual Result GetSize(s64 *out) override {
R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
*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 {
R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
R_UNLESS(size != 0, ResultSuccess());
R_UNLESS(IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange());
return this->base_storage->OperateRange(dst, dst_size, op_id, this->offset + offset, size, src, src_size);
}
using IStorage::OperateRange;
};
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fs::RomPathTool {
Result PathParser::Initialize(const RomPathChar *path) {
AMS_ABORT_UNLESS(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() {
this->prev_path_start = nullptr;
this->prev_path_end = nullptr;
this->next_path = nullptr;
this->finished = false;
}
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_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);
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_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);
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_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);
/* 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) {
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(p != nullptr);
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;
}
while (IsSeparator(*head)) {
head--;
}
end = head;
len = 0;
depth--;
}
len++;
head--;
}
R_UNLESS(depth == 0, fs::ResultDirectoryUnobtainable());
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

@ -140,6 +140,12 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(MapFull, 6811);
R_DEFINE_ERROR_RANGE(BadState, 6900, 6999);
R_DEFINE_ERROR_RESULT(NotMounted, 6905);
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);
}