filesystem.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <utility>
  5. #include "common/assert.h"
  6. #include "common/file_util.h"
  7. #include "core/core.h"
  8. #include "core/file_sys/bis_factory.h"
  9. #include "core/file_sys/control_metadata.h"
  10. #include "core/file_sys/errors.h"
  11. #include "core/file_sys/mode.h"
  12. #include "core/file_sys/partition_filesystem.h"
  13. #include "core/file_sys/patch_manager.h"
  14. #include "core/file_sys/registered_cache.h"
  15. #include "core/file_sys/romfs_factory.h"
  16. #include "core/file_sys/savedata_factory.h"
  17. #include "core/file_sys/sdmc_factory.h"
  18. #include "core/file_sys/vfs.h"
  19. #include "core/file_sys/vfs_offset.h"
  20. #include "core/hle/kernel/process.h"
  21. #include "core/hle/service/filesystem/filesystem.h"
  22. #include "core/hle/service/filesystem/fsp_ldr.h"
  23. #include "core/hle/service/filesystem/fsp_pr.h"
  24. #include "core/hle/service/filesystem/fsp_srv.h"
  25. #include "core/loader/loader.h"
  26. namespace Service::FileSystem {
  27. // Size of emulated sd card free space, reported in bytes.
  28. // Just using 32GB because thats reasonable
  29. // TODO(DarkLordZach): Eventually make this configurable in settings.
  30. constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
  31. // A default size for normal/journal save data size if application control metadata cannot be found.
  32. // This should be large enough to satisfy even the most extreme requirements (~4.2GB)
  33. constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
  34. static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
  35. std::string_view dir_name_) {
  36. std::string dir_name(FileUtil::SanitizePath(dir_name_));
  37. if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
  38. return base;
  39. return base->GetDirectoryRelative(dir_name);
  40. }
  41. VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
  42. : backing(std::move(backing_)) {}
  43. VfsDirectoryServiceWrapper::~VfsDirectoryServiceWrapper() = default;
  44. std::string VfsDirectoryServiceWrapper::GetName() const {
  45. return backing->GetName();
  46. }
  47. ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
  48. std::string path(FileUtil::SanitizePath(path_));
  49. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  50. auto file = dir->CreateFile(FileUtil::GetFilename(path));
  51. if (file == nullptr) {
  52. // TODO(DarkLordZach): Find a better error code for this
  53. return ResultCode(-1);
  54. }
  55. if (!file->Resize(size)) {
  56. // TODO(DarkLordZach): Find a better error code for this
  57. return ResultCode(-1);
  58. }
  59. return RESULT_SUCCESS;
  60. }
  61. ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
  62. std::string path(FileUtil::SanitizePath(path_));
  63. if (path.empty()) {
  64. // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
  65. return RESULT_SUCCESS;
  66. }
  67. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  68. if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
  69. return FileSys::ERROR_PATH_NOT_FOUND;
  70. }
  71. if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
  72. // TODO(DarkLordZach): Find a better error code for this
  73. return ResultCode(-1);
  74. }
  75. return RESULT_SUCCESS;
  76. }
  77. ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
  78. std::string path(FileUtil::SanitizePath(path_));
  79. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  80. if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
  81. dir = backing;
  82. auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
  83. if (new_dir == nullptr) {
  84. // TODO(DarkLordZach): Find a better error code for this
  85. return ResultCode(-1);
  86. }
  87. return RESULT_SUCCESS;
  88. }
  89. ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
  90. std::string path(FileUtil::SanitizePath(path_));
  91. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  92. if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
  93. // TODO(DarkLordZach): Find a better error code for this
  94. return ResultCode(-1);
  95. }
  96. return RESULT_SUCCESS;
  97. }
  98. ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
  99. std::string path(FileUtil::SanitizePath(path_));
  100. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  101. if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
  102. // TODO(DarkLordZach): Find a better error code for this
  103. return ResultCode(-1);
  104. }
  105. return RESULT_SUCCESS;
  106. }
  107. ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
  108. const std::string sanitized_path(FileUtil::SanitizePath(path));
  109. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path));
  110. if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) {
  111. // TODO(DarkLordZach): Find a better error code for this
  112. return ResultCode(-1);
  113. }
  114. return RESULT_SUCCESS;
  115. }
  116. ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
  117. const std::string& dest_path_) const {
  118. std::string src_path(FileUtil::SanitizePath(src_path_));
  119. std::string dest_path(FileUtil::SanitizePath(dest_path_));
  120. auto src = backing->GetFileRelative(src_path);
  121. if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
  122. // Use more-optimized vfs implementation rename.
  123. if (src == nullptr)
  124. return FileSys::ERROR_PATH_NOT_FOUND;
  125. if (!src->Rename(FileUtil::GetFilename(dest_path))) {
  126. // TODO(DarkLordZach): Find a better error code for this
  127. return ResultCode(-1);
  128. }
  129. return RESULT_SUCCESS;
  130. }
  131. // Move by hand -- TODO(DarkLordZach): Optimize
  132. auto c_res = CreateFile(dest_path, src->GetSize());
  133. if (c_res != RESULT_SUCCESS)
  134. return c_res;
  135. auto dest = backing->GetFileRelative(dest_path);
  136. ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
  137. ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
  138. "Could not write all of the bytes but everything else has succeded.");
  139. if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
  140. // TODO(DarkLordZach): Find a better error code for this
  141. return ResultCode(-1);
  142. }
  143. return RESULT_SUCCESS;
  144. }
  145. ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
  146. const std::string& dest_path_) const {
  147. std::string src_path(FileUtil::SanitizePath(src_path_));
  148. std::string dest_path(FileUtil::SanitizePath(dest_path_));
  149. auto src = GetDirectoryRelativeWrapped(backing, src_path);
  150. if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
  151. // Use more-optimized vfs implementation rename.
  152. if (src == nullptr)
  153. return FileSys::ERROR_PATH_NOT_FOUND;
  154. if (!src->Rename(FileUtil::GetFilename(dest_path))) {
  155. // TODO(DarkLordZach): Find a better error code for this
  156. return ResultCode(-1);
  157. }
  158. return RESULT_SUCCESS;
  159. }
  160. // TODO(DarkLordZach): Implement renaming across the tree (move).
  161. ASSERT_MSG(false,
  162. "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
  163. "don't match -- UNIMPLEMENTED",
  164. src_path, dest_path);
  165. // TODO(DarkLordZach): Find a better error code for this
  166. return ResultCode(-1);
  167. }
  168. ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
  169. FileSys::Mode mode) const {
  170. std::string path(FileUtil::SanitizePath(path_));
  171. auto npath = path;
  172. while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
  173. npath = npath.substr(1);
  174. auto file = backing->GetFileRelative(npath);
  175. if (file == nullptr)
  176. return FileSys::ERROR_PATH_NOT_FOUND;
  177. if (mode == FileSys::Mode::Append) {
  178. return MakeResult<FileSys::VirtualFile>(
  179. std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
  180. }
  181. return MakeResult<FileSys::VirtualFile>(file);
  182. }
  183. ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
  184. std::string path(FileUtil::SanitizePath(path_));
  185. auto dir = GetDirectoryRelativeWrapped(backing, path);
  186. if (dir == nullptr) {
  187. // TODO(DarkLordZach): Find a better error code for this
  188. return FileSys::ERROR_PATH_NOT_FOUND;
  189. }
  190. return MakeResult(dir);
  191. }
  192. u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
  193. if (backing->IsWritable())
  194. return EMULATED_SD_REPORTED_SIZE;
  195. return 0;
  196. }
  197. ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
  198. const std::string& path_) const {
  199. std::string path(FileUtil::SanitizePath(path_));
  200. auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
  201. if (dir == nullptr)
  202. return FileSys::ERROR_PATH_NOT_FOUND;
  203. auto filename = FileUtil::GetFilename(path);
  204. // TODO(Subv): Some games use the '/' path, find out what this means.
  205. if (filename.empty())
  206. return MakeResult(FileSys::EntryType::Directory);
  207. if (dir->GetFile(filename) != nullptr)
  208. return MakeResult(FileSys::EntryType::File);
  209. if (dir->GetSubdirectory(filename) != nullptr)
  210. return MakeResult(FileSys::EntryType::Directory);
  211. return FileSys::ERROR_PATH_NOT_FOUND;
  212. }
  213. /**
  214. * Map of registered file systems, identified by type. Once an file system is registered here, it
  215. * is never removed until UnregisterFileSystems is called.
  216. */
  217. static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
  218. static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
  219. static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
  220. static std::unique_ptr<FileSys::BISFactory> bis_factory;
  221. ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
  222. ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
  223. romfs_factory = std::move(factory);
  224. LOG_DEBUG(Service_FS, "Registered RomFS");
  225. return RESULT_SUCCESS;
  226. }
  227. ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
  228. ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
  229. save_data_factory = std::move(factory);
  230. LOG_DEBUG(Service_FS, "Registered save data");
  231. return RESULT_SUCCESS;
  232. }
  233. ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
  234. ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
  235. sdmc_factory = std::move(factory);
  236. LOG_DEBUG(Service_FS, "Registered SDMC");
  237. return RESULT_SUCCESS;
  238. }
  239. ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
  240. ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
  241. bis_factory = std::move(factory);
  242. LOG_DEBUG(Service_FS, "Registered BIS");
  243. return RESULT_SUCCESS;
  244. }
  245. void SetPackedUpdate(FileSys::VirtualFile update_raw) {
  246. LOG_TRACE(Service_FS, "Setting packed update for romfs");
  247. if (romfs_factory == nullptr)
  248. return;
  249. romfs_factory->SetPackedUpdate(std::move(update_raw));
  250. }
  251. ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
  252. LOG_TRACE(Service_FS, "Opening RomFS for current process");
  253. if (romfs_factory == nullptr) {
  254. // TODO(bunnei): Find a better error code for this
  255. return ResultCode(-1);
  256. }
  257. return romfs_factory->OpenCurrentProcess();
  258. }
  259. ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
  260. FileSys::ContentRecordType type) {
  261. LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
  262. title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
  263. if (romfs_factory == nullptr) {
  264. // TODO(bunnei): Find a better error code for this
  265. return ResultCode(-1);
  266. }
  267. return romfs_factory->Open(title_id, storage_id, type);
  268. }
  269. ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
  270. FileSys::SaveDataDescriptor save_struct) {
  271. LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
  272. static_cast<u8>(space), save_struct.DebugInfo());
  273. if (save_data_factory == nullptr) {
  274. return FileSys::ERROR_ENTITY_NOT_FOUND;
  275. }
  276. return save_data_factory->Open(space, save_struct);
  277. }
  278. ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
  279. LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
  280. if (save_data_factory == nullptr) {
  281. return FileSys::ERROR_ENTITY_NOT_FOUND;
  282. }
  283. return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
  284. }
  285. ResultVal<FileSys::VirtualDir> OpenSDMC() {
  286. LOG_TRACE(Service_FS, "Opening SDMC");
  287. if (sdmc_factory == nullptr) {
  288. return FileSys::ERROR_SD_CARD_NOT_FOUND;
  289. }
  290. return sdmc_factory->Open();
  291. }
  292. FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
  293. if (save_data_factory == nullptr) {
  294. return {0, 0};
  295. }
  296. const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
  297. if (value.normal == 0 && value.journal == 0) {
  298. FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
  299. FileSys::NACP nacp;
  300. const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
  301. if (res != Loader::ResultStatus::Success) {
  302. FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
  303. auto [nacp_unique, discard] = pm.GetControlMetadata();
  304. if (nacp_unique != nullptr) {
  305. new_size = {nacp_unique->GetDefaultNormalSaveSize(),
  306. nacp_unique->GetDefaultJournalSaveSize()};
  307. }
  308. } else {
  309. new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
  310. }
  311. WriteSaveDataSize(type, title_id, user_id, new_size);
  312. return new_size;
  313. }
  314. return value;
  315. }
  316. void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
  317. FileSys::SaveDataSize new_value) {
  318. if (save_data_factory != nullptr)
  319. save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
  320. }
  321. FileSys::RegisteredCacheUnion GetUnionContents() {
  322. return FileSys::RegisteredCacheUnion{
  323. {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
  324. }
  325. FileSys::RegisteredCache* GetSystemNANDContents() {
  326. LOG_TRACE(Service_FS, "Opening System NAND Contents");
  327. if (bis_factory == nullptr)
  328. return nullptr;
  329. return bis_factory->GetSystemNANDContents();
  330. }
  331. FileSys::RegisteredCache* GetUserNANDContents() {
  332. LOG_TRACE(Service_FS, "Opening User NAND Contents");
  333. if (bis_factory == nullptr)
  334. return nullptr;
  335. return bis_factory->GetUserNANDContents();
  336. }
  337. FileSys::RegisteredCache* GetSDMCContents() {
  338. LOG_TRACE(Service_FS, "Opening SDMC Contents");
  339. if (sdmc_factory == nullptr)
  340. return nullptr;
  341. return sdmc_factory->GetSDMCContents();
  342. }
  343. FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
  344. LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
  345. if (bis_factory == nullptr)
  346. return nullptr;
  347. return bis_factory->GetModificationLoadRoot(title_id);
  348. }
  349. FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
  350. LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
  351. if (bis_factory == nullptr)
  352. return nullptr;
  353. return bis_factory->GetModificationDumpRoot(title_id);
  354. }
  355. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
  356. if (overwrite) {
  357. bis_factory = nullptr;
  358. save_data_factory = nullptr;
  359. sdmc_factory = nullptr;
  360. }
  361. auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
  362. FileSys::Mode::ReadWrite);
  363. auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
  364. FileSys::Mode::ReadWrite);
  365. auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
  366. FileSys::Mode::ReadWrite);
  367. auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
  368. FileSys::Mode::ReadWrite);
  369. if (bis_factory == nullptr) {
  370. bis_factory =
  371. std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
  372. }
  373. if (save_data_factory == nullptr) {
  374. save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
  375. }
  376. if (sdmc_factory == nullptr) {
  377. sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
  378. }
  379. }
  380. void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
  381. romfs_factory = nullptr;
  382. CreateFactories(vfs, false);
  383. std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
  384. std::make_shared<FSP_PR>()->InstallAsService(service_manager);
  385. std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
  386. }
  387. } // namespace Service::FileSystem