mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-07-13 20:52:15 +02:00
ncm: fix fs use, implement more of < 4.0.0 for-initialize/safemode
This commit is contained in:
parent
2194f04fa7
commit
7c973acb0c
@ -19,6 +19,7 @@
|
|||||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||||
|
#include <stratosphere/fs/impl/fs_filesystem_proxy_type.hpp>
|
||||||
#include <stratosphere/fs/fsa/fs_registrar.hpp>
|
#include <stratosphere/fs/fsa/fs_registrar.hpp>
|
||||||
#include <stratosphere/fs/fs_remote_filesystem.hpp>
|
#include <stratosphere/fs/fs_remote_filesystem.hpp>
|
||||||
#include <stratosphere/fs/fs_readonly_filesystem_adapter.hpp>
|
#include <stratosphere/fs/fs_readonly_filesystem_adapter.hpp>
|
||||||
@ -36,6 +37,7 @@
|
|||||||
#include <stratosphere/fs/impl/fs_data.hpp>
|
#include <stratosphere/fs/impl/fs_data.hpp>
|
||||||
#include <stratosphere/fs/fs_system_data.hpp>
|
#include <stratosphere/fs/fs_system_data.hpp>
|
||||||
#include <stratosphere/fs/fs_bis.hpp>
|
#include <stratosphere/fs/fs_bis.hpp>
|
||||||
|
#include <stratosphere/fs/fs_content.hpp>
|
||||||
#include <stratosphere/fs/fs_content_storage.hpp>
|
#include <stratosphere/fs/fs_content_storage.hpp>
|
||||||
#include <stratosphere/fs/fs_game_card.hpp>
|
#include <stratosphere/fs/fs_game_card.hpp>
|
||||||
#include <stratosphere/fs/fs_sd_card.hpp>
|
#include <stratosphere/fs/fs_sd_card.hpp>
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
enum ContentType {
|
||||||
|
ContentType_Meta = 0,
|
||||||
|
ContentType_Control = 1,
|
||||||
|
ContentType_Manual = 2,
|
||||||
|
ContentType_Logo = 3,
|
||||||
|
ContentType_Data = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
Result MountContent(const char *name, const char *path, ContentType content_type);
|
||||||
|
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type);
|
||||||
|
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type);
|
||||||
|
|
||||||
|
}
|
@ -14,163 +14,217 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "fs_common.hpp"
|
#include <stratosphere/fs/fs_common.hpp>
|
||||||
#include "impl/fs_newable.hpp"
|
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||||
#include "fsa/fs_ifile.hpp"
|
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||||
#include "fsa/fs_idirectory.hpp"
|
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||||
#include "fsa/fs_ifilesystem.hpp"
|
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||||
|
#include <stratosphere/fs/fs_query_range.hpp>
|
||||||
|
#include <stratosphere/fs/fs_path_tool.hpp>
|
||||||
|
#include <stratosphere/fs/fs_path_utils.hpp>
|
||||||
|
|
||||||
namespace ams::fs {
|
namespace ams::fs {
|
||||||
|
|
||||||
class RemoteFile : public fsa::IFile, public impl::Newable {
|
class RemoteFile : public fsa::IFile, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsFile, impl::Deleter> base_file;
|
::FsFile base_file;
|
||||||
public:
|
public:
|
||||||
RemoteFile(::FsFile &f) {
|
RemoteFile(const ::FsFile &f) : base_file(f) { /* ... */ }
|
||||||
this->base_file = impl::MakeUnique<::FsFile>();
|
|
||||||
*this->base_file = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RemoteFile() { fsFileClose(this->base_file.get()); }
|
virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); }
|
||||||
public:
|
public:
|
||||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||||
return fsFileRead(this->base_file.get(), offset, buffer, size, option.value, out);
|
return fsFileRead(std::addressof(this->base_file), offset, buffer, size, option.value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetSizeImpl(s64 *out) override final {
|
virtual Result GetSizeImpl(s64 *out) override final {
|
||||||
return fsFileGetSize(this->base_file.get(), out);
|
return fsFileGetSize(std::addressof(this->base_file), out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result FlushImpl() override final {
|
virtual Result FlushImpl() override final {
|
||||||
return fsFileFlush(this->base_file.get());
|
return fsFileFlush(std::addressof(this->base_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||||
return fsFileWrite(this->base_file.get(), offset, buffer, size, option.value);
|
return fsFileWrite(std::addressof(this->base_file), offset, buffer, size, option.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result SetSizeImpl(s64 size) override final {
|
virtual Result SetSizeImpl(s64 size) override final {
|
||||||
return fsFileSetSize(this->base_file.get(), size);
|
return fsFileSetSize(std::addressof(this->base_file), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 final {
|
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 final {
|
||||||
/* TODO: How should this be handled? */
|
R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA());
|
||||||
return fs::ResultNotImplemented();
|
R_UNLESS(dst_size == sizeof(FileQueryRangeInfo), fs::ResultInvalidSize());
|
||||||
|
|
||||||
|
return fsFileOperateRange(std::addressof(this->base_file), static_cast<::FsOperationId>(op_id), offset, size, reinterpret_cast<::FsRangeInfo *>(dst));
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||||
return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_file->s)};
|
return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_file.s)))};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class RemoteDirectory : public fsa::IDirectory, public impl::Newable {
|
class RemoteDirectory : public fsa::IDirectory, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsDir, impl::Deleter> base_dir;
|
::FsDir base_dir;
|
||||||
public:
|
public:
|
||||||
RemoteDirectory(::FsDir &d) {
|
RemoteDirectory(const ::FsDir &d) : base_dir(d) { /* ... */ }
|
||||||
this->base_dir = impl::MakeUnique<::FsDir>();
|
|
||||||
*this->base_dir = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RemoteDirectory() { fsDirClose(this->base_dir.get()); }
|
virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); }
|
||||||
public:
|
public:
|
||||||
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
|
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
|
||||||
return fsDirRead(this->base_dir.get(), out_count, max_entries, out_entries);
|
return fsDirRead(std::addressof(this->base_dir), out_count, max_entries, out_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetEntryCountImpl(s64 *out) override final {
|
virtual Result GetEntryCountImpl(s64 *out) override final {
|
||||||
return fsDirGetEntryCount(this->base_dir.get(), out);
|
return fsDirGetEntryCount(std::addressof(this->base_dir), out);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||||
return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_dir->s)};
|
return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_dir.s)))};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class RemoteFileSystem : public fsa::IFileSystem, public impl::Newable {
|
class RemoteFileSystem : public fsa::IFileSystem, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsFileSystem, impl::Deleter> base_fs;
|
::FsFileSystem base_fs;
|
||||||
public:
|
public:
|
||||||
RemoteFileSystem(::FsFileSystem &fs) {
|
RemoteFileSystem(const ::FsFileSystem &fs) : base_fs(fs) { /* ... */ }
|
||||||
this->base_fs = impl::MakeUnique<::FsFileSystem>();
|
|
||||||
*this->base_fs = fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RemoteFileSystem() { fsFsClose(this->base_fs.get()); }
|
virtual ~RemoteFileSystem() { fsFsClose(std::addressof(this->base_fs)); }
|
||||||
|
private:
|
||||||
|
Result GetPathForServiceObject(fssrv::sf::Path *out_path, const char *path) {
|
||||||
|
/* Copy and null terminate. */
|
||||||
|
std::strncpy(out_path->str, path, sizeof(out_path->str) - 1);
|
||||||
|
out_path->str[sizeof(out_path->str) - 1] = '\x00';
|
||||||
|
|
||||||
|
/* Replace directory separators. */
|
||||||
|
Replace(out_path->str, sizeof(out_path->str) - 1, StringTraits::AlternateDirectorySeparator, StringTraits::DirectorySeparator);
|
||||||
|
|
||||||
|
/* Get lengths. */
|
||||||
|
const auto mount_name_len = PathTool::IsWindowsAbsolutePath(path) ? 2 : 0;
|
||||||
|
const auto rel_path = out_path->str + mount_name_len;
|
||||||
|
const auto max_len = fs::EntryNameLengthMax - mount_name_len;
|
||||||
|
return VerifyPath(rel_path, max_len, max_len);
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
||||||
return fsFsCreateFile(this->base_fs.get(), path, size, flags);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsCreateFile(std::addressof(this->base_fs), sf_path.str, size, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result DeleteFileImpl(const char *path) override final {
|
virtual Result DeleteFileImpl(const char *path) override final {
|
||||||
return fsFsDeleteFile(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsDeleteFile(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result CreateDirectoryImpl(const char *path) override final {
|
virtual Result CreateDirectoryImpl(const char *path) override final {
|
||||||
return fsFsCreateDirectory(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsCreateDirectory(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
||||||
return fsFsDeleteDirectory(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsDeleteDirectory(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
||||||
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsDeleteDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
||||||
return fsFsRenameFile(this->base_fs.get(), old_path, new_path);
|
fssrv::sf::Path old_sf_path;
|
||||||
|
fssrv::sf::Path new_sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path));
|
||||||
|
return fsFsRenameFile(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
||||||
return fsFsRenameDirectory(this->base_fs.get(), old_path, new_path);
|
fssrv::sf::Path old_sf_path;
|
||||||
|
fssrv::sf::Path new_sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path));
|
||||||
|
return fsFsRenameDirectory(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
||||||
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
|
||||||
static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType));
|
static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType));
|
||||||
return fsFsGetEntryType(this->base_fs.get(), path, reinterpret_cast<::FsDirEntryType *>(out));
|
return fsFsGetEntryType(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsDirEntryType *>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||||
FsFile f;
|
fssrv::sf::Path sf_path;
|
||||||
R_TRY(fsFsOpenFile(this->base_fs.get(), path, mode, &f));
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
|
||||||
*out_file = std::make_unique<RemoteFile>(f);
|
FsFile f;
|
||||||
|
R_TRY(fsFsOpenFile(std::addressof(this->base_fs), sf_path.str, mode, &f));
|
||||||
|
|
||||||
|
std::unique_ptr<RemoteFile> file(new RemoteFile(f));
|
||||||
|
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInNew());
|
||||||
|
|
||||||
|
*out_file = std::move(file);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||||
FsDir d;
|
fssrv::sf::Path sf_path;
|
||||||
R_TRY(fsFsOpenDirectory(this->base_fs.get(), path, mode, &d));
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
|
||||||
*out_dir = std::make_unique<RemoteDirectory>(d);
|
FsDir d;
|
||||||
|
R_TRY(fsFsOpenDirectory(std::addressof(this->base_fs), sf_path.str, mode, &d));
|
||||||
|
|
||||||
|
std::unique_ptr<RemoteDirectory> dir(new RemoteDirectory(d));
|
||||||
|
R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInNew());
|
||||||
|
|
||||||
|
*out_dir = std::move(dir);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result CommitImpl() override final {
|
virtual Result CommitImpl() override final {
|
||||||
return fsFsCommit(this->base_fs.get());
|
return fsFsCommit(std::addressof(this->base_fs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
||||||
return fsFsGetFreeSpace(this->base_fs.get(), path, out);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsGetFreeSpace(std::addressof(this->base_fs), sf_path.str, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
||||||
return fsFsGetTotalSpace(this->base_fs.get(), path, out);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsGetTotalSpace(std::addressof(this->base_fs), sf_path.str, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
||||||
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsCleanDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
||||||
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw));
|
static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw));
|
||||||
return fsFsGetFileTimeStampRaw(this->base_fs.get(), path, reinterpret_cast<::FsTimeStampRaw *>(out));
|
return fsFsGetFileTimeStampRaw(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsTimeStampRaw *>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
||||||
return fsFsQueryEntry(this->base_fs.get(), dst, dst_size, src, src_size, path, static_cast<FsFileSystemQueryId>(query));
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsQueryEntry(std::addressof(this->base_fs), dst, dst_size, src, src_size, sf_path.str, static_cast<FsFileSystemQueryId>(query));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs::impl {
|
||||||
|
|
||||||
|
enum FileSystemProxyType {
|
||||||
|
FileSystemProxyType_Code = 0,
|
||||||
|
FileSystemProxyType_Rom = 1,
|
||||||
|
FileSystemProxyType_Logo = 2,
|
||||||
|
FileSystemProxyType_Control = 3,
|
||||||
|
FileSystemProxyType_Manual = 4,
|
||||||
|
FileSystemProxyType_Meta = 5,
|
||||||
|
FileSystemProxyType_Data = 6,
|
||||||
|
FileSystemProxyType_Package = 7,
|
||||||
|
FileSystemProxyType_UpdatePartition = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -14,9 +14,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../fs/fs_common.hpp"
|
#include <stratosphere/fs/fs_common.hpp>
|
||||||
#include "../fs/fs_directory.hpp"
|
#include <stratosphere/fs/fs_directory.hpp>
|
||||||
#include "../sf/sf_buffer_tags.hpp"
|
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||||
|
|
||||||
namespace ams::fssrv::sf {
|
namespace ams::fssrv::sf {
|
||||||
|
|
||||||
|
@ -25,4 +25,5 @@
|
|||||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_manager_impl.hpp>
|
#include <stratosphere/ncm/ncm_content_manager_impl.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||||
#include <stratosphere/ncm/ncm_api.hpp>
|
#include <stratosphere/ncm/ncm_api.hpp>
|
||||||
|
@ -42,7 +42,7 @@ namespace ams::ncm {
|
|||||||
return this->id_offset;
|
return this->id_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr ContentInfo Make(ContentId id, u64 size, ContentType type, u8 id_ofs) {
|
static constexpr ContentInfo Make(ContentId id, u64 size, ContentType type, u8 id_ofs = 0) {
|
||||||
const u32 size_low = size & 0xFFFFFFFFu;
|
const u32 size_low = size & 0xFFFFFFFFu;
|
||||||
const u16 size_high = static_cast<u16>(size >> 32);
|
const u16 size_high = static_cast<u16>(size >> 32);
|
||||||
return {
|
return {
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 Adubbz, 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/ncm/ncm_content_meta.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
class ContentMetaDatabaseBuilder {
|
||||||
|
private:
|
||||||
|
ContentMetaDatabase *db;
|
||||||
|
private:
|
||||||
|
Result BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info);
|
||||||
|
public:
|
||||||
|
explicit ContentMetaDatabaseBuilder(ContentMetaDatabase *d) : db(d) { /* ... */ }
|
||||||
|
|
||||||
|
Result BuildFromStorage(ContentStorage *storage);
|
||||||
|
|
||||||
|
Result Cleanup();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -19,11 +19,19 @@
|
|||||||
namespace ams::ncm {
|
namespace ams::ncm {
|
||||||
|
|
||||||
struct ContentManagerConfig {
|
struct ContentManagerConfig {
|
||||||
bool import_database_from_system;
|
bool build_system_database;
|
||||||
bool import_database_from_system_on_sd;
|
bool import_database_from_system_on_sd;
|
||||||
|
|
||||||
constexpr bool HasAnyImport() const {
|
bool HasAnyConfig() const {
|
||||||
return this->import_database_from_system || this->import_database_from_system_on_sd;
|
return this->ShouldBuildDatabase() || this->import_database_from_system_on_sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldBuildDatabase() const {
|
||||||
|
return hos::GetVersion() < hos::Version_400 || this->build_system_database;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldImportDatabaseFromSignedSystemPartitionOnSd() const {
|
||||||
|
return this->import_database_from_system_on_sd;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||||
#include <stratosphere/ncm/ncm_bounded_map.hpp>
|
#include <stratosphere/ncm/ncm_bounded_map.hpp>
|
||||||
#include <stratosphere/ncm/ncm_rights_id_cache.hpp>
|
#include <stratosphere/ncm/ncm_rights_id_cache.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_management_utils.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||||
#include <stratosphere/kvdb/kvdb_memory_key_value_store.hpp>
|
#include <stratosphere/kvdb/kvdb_memory_key_value_store.hpp>
|
||||||
|
|
||||||
namespace ams::ncm {
|
namespace ams::ncm {
|
||||||
@ -93,7 +95,9 @@ namespace ams::ncm {
|
|||||||
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas);
|
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas);
|
||||||
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas);
|
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas);
|
||||||
|
|
||||||
Result ImportContentMetaDatabase(StorageId storage_id, const char *import_mount_name, const char *path);
|
Result BuildContentMetaDatabase(StorageId storage_id);
|
||||||
|
Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition);
|
||||||
|
Result ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path);
|
||||||
|
|
||||||
Result EnsureAndMountSystemSaveData(const char *mount, const SystemSaveDataInfo &info) const;
|
Result EnsureAndMountSystemSaveData(const char *mount, const SystemSaveDataInfo &info) const;
|
||||||
public:
|
public:
|
||||||
|
@ -110,7 +110,7 @@ namespace ams::ncm {
|
|||||||
return lc;
|
return lc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListCount ListContentMeta(ContentMetaKey *dst, size_t dst_size, ContentMetaType type, ApplicationId app_id = InvalidApplicationId, u64 min = std::numeric_limits<u64>::min(), u64 max = std::numeric_limits<u64>::max(), ContentInstallType install_type = ContentInstallType::Full) {
|
ListCount ListContentMeta(ContentMetaKey *dst, size_t dst_size, ContentMetaType type = ContentMetaType::Unknown, ApplicationId app_id = InvalidApplicationId, u64 min = std::numeric_limits<u64>::min(), u64 max = std::numeric_limits<u64>::max(), ContentInstallType install_type = ContentInstallType::Full) {
|
||||||
ListCount lc = {};
|
ListCount lc = {};
|
||||||
R_ABORT_UNLESS(this->interface->List(std::addressof(lc.total), std::addressof(lc.written), sf::OutArray<ContentMetaKey>(dst, dst_size), type, app_id, min, max, install_type));
|
R_ABORT_UNLESS(this->interface->List(std::addressof(lc.total), std::addressof(lc.written), sf::OutArray<ContentMetaKey>(dst, dst_size), type, app_id, min, max, install_type));
|
||||||
return lc;
|
return lc;
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 Adubbz, 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/ncm/ncm_auto_buffer.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_storage_id.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_meta_key.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
Result ReadContentMetaPath(AutoBuffer *out, const char *path);
|
||||||
|
|
||||||
|
}
|
74
libraries/libstratosphere/source/fs/fs_content.cpp
Normal file
74
libraries/libstratosphere/source/fs/fs_content.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "fsa/fs_mount_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
impl::FileSystemProxyType ConvertToFileSystemProxyType(ContentType content_type) {
|
||||||
|
switch (content_type) {
|
||||||
|
case ContentType_Control: return impl::FileSystemProxyType_Control;
|
||||||
|
case ContentType_Manual: return impl::FileSystemProxyType_Manual;
|
||||||
|
case ContentType_Meta: return impl::FileSystemProxyType_Meta;
|
||||||
|
case ContentType_Data: return impl::FileSystemProxyType_Data;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MountContentImpl(const char *name, const char *path, u64 id, impl::FileSystemProxyType type) {
|
||||||
|
/* Open a filesystem using libnx bindings. */
|
||||||
|
FsFileSystem fs;
|
||||||
|
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), id, static_cast<::FsFileSystemType>(type), path));
|
||||||
|
|
||||||
|
/* Allocate a new filesystem wrapper. */
|
||||||
|
std::unique_ptr<fsa::IFileSystem> fsa(new RemoteFileSystem(fs));
|
||||||
|
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA());
|
||||||
|
|
||||||
|
/* Register. */
|
||||||
|
return fsa::Register(name, std::move(fsa));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MountContent(const char *name, const char *path, u64 id, ContentType content_type) {
|
||||||
|
/* Validate the mount name. */
|
||||||
|
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||||
|
|
||||||
|
/* Validate the path. */
|
||||||
|
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||||
|
|
||||||
|
/* Mount the content. */
|
||||||
|
return MountContentImpl(name, path, id, ConvertToFileSystemProxyType(content_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MountContent(const char *name, const char *path, ContentType content_type) {
|
||||||
|
/* This API only supports mounting Manual content. */
|
||||||
|
AMS_ABORT_UNLESS(content_type == ContentType_Manual);
|
||||||
|
|
||||||
|
return MountContent(name, path, ncm::InvalidProgramId, content_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type) {
|
||||||
|
return MountContent(name, path, id.value, content_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type) {
|
||||||
|
return MountContent(name, path, id.value, content_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -176,7 +176,7 @@ namespace ams::fs {
|
|||||||
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||||
|
|
||||||
std::unique_ptr<impl::FileAccessor> file_accessor(new impl::FileAccessor(std::move(file), nullptr, static_cast<OpenMode>(mode)));
|
std::unique_ptr<impl::FileAccessor> file_accessor(new impl::FileAccessor(std::move(file), nullptr, static_cast<OpenMode>(mode)));
|
||||||
R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInUserFileSystem());
|
R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInNew());
|
||||||
out->handle = file_accessor.release();
|
out->handle = file_accessor.release();
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 Adubbz, 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::ncm {
|
||||||
|
|
||||||
|
|
||||||
|
Result ContentMetaDatabaseBuilder::BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info) {
|
||||||
|
AMS_ABORT();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ContentMetaDatabaseBuilder::BuildFromStorage(ContentStorage *storage) {
|
||||||
|
/* Get the total count of contents. */
|
||||||
|
s32 total_count;
|
||||||
|
R_TRY(storage->GetContentCount(std::addressof(total_count)));
|
||||||
|
|
||||||
|
/* Loop over all contents, looking for a package we can build from. */
|
||||||
|
const size_t MaxContentIds = 64;
|
||||||
|
ContentId content_ids[MaxContentIds];
|
||||||
|
for (s32 offset = 0; offset < total_count; /* ... */) {
|
||||||
|
/* List contents at the current offset. */
|
||||||
|
s32 count;
|
||||||
|
R_TRY(storage->ListContentId(std::addressof(count), content_ids, MaxContentIds, offset));
|
||||||
|
|
||||||
|
/* Loop the contents we listed, looking for a correct one. */
|
||||||
|
for (s32 i = 0; i < count; i++) {
|
||||||
|
/* Get the path for this content id. */
|
||||||
|
auto &content_id = content_ids[i];
|
||||||
|
ncm::Path path;
|
||||||
|
storage->GetPath(std::addressof(path), content_id);
|
||||||
|
|
||||||
|
/* Read the content meta path, and build. */
|
||||||
|
ncm::AutoBuffer package_meta;
|
||||||
|
if (R_SUCCEEDED(ncm::ReadContentMetaPath(std::addressof(package_meta), path.str))) {
|
||||||
|
/* Get the size of the content. */
|
||||||
|
s64 size;
|
||||||
|
R_TRY(storage->GetSize(std::addressof(size), content_id));
|
||||||
|
|
||||||
|
/* Build. */
|
||||||
|
R_TRY(this->BuildFromPackageContentMeta(package_meta.Get(), package_meta.GetSize(), ContentInfo::Make(content_id, size, ContentType::Meta)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
offset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Commit our changes. */
|
||||||
|
return this->db->Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ContentMetaDatabaseBuilder::Cleanup() {
|
||||||
|
/* This cleans up the content meta by removing all entries. */
|
||||||
|
while (true) {
|
||||||
|
/* List as many keys as we can. */
|
||||||
|
constexpr s32 MaxKeys = 64;
|
||||||
|
ContentMetaKey keys[MaxKeys];
|
||||||
|
auto list_count = this->db->ListContentMeta(keys, MaxKeys);
|
||||||
|
|
||||||
|
/* Remove the listed keys. */
|
||||||
|
for (auto i = 0; i < list_count.written; i++) {
|
||||||
|
R_TRY(this->db->Remove(keys[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there aren't more keys to read, we're done. */
|
||||||
|
if (list_count.written < MaxKeys) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Commit our deletions. */
|
||||||
|
return this->db->Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -108,25 +108,22 @@ namespace ams::ncm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE bool ShouldPerformImport(const ContentManagerConfig &config, const char *bis_mount_name) {
|
ALWAYS_INLINE bool IsSignedSystemPartitionOnSdCardValid(const char *bis_mount_name) {
|
||||||
AMS_ASSERT(config.HasAnyImport());
|
/* Signed system partition should never be checked on < 4.0.0, as it did not exist before then. */
|
||||||
if (config.import_database_from_system) {
|
AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_400);
|
||||||
/* If we're importing from system, just do the import. */
|
|
||||||
return true;
|
/* If we're importing from system on SD, make sure that the signed system partition is valid. */
|
||||||
} else /* if (config.import_database_from_system_on_sd) */ {
|
const auto version = hos::GetVersion();
|
||||||
/* If we're importing from system on SD, make sure that the signed system partition is valid. */
|
if (version >= hos::Version_800) {
|
||||||
const auto version = hos::GetVersion();
|
/* On >= 8.0.0, a simpler method was added to check validity. */
|
||||||
if (version >= hos::Version_800 || version < hos::Version_400) {
|
/* This also works on < 4.0.0 (though the system partition will never be on-sd there), */
|
||||||
/* On >= 8.0.0, a simpler method was added to check validity. */
|
/* and so this will always return false. */
|
||||||
/* This also works on < 4.0.0 (though the system partition will never be on-sd there), */
|
char path[fs::MountNameLengthMax + 2 /* :/ */ + 1];
|
||||||
/* and so this will always return false. */
|
std::snprintf(path, sizeof(path), "%s:/", bis_mount_name);
|
||||||
char path[fs::MountNameLengthMax + 2 /* :/ */ + 1];
|
return fs::IsSignedSystemPartitionOnSdCardValid(path);
|
||||||
std::snprintf(path, sizeof(path), "%s:/", bis_mount_name);
|
} else {
|
||||||
return fs::IsSignedSystemPartitionOnSdCardValid(path);
|
/* On 4.0.0-7.0.1, use the remote command to validate the system partition. */
|
||||||
} else {
|
return fs::IsSignedSystemPartitionOnSdCardValidDeprecated();
|
||||||
/* On 4.0.0-7.0.1, use the remote command to validate the system partition. */
|
|
||||||
return fs::IsSignedSystemPartitionOnSdCardValidDeprecated();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +237,7 @@ namespace ams::ncm {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, const char *import_mount_name, const char *path) {
|
Result ContentManagerImpl::ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path) {
|
||||||
std::scoped_lock lk(this->mutex);
|
std::scoped_lock lk(this->mutex);
|
||||||
|
|
||||||
/* Obtain the content meta database root. */
|
/* Obtain the content meta database root. */
|
||||||
@ -269,6 +266,46 @@ namespace ams::ncm {
|
|||||||
return fs::CommitSaveData(root->mount_name);
|
return fs::CommitSaveData(root->mount_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ContentManagerImpl::BuildContentMetaDatabase(StorageId storage_id) {
|
||||||
|
if (hos::GetVersion() <= hos::Version_400) {
|
||||||
|
/* Temporarily activate the database. */
|
||||||
|
R_TRY(this->ActivateContentMetaDatabase(storage_id));
|
||||||
|
ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(storage_id); };
|
||||||
|
|
||||||
|
/* Open the content meta database and storage. */
|
||||||
|
ContentMetaDatabase meta_db;
|
||||||
|
ContentStorage storage;
|
||||||
|
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(meta_db), storage_id));
|
||||||
|
R_TRY(ncm::OpenContentStorage(std::addressof(storage), storage_id));
|
||||||
|
|
||||||
|
/* Create a builder, and build. */
|
||||||
|
ContentMetaDatabaseBuilder builder(std::addressof(meta_db));
|
||||||
|
return builder.BuildFromStorage(std::addressof(storage));
|
||||||
|
} else {
|
||||||
|
/* On 5.0.0+, building just performs an import. */
|
||||||
|
return this->ImportContentMetaDatabase(storage_id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition) {
|
||||||
|
/* Only support importing BuiltInSystem. */
|
||||||
|
AMS_ABORT_UNLESS(storage_id == StorageId::BuiltInSystem);
|
||||||
|
|
||||||
|
/* Get a mount name for the system partition. */
|
||||||
|
auto bis_mount_name = impl::CreateUniqueMountName();
|
||||||
|
|
||||||
|
/* Mount the BIS partition that contains the database we're importing. */
|
||||||
|
R_TRY(fs::MountBis(bis_mount_name.str, fs::BisPartitionId::System));
|
||||||
|
ON_SCOPE_EXIT { fs::Unmount(bis_mount_name.str); };
|
||||||
|
|
||||||
|
/* If we're not importing from a signed partition (or the partition signature is valid), import. */
|
||||||
|
if (!from_signed_partition || IsSignedSystemPartitionOnSdCardValid(bis_mount_name.str)) {
|
||||||
|
R_TRY(this->ImportContentMetaDatabaseImpl(StorageId::BuiltInSystem, bis_mount_name.str, "cnmtdb.arc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
Result ContentManagerImpl::Initialize(const ContentManagerConfig &config) {
|
Result ContentManagerImpl::Initialize(const ContentManagerConfig &config) {
|
||||||
std::scoped_lock lk(this->mutex);
|
std::scoped_lock lk(this->mutex);
|
||||||
|
|
||||||
@ -297,22 +334,15 @@ namespace ams::ncm {
|
|||||||
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
|
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
|
||||||
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
|
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
|
||||||
|
|
||||||
/* NOTE: Nintendo added support for building/importing in 4.0.0. */
|
/* Try to build or import a database, depending on our configuration. */
|
||||||
/* However, there's no reason to restrict this on a per-version basis. */
|
if (config.ShouldBuildDatabase()) {
|
||||||
|
/* If we should build the database, do so. */
|
||||||
/* If we should import the database from system, do so and verify it. */
|
R_TRY(this->BuildContentMetaDatabase(StorageId::BuiltInSystem));
|
||||||
if (config.HasAnyImport()) {
|
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));
|
||||||
/* Get a mount name for the system partition. */
|
} else if (config.ShouldImportDatabaseFromSignedSystemPartitionOnSd()) {
|
||||||
auto bis_mount_name = impl::CreateUniqueMountName();
|
/* Otherwise if we should import the database from the SD, do so. */
|
||||||
|
R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, true));
|
||||||
/* Mount the BIS partition that contains the database we're importing. */
|
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));
|
||||||
R_TRY(fs::MountBis(bis_mount_name.str, fs::BisPartitionId::System));
|
|
||||||
ON_SCOPE_EXIT { fs::Unmount(bis_mount_name.str); };
|
|
||||||
|
|
||||||
if (ShouldPerformImport(config, bis_mount_name.str)) {
|
|
||||||
R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, bis_mount_name.str, "cnmtdb.arc"));
|
|
||||||
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "ncm_fs_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using FilePathString = kvdb::BoundedString<64>;
|
||||||
|
|
||||||
|
bool IsContentMetaFileName(const char *name) {
|
||||||
|
return impl::PathView(name).HasSuffix(".cnmt");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadContentMetaPath(AutoBuffer *out, const char *path) {
|
||||||
|
/* Mount the content. */
|
||||||
|
auto mount_name = impl::CreateUniqueMountName();
|
||||||
|
R_TRY(fs::MountContent(mount_name.str, path, fs::ContentType_Meta));
|
||||||
|
ON_SCOPE_EXIT { fs::Unmount(mount_name.str); };
|
||||||
|
|
||||||
|
/* Open the root directory. */
|
||||||
|
auto root_path = impl::GetRootDirectoryPath(mount_name);
|
||||||
|
fs::DirectoryHandle dir;
|
||||||
|
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path.str, fs::OpenDirectoryMode_File));
|
||||||
|
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||||
|
|
||||||
|
/* Loop directory reading until we find the entry we're looking for. */
|
||||||
|
while (true) {
|
||||||
|
/* Read one entry, and finish when we fail to read. */
|
||||||
|
fs::DirectoryEntry entry;
|
||||||
|
s64 num_read;
|
||||||
|
R_TRY(fs::ReadDirectory(std::addressof(num_read), std::addressof(entry), dir, 1));
|
||||||
|
if (num_read == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this is the content meta file, parse it. */
|
||||||
|
if (IsContentMetaFileName(entry.name)) {
|
||||||
|
/* Create the file path. */
|
||||||
|
FilePathString file_path(root_path.str);
|
||||||
|
file_path.Append(entry.name);
|
||||||
|
|
||||||
|
/* Open the content meta file. */
|
||||||
|
fs::FileHandle file;
|
||||||
|
R_TRY(fs::OpenFile(std::addressof(file), file_path, fs::OpenMode_Read));
|
||||||
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||||
|
|
||||||
|
/* Get the meta size. */
|
||||||
|
s64 file_size;
|
||||||
|
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||||
|
const size_t meta_size = static_cast<size_t>(file_size);
|
||||||
|
|
||||||
|
/* Create a buffer for the meta. */
|
||||||
|
R_TRY(out->Initialize(meta_size));
|
||||||
|
|
||||||
|
/* Read the meta into the buffer. */
|
||||||
|
return fs::ReadFile(file, 0, out->Get(), meta_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ncm::ResultContentMetaNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -155,21 +155,4 @@ namespace ams::ncm::impl {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetSaveDataFlags(u32 *out_flags, u64 save_id) {
|
|
||||||
FsSaveDataExtraData extra_data;
|
|
||||||
|
|
||||||
R_TRY(fsReadSaveDataFileSystemExtraData(&extra_data, sizeof(FsSaveDataExtraData), save_id));
|
|
||||||
*out_flags = extra_data.flags;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result SetSaveDataFlags(u64 save_id, FsSaveDataSpaceId space_id, u32 flags) {
|
|
||||||
FsSaveDataExtraData extra_data;
|
|
||||||
|
|
||||||
R_TRY(fsReadSaveDataFileSystemExtraData(&extra_data, sizeof(FsSaveDataExtraData), save_id));
|
|
||||||
extra_data.flags = flags;
|
|
||||||
R_TRY(fsWriteSaveDataFileSystemExtraData(&extra_data, sizeof(FsSaveDataExtraData), space_id, save_id));
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,8 @@ namespace ams::fs {
|
|||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217);
|
||||||
|
R_DEFINE_ERROR_RESULT(AllocationFailureInCodeA, 3218);
|
||||||
|
R_DEFINE_ERROR_RESULT(AllocationFailureInContentA, 3219);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222);
|
||||||
@ -79,7 +81,7 @@ namespace ams::fs {
|
|||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407);
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailureInUserFileSystem, 3420);
|
R_DEFINE_ERROR_RESULT(AllocationFailureInNew, 3420);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999);
|
R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999);
|
||||||
|
|
||||||
@ -235,16 +237,17 @@ namespace ams::fs {
|
|||||||
R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201);
|
R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399);
|
R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367);
|
||||||
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
||||||
|
|
||||||
|
@ -189,9 +189,9 @@ namespace {
|
|||||||
|
|
||||||
/* Compile-time configuration. */
|
/* Compile-time configuration. */
|
||||||
#ifdef NCM_BUILD_FOR_INTITIALIZE
|
#ifdef NCM_BUILD_FOR_INTITIALIZE
|
||||||
constexpr inline bool ImportSystemDatabase = true;
|
constexpr inline bool BuildSystemDatabase = true;
|
||||||
#else
|
#else
|
||||||
constexpr inline bool ImportSystemDatabase = false;
|
constexpr inline bool BuildSystemDatabase = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef NCM_BUILD_FOR_SAFEMODE
|
#ifdef NCM_BUILD_FOR_SAFEMODE
|
||||||
@ -200,9 +200,9 @@ namespace {
|
|||||||
constexpr inline bool ImportSystemDatabaseFromSignedSystemPartitionOnSdCard = false;
|
constexpr inline bool ImportSystemDatabaseFromSignedSystemPartitionOnSdCard = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static_assert(!(ImportSystemDatabase && ImportSystemDatabaseFromSignedSystemPartitionOnSdCard), "Invalid NCM build configuration!");
|
static_assert(!(BuildSystemDatabase && ImportSystemDatabaseFromSignedSystemPartitionOnSdCard), "Invalid NCM build configuration!");
|
||||||
|
|
||||||
constexpr inline ncm::ContentManagerConfig ManagerConfig = { ImportSystemDatabase, ImportSystemDatabaseFromSignedSystemPartitionOnSdCard };
|
constexpr inline ncm::ContentManagerConfig ManagerConfig = { BuildSystemDatabase, ImportSystemDatabaseFromSignedSystemPartitionOnSdCard };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user