fsp_srv.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cinttypes>
  5. #include <cstring>
  6. #include <iterator>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include "common/assert.h"
  11. #include "common/common_types.h"
  12. #include "common/hex_util.h"
  13. #include "common/logging/log.h"
  14. #include "common/string_util.h"
  15. #include "core/file_sys/directory.h"
  16. #include "core/file_sys/errors.h"
  17. #include "core/file_sys/mode.h"
  18. #include "core/file_sys/nca_metadata.h"
  19. #include "core/file_sys/patch_manager.h"
  20. #include "core/file_sys/romfs_factory.h"
  21. #include "core/file_sys/savedata_factory.h"
  22. #include "core/file_sys/system_archive/system_archive.h"
  23. #include "core/file_sys/vfs.h"
  24. #include "core/hle/ipc_helpers.h"
  25. #include "core/hle/kernel/process.h"
  26. #include "core/hle/service/filesystem/filesystem.h"
  27. #include "core/hle/service/filesystem/fsp_srv.h"
  28. #include "core/reporter.h"
  29. namespace Service::FileSystem {
  30. struct SizeGetter {
  31. std::function<u64()> get_free_size;
  32. std::function<u64()> get_total_size;
  33. static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
  34. return {
  35. [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
  36. [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
  37. };
  38. }
  39. };
  40. enum class FileSystemType : u8 {
  41. Invalid0 = 0,
  42. Invalid1 = 1,
  43. Logo = 2,
  44. ContentControl = 3,
  45. ContentManual = 4,
  46. ContentMeta = 5,
  47. ContentData = 6,
  48. ApplicationPackage = 7,
  49. };
  50. class IStorage final : public ServiceFramework<IStorage> {
  51. public:
  52. explicit IStorage(FileSys::VirtualFile backend_)
  53. : ServiceFramework("IStorage"), backend(std::move(backend_)) {
  54. static const FunctionInfo functions[] = {
  55. {0, &IStorage::Read, "Read"},
  56. {1, nullptr, "Write"},
  57. {2, nullptr, "Flush"},
  58. {3, nullptr, "SetSize"},
  59. {4, &IStorage::GetSize, "GetSize"},
  60. {5, nullptr, "OperateRange"},
  61. };
  62. RegisterHandlers(functions);
  63. }
  64. private:
  65. FileSys::VirtualFile backend;
  66. void Read(Kernel::HLERequestContext& ctx) {
  67. IPC::RequestParser rp{ctx};
  68. const s64 offset = rp.Pop<s64>();
  69. const s64 length = rp.Pop<s64>();
  70. LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
  71. // Error checking
  72. if (length < 0) {
  73. LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
  74. IPC::ResponseBuilder rb{ctx, 2};
  75. rb.Push(FileSys::ERROR_INVALID_SIZE);
  76. return;
  77. }
  78. if (offset < 0) {
  79. LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
  80. IPC::ResponseBuilder rb{ctx, 2};
  81. rb.Push(FileSys::ERROR_INVALID_OFFSET);
  82. return;
  83. }
  84. // Read the data from the Storage backend
  85. std::vector<u8> output = backend->ReadBytes(length, offset);
  86. // Write the data to memory
  87. ctx.WriteBuffer(output);
  88. IPC::ResponseBuilder rb{ctx, 2};
  89. rb.Push(RESULT_SUCCESS);
  90. }
  91. void GetSize(Kernel::HLERequestContext& ctx) {
  92. const u64 size = backend->GetSize();
  93. LOG_DEBUG(Service_FS, "called, size={}", size);
  94. IPC::ResponseBuilder rb{ctx, 4};
  95. rb.Push(RESULT_SUCCESS);
  96. rb.Push<u64>(size);
  97. }
  98. };
  99. class IFile final : public ServiceFramework<IFile> {
  100. public:
  101. explicit IFile(FileSys::VirtualFile backend_)
  102. : ServiceFramework("IFile"), backend(std::move(backend_)) {
  103. static const FunctionInfo functions[] = {
  104. {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
  105. {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
  106. {4, &IFile::GetSize, "GetSize"}, {5, nullptr, "OperateRange"},
  107. };
  108. RegisterHandlers(functions);
  109. }
  110. private:
  111. FileSys::VirtualFile backend;
  112. void Read(Kernel::HLERequestContext& ctx) {
  113. IPC::RequestParser rp{ctx};
  114. const u64 option = rp.Pop<u64>();
  115. const s64 offset = rp.Pop<s64>();
  116. const s64 length = rp.Pop<s64>();
  117. LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
  118. length);
  119. // Error checking
  120. if (length < 0) {
  121. LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
  122. IPC::ResponseBuilder rb{ctx, 2};
  123. rb.Push(FileSys::ERROR_INVALID_SIZE);
  124. return;
  125. }
  126. if (offset < 0) {
  127. LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
  128. IPC::ResponseBuilder rb{ctx, 2};
  129. rb.Push(FileSys::ERROR_INVALID_OFFSET);
  130. return;
  131. }
  132. // Read the data from the Storage backend
  133. std::vector<u8> output = backend->ReadBytes(length, offset);
  134. // Write the data to memory
  135. ctx.WriteBuffer(output);
  136. IPC::ResponseBuilder rb{ctx, 4};
  137. rb.Push(RESULT_SUCCESS);
  138. rb.Push(static_cast<u64>(output.size()));
  139. }
  140. void Write(Kernel::HLERequestContext& ctx) {
  141. IPC::RequestParser rp{ctx};
  142. const u64 option = rp.Pop<u64>();
  143. const s64 offset = rp.Pop<s64>();
  144. const s64 length = rp.Pop<s64>();
  145. LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
  146. length);
  147. // Error checking
  148. if (length < 0) {
  149. LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
  150. IPC::ResponseBuilder rb{ctx, 2};
  151. rb.Push(FileSys::ERROR_INVALID_SIZE);
  152. return;
  153. }
  154. if (offset < 0) {
  155. LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
  156. IPC::ResponseBuilder rb{ctx, 2};
  157. rb.Push(FileSys::ERROR_INVALID_OFFSET);
  158. return;
  159. }
  160. const std::vector<u8> data = ctx.ReadBuffer();
  161. ASSERT_MSG(
  162. static_cast<s64>(data.size()) <= length,
  163. "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
  164. length, data.size());
  165. // Write the data to the Storage backend
  166. const auto write_size =
  167. static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
  168. const std::size_t written = backend->Write(data.data(), write_size, offset);
  169. ASSERT_MSG(static_cast<s64>(written) == length,
  170. "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
  171. written);
  172. IPC::ResponseBuilder rb{ctx, 2};
  173. rb.Push(RESULT_SUCCESS);
  174. }
  175. void Flush(Kernel::HLERequestContext& ctx) {
  176. LOG_DEBUG(Service_FS, "called");
  177. // Exists for SDK compatibiltity -- No need to flush file.
  178. IPC::ResponseBuilder rb{ctx, 2};
  179. rb.Push(RESULT_SUCCESS);
  180. }
  181. void SetSize(Kernel::HLERequestContext& ctx) {
  182. IPC::RequestParser rp{ctx};
  183. const u64 size = rp.Pop<u64>();
  184. LOG_DEBUG(Service_FS, "called, size={}", size);
  185. backend->Resize(size);
  186. IPC::ResponseBuilder rb{ctx, 2};
  187. rb.Push(RESULT_SUCCESS);
  188. }
  189. void GetSize(Kernel::HLERequestContext& ctx) {
  190. const u64 size = backend->GetSize();
  191. LOG_DEBUG(Service_FS, "called, size={}", size);
  192. IPC::ResponseBuilder rb{ctx, 4};
  193. rb.Push(RESULT_SUCCESS);
  194. rb.Push<u64>(size);
  195. }
  196. };
  197. template <typename T>
  198. static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
  199. FileSys::EntryType type) {
  200. entries.reserve(entries.size() + new_data.size());
  201. for (const auto& new_entry : new_data) {
  202. entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
  203. }
  204. }
  205. class IDirectory final : public ServiceFramework<IDirectory> {
  206. public:
  207. explicit IDirectory(FileSys::VirtualDir backend_)
  208. : ServiceFramework("IDirectory"), backend(std::move(backend_)) {
  209. static const FunctionInfo functions[] = {
  210. {0, &IDirectory::Read, "Read"},
  211. {1, &IDirectory::GetEntryCount, "GetEntryCount"},
  212. };
  213. RegisterHandlers(functions);
  214. // TODO(DarkLordZach): Verify that this is the correct behavior.
  215. // Build entry index now to save time later.
  216. BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
  217. BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
  218. }
  219. private:
  220. FileSys::VirtualDir backend;
  221. std::vector<FileSys::Entry> entries;
  222. u64 next_entry_index = 0;
  223. void Read(Kernel::HLERequestContext& ctx) {
  224. LOG_DEBUG(Service_FS, "called.");
  225. // Calculate how many entries we can fit in the output buffer
  226. const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
  227. // Cap at total number of entries.
  228. const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
  229. // Determine data start and end
  230. const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
  231. const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
  232. const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
  233. next_entry_index += actual_entries;
  234. // Write the data to memory
  235. ctx.WriteBuffer(begin, range_size);
  236. IPC::ResponseBuilder rb{ctx, 4};
  237. rb.Push(RESULT_SUCCESS);
  238. rb.Push(actual_entries);
  239. }
  240. void GetEntryCount(Kernel::HLERequestContext& ctx) {
  241. LOG_DEBUG(Service_FS, "called");
  242. u64 count = entries.size() - next_entry_index;
  243. IPC::ResponseBuilder rb{ctx, 4};
  244. rb.Push(RESULT_SUCCESS);
  245. rb.Push(count);
  246. }
  247. };
  248. class IFileSystem final : public ServiceFramework<IFileSystem> {
  249. public:
  250. explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size)
  251. : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) {
  252. static const FunctionInfo functions[] = {
  253. {0, &IFileSystem::CreateFile, "CreateFile"},
  254. {1, &IFileSystem::DeleteFile, "DeleteFile"},
  255. {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
  256. {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
  257. {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
  258. {5, &IFileSystem::RenameFile, "RenameFile"},
  259. {6, nullptr, "RenameDirectory"},
  260. {7, &IFileSystem::GetEntryType, "GetEntryType"},
  261. {8, &IFileSystem::OpenFile, "OpenFile"},
  262. {9, &IFileSystem::OpenDirectory, "OpenDirectory"},
  263. {10, &IFileSystem::Commit, "Commit"},
  264. {11, nullptr, "GetFreeSpaceSize"},
  265. {12, nullptr, "GetTotalSpaceSize"},
  266. {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
  267. {14, nullptr, "GetFileTimeStampRaw"},
  268. {15, nullptr, "QueryEntry"},
  269. };
  270. RegisterHandlers(functions);
  271. }
  272. void CreateFile(Kernel::HLERequestContext& ctx) {
  273. IPC::RequestParser rp{ctx};
  274. const auto file_buffer = ctx.ReadBuffer();
  275. const std::string name = Common::StringFromBuffer(file_buffer);
  276. const u64 mode = rp.Pop<u64>();
  277. const u32 size = rp.Pop<u32>();
  278. LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, mode, size);
  279. IPC::ResponseBuilder rb{ctx, 2};
  280. rb.Push(backend.CreateFile(name, size));
  281. }
  282. void DeleteFile(Kernel::HLERequestContext& ctx) {
  283. const auto file_buffer = ctx.ReadBuffer();
  284. const std::string name = Common::StringFromBuffer(file_buffer);
  285. LOG_DEBUG(Service_FS, "called. file={}", name);
  286. IPC::ResponseBuilder rb{ctx, 2};
  287. rb.Push(backend.DeleteFile(name));
  288. }
  289. void CreateDirectory(Kernel::HLERequestContext& ctx) {
  290. const auto file_buffer = ctx.ReadBuffer();
  291. const std::string name = Common::StringFromBuffer(file_buffer);
  292. LOG_DEBUG(Service_FS, "called. directory={}", name);
  293. IPC::ResponseBuilder rb{ctx, 2};
  294. rb.Push(backend.CreateDirectory(name));
  295. }
  296. void DeleteDirectory(Kernel::HLERequestContext& ctx) {
  297. const auto file_buffer = ctx.ReadBuffer();
  298. const std::string name = Common::StringFromBuffer(file_buffer);
  299. LOG_DEBUG(Service_FS, "called. directory={}", name);
  300. IPC::ResponseBuilder rb{ctx, 2};
  301. rb.Push(backend.DeleteDirectory(name));
  302. }
  303. void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
  304. const auto file_buffer = ctx.ReadBuffer();
  305. const std::string name = Common::StringFromBuffer(file_buffer);
  306. LOG_DEBUG(Service_FS, "called. directory={}", name);
  307. IPC::ResponseBuilder rb{ctx, 2};
  308. rb.Push(backend.DeleteDirectoryRecursively(name));
  309. }
  310. void CleanDirectoryRecursively(Kernel::HLERequestContext& ctx) {
  311. const auto file_buffer = ctx.ReadBuffer();
  312. const std::string name = Common::StringFromBuffer(file_buffer);
  313. LOG_DEBUG(Service_FS, "called. Directory: {}", name);
  314. IPC::ResponseBuilder rb{ctx, 2};
  315. rb.Push(backend.CleanDirectoryRecursively(name));
  316. }
  317. void RenameFile(Kernel::HLERequestContext& ctx) {
  318. std::vector<u8> buffer;
  319. buffer.resize(ctx.BufferDescriptorX()[0].Size());
  320. Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
  321. const std::string src_name = Common::StringFromBuffer(buffer);
  322. buffer.resize(ctx.BufferDescriptorX()[1].Size());
  323. Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
  324. const std::string dst_name = Common::StringFromBuffer(buffer);
  325. LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
  326. IPC::ResponseBuilder rb{ctx, 2};
  327. rb.Push(backend.RenameFile(src_name, dst_name));
  328. }
  329. void OpenFile(Kernel::HLERequestContext& ctx) {
  330. IPC::RequestParser rp{ctx};
  331. const auto file_buffer = ctx.ReadBuffer();
  332. const std::string name = Common::StringFromBuffer(file_buffer);
  333. const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
  334. LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode));
  335. auto result = backend.OpenFile(name, mode);
  336. if (result.Failed()) {
  337. IPC::ResponseBuilder rb{ctx, 2};
  338. rb.Push(result.Code());
  339. return;
  340. }
  341. IFile file(result.Unwrap());
  342. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  343. rb.Push(RESULT_SUCCESS);
  344. rb.PushIpcInterface<IFile>(std::move(file));
  345. }
  346. void OpenDirectory(Kernel::HLERequestContext& ctx) {
  347. IPC::RequestParser rp{ctx};
  348. const auto file_buffer = ctx.ReadBuffer();
  349. const std::string name = Common::StringFromBuffer(file_buffer);
  350. // TODO(Subv): Implement this filter.
  351. const u32 filter_flags = rp.Pop<u32>();
  352. LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
  353. auto result = backend.OpenDirectory(name);
  354. if (result.Failed()) {
  355. IPC::ResponseBuilder rb{ctx, 2};
  356. rb.Push(result.Code());
  357. return;
  358. }
  359. IDirectory directory(result.Unwrap());
  360. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  361. rb.Push(RESULT_SUCCESS);
  362. rb.PushIpcInterface<IDirectory>(std::move(directory));
  363. }
  364. void GetEntryType(Kernel::HLERequestContext& ctx) {
  365. const auto file_buffer = ctx.ReadBuffer();
  366. const std::string name = Common::StringFromBuffer(file_buffer);
  367. LOG_DEBUG(Service_FS, "called. file={}", name);
  368. auto result = backend.GetEntryType(name);
  369. if (result.Failed()) {
  370. IPC::ResponseBuilder rb{ctx, 2};
  371. rb.Push(result.Code());
  372. return;
  373. }
  374. IPC::ResponseBuilder rb{ctx, 3};
  375. rb.Push(RESULT_SUCCESS);
  376. rb.Push<u32>(static_cast<u32>(*result));
  377. }
  378. void Commit(Kernel::HLERequestContext& ctx) {
  379. LOG_WARNING(Service_FS, "(STUBBED) called");
  380. IPC::ResponseBuilder rb{ctx, 2};
  381. rb.Push(RESULT_SUCCESS);
  382. }
  383. void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
  384. LOG_DEBUG(Service_FS, "called");
  385. IPC::ResponseBuilder rb{ctx, 4};
  386. rb.Push(RESULT_SUCCESS);
  387. rb.Push(size.get_free_size());
  388. }
  389. void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
  390. LOG_DEBUG(Service_FS, "called");
  391. IPC::ResponseBuilder rb{ctx, 4};
  392. rb.Push(RESULT_SUCCESS);
  393. rb.Push(size.get_total_size());
  394. }
  395. private:
  396. VfsDirectoryServiceWrapper backend;
  397. SizeGetter size;
  398. };
  399. class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
  400. public:
  401. explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
  402. : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
  403. static const FunctionInfo functions[] = {
  404. {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
  405. };
  406. RegisterHandlers(functions);
  407. FindAllSaves(space);
  408. }
  409. void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
  410. LOG_DEBUG(Service_FS, "called");
  411. // Calculate how many entries we can fit in the output buffer
  412. const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
  413. // Cap at total number of entries.
  414. const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
  415. // Determine data start and end
  416. const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
  417. const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
  418. const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
  419. next_entry_index += actual_entries;
  420. // Write the data to memory
  421. ctx.WriteBuffer(begin, range_size);
  422. IPC::ResponseBuilder rb{ctx, 3};
  423. rb.Push(RESULT_SUCCESS);
  424. rb.Push<u32>(static_cast<u32>(actual_entries));
  425. }
  426. private:
  427. static u64 stoull_be(std::string_view str) {
  428. if (str.size() != 16)
  429. return 0;
  430. const auto bytes = Common::HexStringToArray<0x8>(str);
  431. u64 out{};
  432. std::memcpy(&out, bytes.data(), sizeof(u64));
  433. return Common::swap64(out);
  434. }
  435. void FindAllSaves(FileSys::SaveDataSpaceId space) {
  436. const auto save_root = fsc.OpenSaveDataSpace(space);
  437. if (save_root.Failed() || *save_root == nullptr) {
  438. LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
  439. static_cast<u8>(space));
  440. return;
  441. }
  442. for (const auto& type : (*save_root)->GetSubdirectories()) {
  443. if (type->GetName() == "save") {
  444. for (const auto& save_id : type->GetSubdirectories()) {
  445. for (const auto& user_id : save_id->GetSubdirectories()) {
  446. const auto save_id_numeric = stoull_be(save_id->GetName());
  447. auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
  448. std::reverse(user_id_numeric.begin(), user_id_numeric.end());
  449. if (save_id_numeric != 0) {
  450. // System Save Data
  451. info.emplace_back(SaveDataInfo{
  452. 0,
  453. space,
  454. FileSys::SaveDataType::SystemSaveData,
  455. {},
  456. user_id_numeric,
  457. save_id_numeric,
  458. 0,
  459. user_id->GetSize(),
  460. {},
  461. });
  462. continue;
  463. }
  464. for (const auto& title_id : user_id->GetSubdirectories()) {
  465. const auto device =
  466. std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
  467. [](u8 val) { return val == 0; });
  468. info.emplace_back(SaveDataInfo{
  469. 0,
  470. space,
  471. device ? FileSys::SaveDataType::DeviceSaveData
  472. : FileSys::SaveDataType::SaveData,
  473. {},
  474. user_id_numeric,
  475. save_id_numeric,
  476. stoull_be(title_id->GetName()),
  477. title_id->GetSize(),
  478. {},
  479. });
  480. }
  481. }
  482. }
  483. } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
  484. // Temporary Storage
  485. for (const auto& user_id : type->GetSubdirectories()) {
  486. for (const auto& title_id : user_id->GetSubdirectories()) {
  487. if (!title_id->GetFiles().empty() ||
  488. !title_id->GetSubdirectories().empty()) {
  489. auto user_id_numeric =
  490. Common::HexStringToArray<0x10>(user_id->GetName());
  491. std::reverse(user_id_numeric.begin(), user_id_numeric.end());
  492. info.emplace_back(SaveDataInfo{
  493. 0,
  494. space,
  495. FileSys::SaveDataType::TemporaryStorage,
  496. {},
  497. user_id_numeric,
  498. stoull_be(type->GetName()),
  499. stoull_be(title_id->GetName()),
  500. title_id->GetSize(),
  501. {},
  502. });
  503. }
  504. }
  505. }
  506. }
  507. }
  508. }
  509. struct SaveDataInfo {
  510. u64_le save_id_unknown;
  511. FileSys::SaveDataSpaceId space;
  512. FileSys::SaveDataType type;
  513. INSERT_PADDING_BYTES(0x6);
  514. std::array<u8, 0x10> user_id;
  515. u64_le save_id;
  516. u64_le title_id;
  517. u64_le save_image_size;
  518. u16_le index;
  519. FileSys::SaveDataRank rank;
  520. INSERT_PADDING_BYTES(0x25);
  521. };
  522. static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
  523. FileSystemController& fsc;
  524. std::vector<SaveDataInfo> info;
  525. u64 next_entry_index = 0;
  526. };
  527. FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
  528. : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
  529. // clang-format off
  530. static const FunctionInfo functions[] = {
  531. {0, nullptr, "OpenFileSystem"},
  532. {1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"},
  533. {2, nullptr, "OpenDataFileSystemByCurrentProcess"},
  534. {7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
  535. {8, nullptr, "OpenFileSystemWithId"},
  536. {9, nullptr, "OpenDataFileSystemByApplicationId"},
  537. {11, nullptr, "OpenBisFileSystem"},
  538. {12, nullptr, "OpenBisStorage"},
  539. {13, nullptr, "InvalidateBisCache"},
  540. {17, nullptr, "OpenHostFileSystem"},
  541. {18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"},
  542. {19, nullptr, "FormatSdCardFileSystem"},
  543. {21, nullptr, "DeleteSaveDataFileSystem"},
  544. {22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
  545. {23, nullptr, "CreateSaveDataFileSystemBySystemSaveDataId"},
  546. {24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
  547. {25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
  548. {26, nullptr, "FormatSdCardDryRun"},
  549. {27, nullptr, "IsExFatSupported"},
  550. {28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"},
  551. {30, nullptr, "OpenGameCardStorage"},
  552. {31, nullptr, "OpenGameCardFileSystem"},
  553. {32, nullptr, "ExtendSaveDataFileSystem"},
  554. {33, nullptr, "DeleteCacheStorage"},
  555. {34, nullptr, "GetCacheStorageSize"},
  556. {35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
  557. {36, nullptr, "OpenHostFileSystemWithOption"},
  558. {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
  559. {52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
  560. {53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
  561. {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
  562. {58, nullptr, "ReadSaveDataFileSystemExtraData"},
  563. {59, nullptr, "WriteSaveDataFileSystemExtraData"},
  564. {60, nullptr, "OpenSaveDataInfoReader"},
  565. {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
  566. {62, nullptr, "OpenCacheStorageList"},
  567. {64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
  568. {65, nullptr, "UpdateSaveDataMacForDebug"},
  569. {66, nullptr, "WriteSaveDataFileSystemExtraData2"},
  570. {67, nullptr, "FindSaveDataWithFilter"},
  571. {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
  572. {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
  573. {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
  574. {80, nullptr, "OpenSaveDataMetaFile"},
  575. {81, nullptr, "OpenSaveDataTransferManager"},
  576. {82, nullptr, "OpenSaveDataTransferManagerVersion2"},
  577. {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
  578. {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
  579. {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"},
  580. {100, nullptr, "OpenImageDirectoryFileSystem"},
  581. {110, nullptr, "OpenContentStorageFileSystem"},
  582. {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
  583. {130, nullptr, "OpenCustomStorageFileSystem"},
  584. {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
  585. {201, nullptr, "OpenDataStorageByProgramId"},
  586. {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
  587. {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
  588. {204, nullptr, "OpenDataFileSystemByProgramIndex"},
  589. {205, nullptr, "OpenDataStorageByProgramIndex"},
  590. {400, nullptr, "OpenDeviceOperator"},
  591. {500, nullptr, "OpenSdCardDetectionEventNotifier"},
  592. {501, nullptr, "OpenGameCardDetectionEventNotifier"},
  593. {510, nullptr, "OpenSystemDataUpdateEventNotifier"},
  594. {511, nullptr, "NotifySystemDataUpdateEvent"},
  595. {520, nullptr, "SimulateGameCardDetectionEvent"},
  596. {600, nullptr, "SetCurrentPosixTime"},
  597. {601, nullptr, "QuerySaveDataTotalSize"},
  598. {602, nullptr, "VerifySaveDataFileSystem"},
  599. {603, nullptr, "CorruptSaveDataFileSystem"},
  600. {604, nullptr, "CreatePaddingFile"},
  601. {605, nullptr, "DeleteAllPaddingFiles"},
  602. {606, nullptr, "GetRightsId"},
  603. {607, nullptr, "RegisterExternalKey"},
  604. {608, nullptr, "UnregisterAllExternalKey"},
  605. {609, nullptr, "GetRightsIdByPath"},
  606. {610, nullptr, "GetRightsIdAndKeyGenerationByPath"},
  607. {611, nullptr, "SetCurrentPosixTimeWithTimeDifference"},
  608. {612, nullptr, "GetFreeSpaceSizeForSaveData"},
  609. {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"},
  610. {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"},
  611. {615, nullptr, "QuerySaveDataInternalStorageTotalSize"},
  612. {616, nullptr, "GetSaveDataCommitId"},
  613. {617, nullptr, "UnregisterExternalKey"},
  614. {620, nullptr, "SetSdCardEncryptionSeed"},
  615. {630, nullptr, "SetSdCardAccessibility"},
  616. {631, nullptr, "IsSdCardAccessible"},
  617. {640, nullptr, "IsSignedSystemPartitionOnSdCardValid"},
  618. {700, nullptr, "OpenAccessFailureResolver"},
  619. {701, nullptr, "GetAccessFailureDetectionEvent"},
  620. {702, nullptr, "IsAccessFailureDetected"},
  621. {710, nullptr, "ResolveAccessFailure"},
  622. {720, nullptr, "AbandonAccessFailure"},
  623. {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
  624. {810, nullptr, "RegisterProgramIndexMapInfo"},
  625. {1000, nullptr, "SetBisRootForHost"},
  626. {1001, nullptr, "SetSaveDataSize"},
  627. {1002, nullptr, "SetSaveDataRootPath"},
  628. {1003, nullptr, "DisableAutoSaveDataCreation"},
  629. {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"},
  630. {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"},
  631. {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"},
  632. {1007, nullptr, "RegisterUpdatePartition"},
  633. {1008, nullptr, "OpenRegisteredUpdatePartition"},
  634. {1009, nullptr, "GetAndClearMemoryReportInfo"},
  635. {1010, nullptr, "SetDataStorageRedirectTarget"},
  636. {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"},
  637. {1012, nullptr, "GetFsStackUsage"},
  638. {1013, nullptr, "UnsetSaveDataRootPath"},
  639. {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
  640. {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
  641. {1200, nullptr, "OpenMultiCommitManager"},
  642. };
  643. // clang-format on
  644. RegisterHandlers(functions);
  645. }
  646. FSP_SRV::~FSP_SRV() = default;
  647. void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
  648. IPC::RequestParser rp{ctx};
  649. current_process_id = rp.Pop<u64>();
  650. LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
  651. IPC::ResponseBuilder rb{ctx, 2};
  652. rb.Push(RESULT_SUCCESS);
  653. }
  654. void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
  655. IPC::RequestParser rp{ctx};
  656. const auto type = rp.PopRaw<FileSystemType>();
  657. const auto title_id = rp.PopRaw<u64>();
  658. LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}",
  659. static_cast<u8>(type), title_id);
  660. IPC::ResponseBuilder rb{ctx, 2, 0, 0};
  661. rb.Push(ResultCode(-1));
  662. }
  663. void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
  664. LOG_DEBUG(Service_FS, "called");
  665. IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
  666. SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
  667. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  668. rb.Push(RESULT_SUCCESS);
  669. rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
  670. }
  671. void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
  672. IPC::RequestParser rp{ctx};
  673. auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
  674. [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
  675. u128 uid = rp.PopRaw<u128>();
  676. LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
  677. uid[1], uid[0]);
  678. fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
  679. IPC::ResponseBuilder rb{ctx, 2};
  680. rb.Push(RESULT_SUCCESS);
  681. }
  682. void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
  683. LOG_INFO(Service_FS, "called.");
  684. struct Parameters {
  685. FileSys::SaveDataSpaceId save_data_space_id;
  686. FileSys::SaveDataDescriptor descriptor;
  687. };
  688. IPC::RequestParser rp{ctx};
  689. const auto parameters = rp.PopRaw<Parameters>();
  690. auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
  691. if (dir.Failed()) {
  692. IPC::ResponseBuilder rb{ctx, 2, 0, 0};
  693. rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
  694. return;
  695. }
  696. FileSys::StorageId id;
  697. if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
  698. id = FileSys::StorageId::NandUser;
  699. } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
  700. parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
  701. id = FileSys::StorageId::SdCard;
  702. } else {
  703. id = FileSys::StorageId::NandSystem;
  704. }
  705. IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
  706. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  707. rb.Push(RESULT_SUCCESS);
  708. rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
  709. }
  710. void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
  711. LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
  712. OpenSaveDataFileSystem(ctx);
  713. }
  714. void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
  715. IPC::RequestParser rp{ctx};
  716. const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
  717. LOG_INFO(Service_FS, "called, space={}", static_cast<u8>(space));
  718. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  719. rb.Push(RESULT_SUCCESS);
  720. rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
  721. }
  722. void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
  723. IPC::RequestParser rp{ctx};
  724. log_mode = rp.PopEnum<LogMode>();
  725. LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
  726. IPC::ResponseBuilder rb{ctx, 2};
  727. rb.Push(RESULT_SUCCESS);
  728. }
  729. void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
  730. LOG_DEBUG(Service_FS, "called");
  731. IPC::ResponseBuilder rb{ctx, 3};
  732. rb.Push(RESULT_SUCCESS);
  733. rb.PushEnum(log_mode);
  734. }
  735. void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
  736. LOG_DEBUG(Service_FS, "called");
  737. auto romfs = fsc.OpenRomFSCurrentProcess();
  738. if (romfs.Failed()) {
  739. // TODO (bunnei): Find the right error code to use here
  740. LOG_CRITICAL(Service_FS, "no file system interface available!");
  741. IPC::ResponseBuilder rb{ctx, 2};
  742. rb.Push(ResultCode(-1));
  743. return;
  744. }
  745. IStorage storage(std::move(romfs.Unwrap()));
  746. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  747. rb.Push(RESULT_SUCCESS);
  748. rb.PushIpcInterface<IStorage>(std::move(storage));
  749. }
  750. void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
  751. IPC::RequestParser rp{ctx};
  752. const auto storage_id = rp.PopRaw<FileSys::StorageId>();
  753. const auto unknown = rp.PopRaw<u32>();
  754. const auto title_id = rp.PopRaw<u64>();
  755. LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
  756. static_cast<u8>(storage_id), unknown, title_id);
  757. auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
  758. if (data.Failed()) {
  759. const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
  760. if (archive != nullptr) {
  761. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  762. rb.Push(RESULT_SUCCESS);
  763. rb.PushIpcInterface(std::make_shared<IStorage>(archive));
  764. return;
  765. }
  766. // TODO(DarkLordZach): Find the right error code to use here
  767. LOG_ERROR(Service_FS,
  768. "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
  769. static_cast<u8>(storage_id));
  770. IPC::ResponseBuilder rb{ctx, 2};
  771. rb.Push(ResultCode(-1));
  772. return;
  773. }
  774. FileSys::PatchManager pm{title_id};
  775. IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
  776. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  777. rb.Push(RESULT_SUCCESS);
  778. rb.PushIpcInterface<IStorage>(std::move(storage));
  779. }
  780. void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
  781. IPC::RequestParser rp{ctx};
  782. auto storage_id = rp.PopRaw<FileSys::StorageId>();
  783. auto title_id = rp.PopRaw<u64>();
  784. LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
  785. static_cast<u8>(storage_id), title_id);
  786. IPC::ResponseBuilder rb{ctx, 2};
  787. rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
  788. }
  789. void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
  790. const auto raw = ctx.ReadBuffer();
  791. auto log = Common::StringFromFixedZeroTerminatedBuffer(
  792. reinterpret_cast<const char*>(raw.data()), raw.size());
  793. LOG_DEBUG(Service_FS, "called, log='{}'", log);
  794. reporter.SaveFilesystemAccessReport(log_mode, std::move(log));
  795. IPC::ResponseBuilder rb{ctx, 2};
  796. rb.Push(RESULT_SUCCESS);
  797. }
  798. void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
  799. LOG_DEBUG(Service_FS, "called");
  800. IPC::ResponseBuilder rb{ctx, 4};
  801. rb.Push(RESULT_SUCCESS);
  802. rb.PushEnum(AccessLogVersion::Latest);
  803. rb.Push(access_log_program_index);
  804. }
  805. } // namespace Service::FileSystem