From 0fe4e2950e6fd428eb83345cf02e02fbbaeb2ca9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 4 Mar 2020 04:02:33 -0800 Subject: [PATCH] fs: Add wrappers needed for ncm --- .../include/stratosphere/fs.hpp | 5 + .../stratosphere/fs/fs_content_storage.hpp | 34 ++++ .../include/stratosphere/fs/fs_game_card.hpp | 47 +++++ .../fs/fs_save_data_management.hpp | 26 +++ .../stratosphere/fs/fs_save_data_types.hpp | 168 ++++++++++++++++++ .../stratosphere/fs/fs_system_save_data.hpp | 40 +++++ .../fs/impl/fs_common_mount_name.hpp | 35 ++-- .../stratosphere/kvdb/kvdb_auto_buffer.hpp | 7 +- .../include/stratosphere/ncm.hpp | 1 + .../stratosphere/ncm/ncm_auto_buffer.hpp | 89 ++++++++++ .../include/stratosphere/ncm/ncm_types.hpp | 4 - .../source/fs/fs_content_storage.cpp | 93 ++++++++++ .../source/fs/fs_game_card.cpp | 87 +++++++++ .../source/fs/fs_save_data_management.cpp | 110 ++++++++++++ .../libstratosphere/source/fs/fs_sd_card.cpp | 4 + .../source/fs/fs_system_save_data.cpp | 61 +++++++ .../include/vapours/results/fs_results.hpp | 9 + 17 files changed, 798 insertions(+), 22 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp create mode 100644 libraries/libstratosphere/source/fs/fs_content_storage.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_game_card.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_save_data_management.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_system_save_data.cpp diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 0fb067c34..fcbb5aac2 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -29,4 +29,9 @@ #include "fs/fs_mount.hpp" #include "fs/fs_path_tool.hpp" #include "fs/fs_path_utils.hpp" +#include "fs/fs_content_storage.hpp" +#include "fs/fs_game_card.hpp" #include "fs/fs_sd_card.hpp" +#include "fs/fs_save_data_types.hpp" +#include "fs/fs_save_data_management.hpp" +#include "fs/fs_system_save_data.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp new file mode 100644 index 000000000..4a52391e2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.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" + +namespace ams::fs { + + enum class ContentStorageId : u32 { + System = 0, + User = 1, + SdCard = 2, + }; + + constexpr inline const char * const ContentStorageDirectoryName = "Contents"; + + const char *GetContentStorageMountName(ContentStorageId id); + + Result MountContentStorage(ContentStorageId id); + Result MountContentStorage(const char *name, ContentStorageId id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp new file mode 100644 index 000000000..612b7844a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + enum class GameCardPartition { + Update = 0, + Normal = 1, + Secure = 2, + Logo = 3, + }; + + enum class GameCardPartitionRaw { + NormalReadable, + SecureReadable, + RootWriteable, + }; + + enum class GameCardAttribute : u8 { + AutoBootFlag = (1 << 0), + HistoryEraseFlag = (1 << 1), + RepairToolFlag = (1 << 2), + DifferentRegionCupToTerraDeviceFlag = (1 << 3), + DifferentRegionCupToGlobalDeviceFlag = (1 << 4), + }; + + using GameCardHandle = u32; + + Result GetGameCardHandle(GameCardHandle *out); + Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp new file mode 100644 index 000000000..9b1eb374f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_save_data_types.hpp" + +namespace ams::fs { + + Result GetSaveDataFlags(u32 *out, SaveDataId id); + Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id); + Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp new file mode 100644 index 000000000..42e6fc466 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + using SaveDataId = u64; + using SystemSaveDataId = u64; + using SystemBcatSaveDataId = SystemSaveDataId; + + enum class SaveDataSpaceId : u8 { + System = 0, + User = 1, + SdSystem = 2, + Temporary = 3, + SdUser = 4, + + ProperSystem = 100, + SafeMode = 101, + }; + + enum class SaveDataType : u8 { + System = 0, + Account = 1, + Bcat = 2, + Device = 3, + Temporary = 4, + Cache = 5, + SystemBcat = 6, + }; + + enum class SaveDataRank : u8 { + Primary = 0, + Secondary = 1, + }; + + struct UserId { + u64 data[2]; + }; + static_assert(std::is_pod::value); + + constexpr inline bool operator<(const UserId &lhs, const UserId &rhs) { + if (lhs.data[0] < rhs.data[0]) { + return true; + } else if (lhs.data[0] == rhs.data[0] && lhs.data[1] < rhs.data[1]) { + return true; + } else { + return false; + } + } + + constexpr inline bool operator==(const UserId &lhs, const UserId &rhs) { + return lhs.data[0] == rhs.data[0] && lhs.data[1] == rhs.data[1]; + } + + constexpr inline bool operator!=(const UserId &lhs, const UserId &rhs) { + return !(lhs == rhs); + } + + constexpr inline SystemSaveDataId InvalidSystemSaveDataId = 0; + constexpr inline UserId InvalidUserId = {}; + + struct SaveDataCreationInfo { + s64 size; + s64 journal_size; + s64 block_size; + u64 owner_id; + u32 flags; + SaveDataSpaceId space_id; + bool pseudo; + u8 reserved[0x1A]; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(SaveDataCreationInfo) == 0x40); + + struct SaveDataAttribute { + ncm::ProgramId program_id; + UserId user_id; + SystemSaveDataId system_save_data_id; + SaveDataType type; + SaveDataRank rank; + u16 index; + u8 reserved[0x1C]; + + static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index, SaveDataRank rank) { + return { + .program_id = program_id, + .user_id = user_id, + .system_save_data_id = system_save_data_id, + .type = type, + .rank = rank, + .index = index, + }; + } + + static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index) { + return Make(program_id, type, user_id, system_save_data_id, index, SaveDataRank::Primary); + } + + static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id) { + return Make(program_id, type, user_id, system_save_data_id, 0, SaveDataRank::Primary); + } + }; + static_assert(sizeof(SaveDataAttribute) == 0x40); + static_assert(std::is_trivially_destructible::value); + + constexpr inline bool operator<(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) { + #define FS_SDA_CHECK_FIELD(FIELD) \ + if (lhs.FIELD < rhs.FIELD) { \ + return true; \ + } else if (lhs.FIELD != rhs.FIELD) { \ + return false; \ + } + + FS_SDA_CHECK_FIELD(program_id); + FS_SDA_CHECK_FIELD(user_id); + FS_SDA_CHECK_FIELD(system_save_data_id); + FS_SDA_CHECK_FIELD(index); + FS_SDA_CHECK_FIELD(rank); + return false; + + #undef FS_SDA_CHECK_FIELD + } + + constexpr inline bool operator==(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) { + return lhs.program_id == rhs.program_id && + lhs.user_id == rhs.user_id && + lhs.system_save_data_id == rhs.system_save_data_id && + lhs.type == rhs.type && + lhs.rank == rhs.rank && + lhs.index == rhs.index; + } + + constexpr inline bool operator!=(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) { + return !(lhs == rhs); + } + + constexpr inline size_t DefaultSaveDataBlockSize = 16_KB; + + struct SaveDataExtraData { + SaveDataAttribute attr; + u64 owner_id; + s64 timestamp; + u32 flags; + u8 pad[4]; + s64 available_size; + s64 journal_size; + s64 commit_id; + u8 unused[0x190]; + }; + static_assert(sizeof(SaveDataExtraData) == 0x200); + static_assert(std::is_pod::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp new file mode 100644 index 000000000..95093dcde --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp @@ -0,0 +1,40 @@ +/* + * 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 "fs_save_data_types.hpp" + +namespace ams::fs { + + void DisableAutoSaveDataCreation(); + + Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + + Result MountSystemSaveData(const char *name, SystemSaveDataId id); + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id); + + Result MountSystemSaveData(const char *name, SystemSaveDataId id, UserId user_id); + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id); + + Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp index adeec7305..4e743762f 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp @@ -18,26 +18,33 @@ namespace ams::fs::impl { /* Delimiting of mount names. */ - constexpr inline const char ReservedMountNamePrefixCharacter = '@'; - constexpr inline const char *MountNameDelimiter = ":/"; + constexpr inline const char ReservedMountNamePrefixCharacter = '@'; + constexpr inline const char * const MountNameDelimiter = ":/"; /* Filesystem names. */ - constexpr inline const char *HostRootFileSystemMountName = "@Host"; - constexpr inline const char *SdCardFileSystemMountName = "@Sdcard"; - constexpr inline const char *GameCardFileSystemMountName = "@Gc"; + constexpr inline const char * const HostRootFileSystemMountName = "@Host"; + constexpr inline const char * const SdCardFileSystemMountName = "@Sdcard"; + constexpr inline const char * const GameCardFileSystemMountName = "@Gc"; - constexpr inline size_t GameCardFileSystemMountNameSuffixLength = 1; - constexpr inline const char *GameCardFileSystemMountNameUpdateSuffix = "U"; - constexpr inline const char *GameCardFileSystemMountNameNormalSuffix = "N"; - constexpr inline const char *GameCardFileSystemMountNameSecureSuffix = "S"; + constexpr inline size_t GameCardFileSystemMountNameSuffixLength = 1; + + constexpr inline const char * const GameCardFileSystemMountNameUpdateSuffix = "U"; + constexpr inline const char * const GameCardFileSystemMountNameNormalSuffix = "N"; + constexpr inline const char * const GameCardFileSystemMountNameSecureSuffix = "S"; /* Built-in storage names. */ - constexpr inline const char *BisCalibrationFilePartitionMountName = "@CalibFile"; - constexpr inline const char *BisSafeModePartitionMountName = "@Safe"; - constexpr inline const char *BisUserPartitionMountName = "@User"; - constexpr inline const char *BisSystemPartitionMountName = "@System"; + constexpr inline const char * const BisCalibrationFilePartitionMountName = "@CalibFile"; + constexpr inline const char * const BisSafeModePartitionMountName = "@Safe"; + constexpr inline const char * const BisUserPartitionMountName = "@User"; + constexpr inline const char * const BisSystemPartitionMountName = "@System"; + + /* Content storage names. */ + constexpr inline const char * const ContentStorageSystemMountName = "@SystemContent"; + constexpr inline const char * const ContentStorageUserMountName = "@UserContent"; + constexpr inline const char * const ContentStorageSdCardMountName = "@SdCardContent"; + /* Registered update partition. */ - constexpr inline const char *RegisteredUpdatePartitionMountName = "@RegUpdate"; + constexpr inline const char * const RegisteredUpdatePartitionMountName = "@RegUpdate"; } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp index bb5378b70..23ab2c96e 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp @@ -39,7 +39,7 @@ namespace ams::kvdb { } AutoBuffer& operator=(AutoBuffer &&rhs) { - rhs.Swap(*this); + AutoBuffer(std::move(rhs)).Swap(*this); return *this; } @@ -70,9 +70,8 @@ namespace ams::kvdb { /* Allocate a buffer. */ this->buffer = static_cast(std::malloc(size)); - if (this->buffer == nullptr) { - return ResultAllocationFailed(); - } + R_UNLESS(this->buffer != nullptr, ResultAllocationFailed()); + this->size = size; return ResultSuccess(); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index 38811eaec..80ddb5ea3 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -17,6 +17,7 @@ #pragma once #include "ncm/ncm_types.hpp" +#include "ncm/ncm_auto_buffer.hpp" #include "ncm/ncm_content_meta.hpp" #include "ncm/ncm_content_meta_database.hpp" #include "ncm/ncm_content_storage.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp new file mode 100644 index 000000000..af2c2b54a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::ncm { + + class AutoBuffer { + NON_COPYABLE(AutoBuffer); + private: + u8 *buffer; + size_t size; + public: + AutoBuffer() : buffer(nullptr), size(0) { /* ... */ } + + ~AutoBuffer() { + this->Reset(); + } + + AutoBuffer(AutoBuffer &&rhs) { + this->buffer = rhs.buffer; + this->size = rhs.size; + rhs.buffer = nullptr; + rhs.size = 0; + } + + AutoBuffer& operator=(AutoBuffer &&rhs) { + AutoBuffer(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(AutoBuffer &rhs) { + std::swap(this->buffer, rhs.buffer); + std::swap(this->size, rhs.size); + } + + void Reset() { + if (this->buffer != nullptr) { + std::free(this->buffer); + this->buffer = nullptr; + this->size = 0; + } + } + + u8 *Get() const { + return this->buffer; + } + + size_t GetSize() const { + return this->size; + } + + Result Initialize(size_t size) { + /* Check that we're not already initialized. */ + AMS_ABORT_UNLESS(this->buffer == nullptr); + + /* Allocate a buffer. */ + this->buffer = static_cast(std::malloc(size)); + R_UNLESS(this->buffer != nullptr, ResultAllocationFailed()); + + this->size = size; + return ResultSuccess(); + } + + Result Initialize(const void *buf, size_t size) { + /* Create a new buffer of the right size. */ + R_TRY(this->Initialize(size)); + + /* Copy the input data in. */ + std::memcpy(this->buffer, buf, size); + + return ResultSuccess(); + } + }; +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp index 31fdd73e6..167764a13 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp @@ -55,10 +55,6 @@ namespace ams::ncm { Unknown = 7, }; - struct MountName { - char name[0x10]; - }; - struct alignas(8) PlaceHolderId { util::Uuid uuid; diff --git a/libraries/libstratosphere/source/fs/fs_content_storage.cpp b/libraries/libstratosphere/source/fs/fs_content_storage.cpp new file mode 100644 index 000000000..59147333b --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_content_storage.cpp @@ -0,0 +1,93 @@ +/* + * 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 { + + class ContentStorageCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { + private: + const ContentStorageId id; + public: + explicit ContentStorageCommonMountNameGenerator(ContentStorageId i) : id(i) { /* ... */ } + + virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override { + /* Determine how much space we need. */ + const size_t needed_size = strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2; + AMS_ABORT_UNLESS(dst_size >= needed_size); + + /* Generate the name. */ + auto size = std::snprintf(dst, dst_size, "%s:", GetContentStorageMountName(id)); + AMS_ASSERT(static_cast(size) == needed_size - 1); + + return ResultSuccess(); + } + }; + + } + + const char *GetContentStorageMountName(ContentStorageId id) { + switch (id) { + case ContentStorageId::System: return impl::ContentStorageSystemMountName; + case ContentStorageId::User: return impl::ContentStorageUserMountName; + case ContentStorageId::SdCard: return impl::ContentStorageSdCardMountName; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + Result MountContentStorage(ContentStorageId id) { + return MountContentStorage(GetContentStorageMountName(id), id); + } + + Result MountContentStorage(const char *name, ContentStorageId id) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountNameAllowingReserved(name)); + + /* It can take some time for the system partition to be ready (if it's on the SD card). */ + /* Thus, we will retry up to 10 times, waiting one second each time. */ + constexpr size_t MaxRetries = 10; + constexpr u64 RetryInterval = 1'000'000'000ul; + + /* Mount the content storage, use libnx bindings. */ + ::FsFileSystem fs; + for (size_t i = 0; i < MaxRetries; i++) { + R_TRY_CATCH(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id))) { + R_CATCH(fs::ResultSystemPartitionNotReady) { + if (i < MaxRetries - 1) { + /* TODO: os::SleepThread */ + svcSleepThread(RetryInterval); + } else { + return fs::ResultSystemPartitionNotReady(); + } + } + } R_END_TRY_CATCH; + } + + /* Allocate a new filesystem wrapper. */ + std::unique_ptr fsa(new RemoteFileSystem(fs)); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA()); + + /* Allocate a new mountname generator. */ + std::unique_ptr generator(new ContentStorageCommonMountNameGenerator(id)); + R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB()); + + /* Register. */ + return fsa::Register(name, std::move(fsa), std::move(generator)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_game_card.cpp b/libraries/libstratosphere/source/fs/fs_game_card.cpp new file mode 100644 index 000000000..97c5ddb4a --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_game_card.cpp @@ -0,0 +1,87 @@ +/* + * 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 { + + const char *GetGameCardMountNameSuffix(GameCardPartition which) { + switch (which) { + case GameCardPartition::Update: return impl::GameCardFileSystemMountNameUpdateSuffix; + case GameCardPartition::Normal: return impl::GameCardFileSystemMountNameNormalSuffix; + case GameCardPartition::Secure: return impl::GameCardFileSystemMountNameSecureSuffix; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + class GameCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { + private: + const GameCardHandle handle; + const GameCardPartition partition; + public: + explicit GameCardCommonMountNameGenerator(GameCardHandle h, GameCardPartition p) : handle(h), partition(p) { /* ... */ } + + virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override { + /* Determine how much space we need. */ + const size_t needed_size = strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + strnlen(GetGameCardMountNameSuffix(this->partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2; + AMS_ABORT_UNLESS(dst_size >= needed_size); + + /* Generate the name. */ + auto size = std::snprintf(dst, dst_size, "%s%s%08x:", impl::GameCardFileSystemMountName, GetGameCardMountNameSuffix(this->partition), this->handle); + AMS_ASSERT(static_cast(size) == needed_size - 1); + + return ResultSuccess(); + } + }; + + } + + Result GetGameCardHandle(GameCardHandle *out) { + /* TODO: fs::DeviceOperator */ + /* Open a DeviceOperator. */ + ::FsDeviceOperator d; + R_TRY(fsOpenDeviceOperator(std::addressof(d))); + ON_SCOPE_EXIT { fsDeviceOperatorClose(std::addressof(d)); }; + + /* Get the handle. */ + static_assert(sizeof(GameCardHandle) == sizeof(::FsGameCardHandle)); + return fsDeviceOperatorGetGameCardHandle(std::addressof(d), reinterpret_cast<::FsGameCardHandle *>(out)); + } + + Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountNameAllowingReserved(name)); + + /* Open gamecard filesystem. This uses libnx bindings. */ + ::FsFileSystem fs; + const ::FsGameCardHandle _hnd = {handle}; + R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition))); + + /* Allocate a new filesystem wrapper. */ + std::unique_ptr fsa(new RemoteFileSystem(fs)); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC()); + + /* Allocate a new mountname generator. */ + std::unique_ptr generator(new GameCardCommonMountNameGenerator(handle, partition)); + R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD()); + + /* Register. */ + return fsa::Register(name, std::move(fsa), std::move(generator)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp new file mode 100644 index 000000000..aa6216ebc --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp @@ -0,0 +1,110 @@ +/* + * 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 { + + Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataId id) { + return fsReadSaveDataFileSystemExtraData(out, sizeof(*out), id); + } + + Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataSpaceId space_id, SaveDataId id) { + return fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(out, sizeof(*out), static_cast<::FsSaveDataSpaceId>(space_id), id); + } + + Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId space_id, SaveDataId id, const SaveDataExtraData &extra_data) { + return fsWriteSaveDataFileSystemExtraData(std::addressof(extra_data), sizeof(extra_data), static_cast<::FsSaveDataSpaceId>(space_id), id); + } + + } + + void DisableAutoSaveDataCreation() { + /* Use libnx binding. */ + R_ABORT_UNLESS(fsDisableAutoSaveDataCreation()); + } + + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id); + const SaveDataCreationInfo info = { + .size = size, + .journal_size = journal_size, + .block_size = DefaultSaveDataBlockSize, + .owner_id = owner_id, + .flags = flags, + .space_id = space_id, + .pseudo = false, + }; + + static_assert(sizeof(SaveDataAttribute) == sizeof(::FsSaveDataAttribute)); + static_assert(sizeof(SaveDataCreationInfo) == sizeof(::FsSaveDataCreationInfo)); + return fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast(std::addressof(attribute)), reinterpret_cast(std::addressof(info))); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, InvalidUserId, 0, size, journal_size, flags); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, InvalidUserId, owner_id, size, journal_size, flags); + } + + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(space_id, save_id, InvalidUserId, owner_id, size, journal_size, flags); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, 0, size, journal_size, flags); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, owner_id, size, journal_size, flags); + } + + Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) { + const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id); + + /* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */ + AMS_UNUSED(attribute); + AMS_ABORT(); + } + + Result GetSaveDataFlags(u32 *out, SaveDataId id) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id)); + + *out = extra_data.flags; + return ResultSuccess(); + } + + Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), space_id, id)); + + *out = extra_data.flags; + return ResultSuccess(); + } + + Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), space_id, id)); + extra_data.flags = flags; + return impl::WriteSaveDataFileSystemExtraData(space_id, id, extra_data); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_sd_card.cpp b/libraries/libstratosphere/source/fs/fs_sd_card.cpp index 2bdc2cb34..cb20560da 100644 --- a/libraries/libstratosphere/source/fs/fs_sd_card.cpp +++ b/libraries/libstratosphere/source/fs/fs_sd_card.cpp @@ -14,10 +14,14 @@ * along with this program. If not, see . */ #include +#include "fsa/fs_mount_utils.hpp" namespace ams::fs { Result MountSdCard(const char *name) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + /* Open the SD card. This uses libnx bindings. */ FsFileSystem fs; R_TRY(fsOpenSdCardFileSystem(std::addressof(fs))); diff --git a/libraries/libstratosphere/source/fs/fs_system_save_data.cpp b/libraries/libstratosphere/source/fs/fs_system_save_data.cpp new file mode 100644 index 000000000..c4802cbfe --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_system_save_data.cpp @@ -0,0 +1,61 @@ +/* + * 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 { + + Result MountSystemSaveDataImpl(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id, SaveDataType type) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Create the attribute. */ + const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, type, user_id, id); + static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute)); + + /* Open the filesystem, use libnx bindings. */ + ::FsFileSystem fs; + R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast(std::addressof(attribute)))); + + /* Allocate a new filesystem wrapper. */ + std::unique_ptr fsa(new RemoteFileSystem(fs)); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA()); + + /* Register. */ + return fsa::Register(name, std::move(fsa)); + } + + } + + Result MountSystemSaveData(const char *name, SystemSaveDataId id) { + return MountSystemSaveData(name, id, InvalidUserId); + } + + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id) { + return MountSystemSaveData(name, space_id, id, InvalidUserId); + } + + Result MountSystemSaveData(const char *name, SystemSaveDataId id, UserId user_id) { + return MountSystemSaveData(name, SaveDataSpaceId::System, id, user_id); + } + + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) { + return MountSystemSaveDataImpl(name, space_id, id, user_id, SaveDataType::System); + } + +} diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index 02483283c..74ed9aafe 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -47,11 +47,20 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(NotImplemented, 3001); R_DEFINE_ERROR_RESULT(OutOfRange, 3005); + R_DEFINE_ERROR_RESULT(SystemPartitionNotReady, 3100); + R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499); R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorA, 3211); R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardA, 3225); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228); R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244); R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245); + R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246); R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);