filesystem.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "boost/container/flat_map.hpp"
  5. #include "common/assert.h"
  6. #include "common/common_paths.h"
  7. #include "common/file_util.h"
  8. #include "core/core.h"
  9. #include "core/file_sys/errors.h"
  10. #include "core/file_sys/filesystem.h"
  11. #include "core/file_sys/vfs.h"
  12. #include "core/file_sys/vfs_offset.h"
  13. #include "core/file_sys/vfs_real.h"
  14. #include "core/hle/kernel/process.h"
  15. #include "core/hle/service/filesystem/filesystem.h"
  16. #include "core/hle/service/filesystem/fsp_srv.h"
  17. namespace Service::FileSystem {
  18. // Size of emulated sd card free space, reported in bytes.
  19. // Just using 32GB because thats reasonable
  20. // TODO(DarkLordZach): Eventually make this configurable in settings.
  21. constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
  22. static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
  23. const std::string& dir_name) {
  24. if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\")
  25. return base;
  26. return base->GetDirectoryRelative(dir_name);
  27. }
  28. VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
  29. : backing(backing_) {}
  30. std::string VfsDirectoryServiceWrapper::GetName() const {
  31. return backing->GetName();
  32. }
  33. ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
  34. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  35. auto file = dir->CreateFile(FileUtil::GetFilename(path));
  36. if (file == nullptr)
  37. return ResultCode(-1);
  38. if (!file->Resize(size))
  39. return ResultCode(-1);
  40. return RESULT_SUCCESS;
  41. }
  42. ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
  43. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  44. if (!backing->DeleteFile(FileUtil::GetFilename(path)))
  45. return ResultCode(-1);
  46. return RESULT_SUCCESS;
  47. }
  48. ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
  49. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  50. if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
  51. dir = backing;
  52. auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
  53. if (new_dir == nullptr)
  54. return ResultCode(-1);
  55. return RESULT_SUCCESS;
  56. }
  57. ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
  58. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  59. if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path)))
  60. return ResultCode(-1);
  61. return RESULT_SUCCESS;
  62. }
  63. ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
  64. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  65. if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)))
  66. return ResultCode(-1);
  67. return RESULT_SUCCESS;
  68. }
  69. ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
  70. const std::string& dest_path) const {
  71. auto src = backing->GetFileRelative(src_path);
  72. if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
  73. // Use more-optimized vfs implementation rename.
  74. if (src == nullptr)
  75. return FileSys::ERROR_PATH_NOT_FOUND;
  76. if (!src->Rename(FileUtil::GetFilename(dest_path)))
  77. return ResultCode(-1);
  78. return RESULT_SUCCESS;
  79. }
  80. // Move by hand -- TODO(DarkLordZach): Optimize
  81. auto c_res = CreateFile(dest_path, src->GetSize());
  82. if (c_res != RESULT_SUCCESS)
  83. return c_res;
  84. auto dest = backing->GetFileRelative(dest_path);
  85. ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
  86. ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
  87. "Could not write all of the bytes but everything else has succeded.");
  88. if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path)))
  89. return ResultCode(-1);
  90. return RESULT_SUCCESS;
  91. }
  92. ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
  93. const std::string& dest_path) const {
  94. auto src = GetDirectoryRelativeWrapped(backing, src_path);
  95. if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
  96. // Use more-optimized vfs implementation rename.
  97. if (src == nullptr)
  98. return FileSys::ERROR_PATH_NOT_FOUND;
  99. if (!src->Rename(FileUtil::GetFilename(dest_path)))
  100. return ResultCode(-1);
  101. return RESULT_SUCCESS;
  102. }
  103. // TODO(DarkLordZach): Implement renaming across the tree (move).
  104. ASSERT_MSG(false,
  105. "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
  106. "don't match -- UNIMPLEMENTED",
  107. src_path, dest_path);
  108. return ResultCode(-1);
  109. }
  110. ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
  111. FileSys::Mode mode) const {
  112. auto file = backing->GetFileRelative(path);
  113. if (file == nullptr)
  114. return FileSys::ERROR_PATH_NOT_FOUND;
  115. if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) {
  116. return MakeResult<FileSys::VirtualFile>(
  117. std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
  118. }
  119. return MakeResult<FileSys::VirtualFile>(file);
  120. }
  121. ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
  122. auto dir = GetDirectoryRelativeWrapped(backing, path);
  123. if (dir == nullptr)
  124. return ResultCode(-1);
  125. return MakeResult(dir);
  126. }
  127. u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
  128. if (backing->IsWritable())
  129. return EMULATED_SD_REPORTED_SIZE;
  130. return 0;
  131. }
  132. ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
  133. const std::string& path) const {
  134. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  135. if (dir == nullptr)
  136. return ResultCode(-1);
  137. auto filename = FileUtil::GetFilename(path);
  138. if (dir->GetFile(filename) != nullptr)
  139. return MakeResult(FileSys::EntryType::File);
  140. if (dir->GetSubdirectory(filename) != nullptr)
  141. return MakeResult(FileSys::EntryType::Directory);
  142. return ResultCode(-1);
  143. }
  144. // A deferred filesystem for nand save data.
  145. // This must be deferred because the directory is dependent on title id, which is not set at
  146. // registration time.
  147. struct SaveDataDeferredFilesystem : DeferredFilesystem {
  148. protected:
  149. FileSys::VirtualDir CreateFilesystem() override {
  150. u64 title_id = Core::CurrentProcess()->program_id;
  151. // TODO(DarkLordZach): Users
  152. u32 user_id = 0;
  153. std::string nand_directory = fmt::format(
  154. "{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id);
  155. auto savedata =
  156. std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write);
  157. return savedata;
  158. }
  159. };
  160. /**
  161. * Map of registered file systems, identified by type. Once an file system is registered here, it
  162. * is never removed until UnregisterFileSystems is called.
  163. */
  164. static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
  165. static FileSys::VirtualFile filesystem_romfs = nullptr;
  166. ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
  167. auto result = filesystem_map.emplace(type, std::move(factory));
  168. bool inserted = result.second;
  169. ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
  170. auto& filesystem = result.first->second;
  171. LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type));
  172. return RESULT_SUCCESS;
  173. }
  174. ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) {
  175. ASSERT_MSG(filesystem_romfs == nullptr,
  176. "Tried to register more than one system with same id code");
  177. filesystem_romfs = filesystem;
  178. LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
  179. static_cast<u32>(Type::RomFS));
  180. return RESULT_SUCCESS;
  181. }
  182. ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) {
  183. LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
  184. auto itr = filesystem_map.find(type);
  185. if (itr == filesystem_map.end()) {
  186. // TODO(bunnei): Find a better error code for this
  187. return ResultCode(-1);
  188. }
  189. return MakeResult(itr->second->Get());
  190. }
  191. ResultVal<FileSys::VirtualFile> OpenRomFS() {
  192. if (filesystem_romfs == nullptr)
  193. return ResultCode(-1);
  194. return MakeResult(filesystem_romfs);
  195. }
  196. ResultCode FormatFileSystem(Type type) {
  197. LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type));
  198. auto itr = filesystem_map.find(type);
  199. if (itr == filesystem_map.end()) {
  200. // TODO(bunnei): Find a better error code for this
  201. return ResultCode(-1);
  202. }
  203. return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory(
  204. itr->second->Get()->GetName())
  205. ? RESULT_SUCCESS
  206. : ResultCode(-1);
  207. }
  208. void RegisterFileSystems() {
  209. filesystem_map.clear();
  210. filesystem_romfs = nullptr;
  211. std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
  212. auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
  213. RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
  214. RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);
  215. }
  216. void InstallInterfaces(SM::ServiceManager& service_manager) {
  217. RegisterFileSystems();
  218. std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
  219. }
  220. } // namespace Service::FileSystem