diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 94c2498d1..ec4975eb8 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_content.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_content.hpp new file mode 100644 index 000000000..32a2eded9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_content.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 . + */ +#pragma once +#include "fs_common.hpp" +#include + +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); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp index 911097048..7ead1606e 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp @@ -14,163 +14,217 @@ * along with this program. If not, see . */ #pragma once -#include "fs_common.hpp" -#include "impl/fs_newable.hpp" -#include "fsa/fs_ifile.hpp" -#include "fsa/fs_idirectory.hpp" -#include "fsa/fs_ifilesystem.hpp" +#include +#include +#include +#include +#include +#include +#include +#include namespace ams::fs { class RemoteFile : public fsa::IFile, public impl::Newable { private: - std::unique_ptr<::FsFile, impl::Deleter> base_file; + ::FsFile base_file; public: - RemoteFile(::FsFile &f) { - this->base_file = impl::MakeUnique<::FsFile>(); - *this->base_file = f; - } + RemoteFile(const ::FsFile &f) : base_file(f) { /* ... */ } - virtual ~RemoteFile() { fsFileClose(this->base_file.get()); } + virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); } public: 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 { - return fsFileGetSize(this->base_file.get(), out); + return fsFileGetSize(std::addressof(this->base_file), out); } 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 { - 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 { - 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 { - /* TODO: How should this be handled? */ - return fs::ResultNotImplemented(); + R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA()); + 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: 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 { private: - std::unique_ptr<::FsDir, impl::Deleter> base_dir; + ::FsDir base_dir; public: - RemoteDirectory(::FsDir &d) { - this->base_dir = impl::MakeUnique<::FsDir>(); - *this->base_dir = d; - } + RemoteDirectory(const ::FsDir &d) : base_dir(d) { /* ... */ } - virtual ~RemoteDirectory() { fsDirClose(this->base_dir.get()); } + virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); } public: 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 { - return fsDirGetEntryCount(this->base_dir.get(), out); + return fsDirGetEntryCount(std::addressof(this->base_dir), out); } public: 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 { private: - std::unique_ptr<::FsFileSystem, impl::Deleter> base_fs; + ::FsFileSystem base_fs; public: - RemoteFileSystem(::FsFileSystem &fs) { - this->base_fs = impl::MakeUnique<::FsFileSystem>(); - *this->base_fs = fs; - } + RemoteFileSystem(const ::FsFileSystem &fs) : 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: 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + 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 *out_file, const char *path, OpenMode mode) override final { - FsFile f; - R_TRY(fsFsOpenFile(this->base_fs.get(), path, mode, &f)); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); - *out_file = std::make_unique(f); + FsFile f; + R_TRY(fsFsOpenFile(std::addressof(this->base_fs), sf_path.str, mode, &f)); + + std::unique_ptr file(new RemoteFile(f)); + R_UNLESS(file != nullptr, fs::ResultAllocationFailureInNew()); + + *out_file = std::move(file); return ResultSuccess(); } virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) override final { - FsDir d; - R_TRY(fsFsOpenDirectory(this->base_fs.get(), path, mode, &d)); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); - *out_dir = std::make_unique(d); + FsDir d; + R_TRY(fsFsOpenDirectory(std::addressof(this->base_fs), sf_path.str, mode, &d)); + + std::unique_ptr dir(new RemoteDirectory(d)); + R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInNew()); + + *out_dir = std::move(dir); return ResultSuccess(); } 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) { - 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) { - 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) { - 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) { + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); 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) { - return fsFsQueryEntry(this->base_fs.get(), dst, dst_size, src, src_size, path, static_cast(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(query)); } }; diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp new file mode 100644 index 000000000..46f36d41d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp @@ -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 . + */ +#pragma once +#include + +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, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp index e2bcb1304..a847a258d 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp @@ -14,9 +14,9 @@ * along with this program. If not, see . */ #pragma once -#include "../fs/fs_common.hpp" -#include "../fs/fs_directory.hpp" -#include "../sf/sf_buffer_tags.hpp" +#include +#include +#include namespace ams::fssrv::sf { diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index 4efc4377e..cffff25a4 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -25,4 +25,5 @@ #include #include #include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp index f555627d6..c0fcd21f8 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp @@ -42,7 +42,7 @@ namespace ams::ncm { 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 u16 size_high = static_cast(size >> 32); return { diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp new file mode 100644 index 000000000..4f84e09a4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +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(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp index 041582e00..39b63231b 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp @@ -19,11 +19,19 @@ namespace ams::ncm { struct ContentManagerConfig { - bool import_database_from_system; + bool build_system_database; bool import_database_from_system_on_sd; - constexpr bool HasAnyImport() const { - return this->import_database_from_system || this->import_database_from_system_on_sd; + bool HasAnyConfig() const { + 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; } }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index 507d7b24c..fd274264a 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include 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 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; public: diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp index 0bda84139..b41bcb532 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp @@ -110,7 +110,7 @@ namespace ams::ncm { return lc; } - ListCount ListContentMeta(ContentMetaKey *dst, size_t dst_size, ContentMetaType type, ApplicationId app_id = InvalidApplicationId, u64 min = std::numeric_limits::min(), u64 max = std::numeric_limits::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::min(), u64 max = std::numeric_limits::max(), ContentInstallType install_type = ContentInstallType::Full) { ListCount lc = {}; R_ABORT_UNLESS(this->interface->List(std::addressof(lc.total), std::addressof(lc.written), sf::OutArray(dst, dst_size), type, app_id, min, max, install_type)); return lc; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp new file mode 100644 index 000000000..6673e063d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp @@ -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 . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::ncm { + + Result ReadContentMetaPath(AutoBuffer *out, const char *path); + +} diff --git a/libraries/libstratosphere/source/fs/fs_content.cpp b/libraries/libstratosphere/source/fs/fs_content.cpp new file mode 100644 index 000000000..d36aaef2f --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_content.cpp @@ -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 . + */ +#include +#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(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); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp index 7618f7b11..1a10260a0 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp @@ -176,7 +176,7 @@ namespace ams::fs { R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); std::unique_ptr file_accessor(new impl::FileAccessor(std::move(file), nullptr, static_cast(mode))); - R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInUserFileSystem()); + R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInNew()); out->handle = file_accessor.release(); return ResultSuccess(); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp new file mode 100644 index 000000000..ef89ee0b2 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp @@ -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 . + */ +#include + +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(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp index 530780437..e9ee108c9 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -108,25 +108,22 @@ namespace ams::ncm { } } - ALWAYS_INLINE bool ShouldPerformImport(const ContentManagerConfig &config, const char *bis_mount_name) { - AMS_ASSERT(config.HasAnyImport()); - if (config.import_database_from_system) { - /* If we're importing from system, just do the import. */ - return true; - } else /* if (config.import_database_from_system_on_sd) */ { - /* If we're importing from system on SD, make sure that the signed system partition is valid. */ - const auto version = hos::GetVersion(); - if (version >= hos::Version_800 || version < hos::Version_400) { - /* On >= 8.0.0, a simpler method was added to check validity. */ - /* This also works on < 4.0.0 (though the system partition will never be on-sd there), */ - /* and so this will always return false. */ - char path[fs::MountNameLengthMax + 2 /* :/ */ + 1]; - std::snprintf(path, sizeof(path), "%s:/", bis_mount_name); - return fs::IsSignedSystemPartitionOnSdCardValid(path); - } else { - /* On 4.0.0-7.0.1, use the remote command to validate the system partition. */ - return fs::IsSignedSystemPartitionOnSdCardValidDeprecated(); - } + ALWAYS_INLINE bool IsSignedSystemPartitionOnSdCardValid(const char *bis_mount_name) { + /* Signed system partition should never be checked on < 4.0.0, as it did not exist before then. */ + AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_400); + + /* If we're importing from system on SD, make sure that the signed system partition is valid. */ + const auto version = hos::GetVersion(); + if (version >= hos::Version_800) { + /* On >= 8.0.0, a simpler method was added to check validity. */ + /* This also works on < 4.0.0 (though the system partition will never be on-sd there), */ + /* and so this will always return false. */ + char path[fs::MountNameLengthMax + 2 /* :/ */ + 1]; + std::snprintf(path, sizeof(path), "%s:/", bis_mount_name); + return fs::IsSignedSystemPartitionOnSdCardValid(path); + } else { + /* 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(); } - 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); /* Obtain the content meta database root. */ @@ -269,6 +266,46 @@ namespace ams::ncm { 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) { std::scoped_lock lk(this->mutex); @@ -297,22 +334,15 @@ namespace ams::ncm { if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) { R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem)); - /* NOTE: Nintendo added support for building/importing in 4.0.0. */ - /* However, there's no reason to restrict this on a per-version basis. */ - - /* If we should import the database from system, do so and verify it. */ - if (config.HasAnyImport()) { - /* 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 (ShouldPerformImport(config, bis_mount_name.str)) { - R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, bis_mount_name.str, "cnmtdb.arc")); - R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem)); - } + /* Try to build or import a database, depending on our configuration. */ + if (config.ShouldBuildDatabase()) { + /* If we should build the database, do so. */ + R_TRY(this->BuildContentMetaDatabase(StorageId::BuiltInSystem)); + R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem)); + } else if (config.ShouldImportDatabaseFromSignedSystemPartitionOnSd()) { + /* Otherwise if we should import the database from the SD, do so. */ + R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, true)); + R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem)); } } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp new file mode 100644 index 000000000..df9a9e0e8 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp @@ -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 . + */ +#include +#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(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(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp index 86b8ba5bf..56881b558 100644 --- a/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp @@ -155,21 +155,4 @@ namespace ams::ncm::impl { 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(); - } - } diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index be0cb5c36..7af09fc21 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -56,6 +56,8 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215); R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216); 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(AllocationFailureInContentStorageB, 3221); R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222); @@ -79,7 +81,7 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377); 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); @@ -235,16 +237,17 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201); R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449); diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp index 2f0d6cd6e..1963c82d7 100644 --- a/stratosphere/ncm/source/ncm_main.cpp +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -189,9 +189,9 @@ namespace { /* Compile-time configuration. */ #ifdef NCM_BUILD_FOR_INTITIALIZE - constexpr inline bool ImportSystemDatabase = true; + constexpr inline bool BuildSystemDatabase = true; #else - constexpr inline bool ImportSystemDatabase = false; + constexpr inline bool BuildSystemDatabase = false; #endif #ifdef NCM_BUILD_FOR_SAFEMODE @@ -200,9 +200,9 @@ namespace { constexpr inline bool ImportSystemDatabaseFromSignedSystemPartitionOnSdCard = false; #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 }; }