fsp_srv.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  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::EntryType::File);
  217. BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::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, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
  265. {12, &IFileSystem::GetTotalSpaceSize, "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 = ctx.ReadBuffer(0);
  319. const std::string src_name = Common::StringFromBuffer(buffer);
  320. buffer = ctx.ReadBuffer(1);
  321. const std::string dst_name = Common::StringFromBuffer(buffer);
  322. LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
  323. IPC::ResponseBuilder rb{ctx, 2};
  324. rb.Push(backend.RenameFile(src_name, dst_name));
  325. }
  326. void OpenFile(Kernel::HLERequestContext& ctx) {
  327. IPC::RequestParser rp{ctx};
  328. const auto file_buffer = ctx.ReadBuffer();
  329. const std::string name = Common::StringFromBuffer(file_buffer);
  330. const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
  331. LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode));
  332. auto result = backend.OpenFile(name, mode);
  333. if (result.Failed()) {
  334. IPC::ResponseBuilder rb{ctx, 2};
  335. rb.Push(result.Code());
  336. return;
  337. }
  338. auto file = std::make_shared<IFile>(result.Unwrap());
  339. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  340. rb.Push(RESULT_SUCCESS);
  341. rb.PushIpcInterface<IFile>(std::move(file));
  342. }
  343. void OpenDirectory(Kernel::HLERequestContext& ctx) {
  344. IPC::RequestParser rp{ctx};
  345. const auto file_buffer = ctx.ReadBuffer();
  346. const std::string name = Common::StringFromBuffer(file_buffer);
  347. // TODO(Subv): Implement this filter.
  348. const u32 filter_flags = rp.Pop<u32>();
  349. LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
  350. auto result = backend.OpenDirectory(name);
  351. if (result.Failed()) {
  352. IPC::ResponseBuilder rb{ctx, 2};
  353. rb.Push(result.Code());
  354. return;
  355. }
  356. auto directory = std::make_shared<IDirectory>(result.Unwrap());
  357. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  358. rb.Push(RESULT_SUCCESS);
  359. rb.PushIpcInterface<IDirectory>(std::move(directory));
  360. }
  361. void GetEntryType(Kernel::HLERequestContext& ctx) {
  362. const auto file_buffer = ctx.ReadBuffer();
  363. const std::string name = Common::StringFromBuffer(file_buffer);
  364. LOG_DEBUG(Service_FS, "called. file={}", name);
  365. auto result = backend.GetEntryType(name);
  366. if (result.Failed()) {
  367. IPC::ResponseBuilder rb{ctx, 2};
  368. rb.Push(result.Code());
  369. return;
  370. }
  371. IPC::ResponseBuilder rb{ctx, 3};
  372. rb.Push(RESULT_SUCCESS);
  373. rb.Push<u32>(static_cast<u32>(*result));
  374. }
  375. void Commit(Kernel::HLERequestContext& ctx) {
  376. LOG_WARNING(Service_FS, "(STUBBED) called");
  377. IPC::ResponseBuilder rb{ctx, 2};
  378. rb.Push(RESULT_SUCCESS);
  379. }
  380. void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
  381. LOG_DEBUG(Service_FS, "called");
  382. IPC::ResponseBuilder rb{ctx, 4};
  383. rb.Push(RESULT_SUCCESS);
  384. rb.Push(size.get_free_size());
  385. }
  386. void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
  387. LOG_DEBUG(Service_FS, "called");
  388. IPC::ResponseBuilder rb{ctx, 4};
  389. rb.Push(RESULT_SUCCESS);
  390. rb.Push(size.get_total_size());
  391. }
  392. private:
  393. VfsDirectoryServiceWrapper backend;
  394. SizeGetter size;
  395. };
  396. class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
  397. public:
  398. explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
  399. : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
  400. static const FunctionInfo functions[] = {
  401. {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
  402. };
  403. RegisterHandlers(functions);
  404. FindAllSaves(space);
  405. }
  406. void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
  407. LOG_DEBUG(Service_FS, "called");
  408. // Calculate how many entries we can fit in the output buffer
  409. const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
  410. // Cap at total number of entries.
  411. const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
  412. // Determine data start and end
  413. const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
  414. const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
  415. const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
  416. next_entry_index += actual_entries;
  417. // Write the data to memory
  418. ctx.WriteBuffer(begin, range_size);
  419. IPC::ResponseBuilder rb{ctx, 3};
  420. rb.Push(RESULT_SUCCESS);
  421. rb.Push<u32>(static_cast<u32>(actual_entries));
  422. }
  423. private:
  424. static u64 stoull_be(std::string_view str) {
  425. if (str.size() != 16)
  426. return 0;
  427. const auto bytes = Common::HexStringToArray<0x8>(str);
  428. u64 out{};
  429. std::memcpy(&out, bytes.data(), sizeof(u64));
  430. return Common::swap64(out);
  431. }
  432. void FindAllSaves(FileSys::SaveDataSpaceId space) {
  433. const auto save_root = fsc.OpenSaveDataSpace(space);
  434. if (save_root.Failed() || *save_root == nullptr) {
  435. LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
  436. static_cast<u8>(space));
  437. return;
  438. }
  439. for (const auto& type : (*save_root)->GetSubdirectories()) {
  440. if (type->GetName() == "save") {
  441. for (const auto& save_id : type->GetSubdirectories()) {
  442. for (const auto& user_id : save_id->GetSubdirectories()) {
  443. const auto save_id_numeric = stoull_be(save_id->GetName());
  444. auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
  445. std::reverse(user_id_numeric.begin(), user_id_numeric.end());
  446. if (save_id_numeric != 0) {
  447. // System Save Data
  448. info.emplace_back(SaveDataInfo{
  449. 0,
  450. space,
  451. FileSys::SaveDataType::SystemSaveData,
  452. {},
  453. user_id_numeric,
  454. save_id_numeric,
  455. 0,
  456. user_id->GetSize(),
  457. {},
  458. {},
  459. });
  460. continue;
  461. }
  462. for (const auto& title_id : user_id->GetSubdirectories()) {
  463. const auto device =
  464. std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
  465. [](u8 val) { return val == 0; });
  466. info.emplace_back(SaveDataInfo{
  467. 0,
  468. space,
  469. device ? FileSys::SaveDataType::DeviceSaveData
  470. : FileSys::SaveDataType::SaveData,
  471. {},
  472. user_id_numeric,
  473. save_id_numeric,
  474. stoull_be(title_id->GetName()),
  475. title_id->GetSize(),
  476. {},
  477. {},
  478. });
  479. }
  480. }
  481. }
  482. } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
  483. // Temporary Storage
  484. for (const auto& user_id : type->GetSubdirectories()) {
  485. for (const auto& title_id : user_id->GetSubdirectories()) {
  486. if (!title_id->GetFiles().empty() ||
  487. !title_id->GetSubdirectories().empty()) {
  488. auto user_id_numeric =
  489. Common::HexStringToArray<0x10>(user_id->GetName());
  490. std::reverse(user_id_numeric.begin(), user_id_numeric.end());
  491. info.emplace_back(SaveDataInfo{
  492. 0,
  493. space,
  494. FileSys::SaveDataType::TemporaryStorage,
  495. {},
  496. user_id_numeric,
  497. stoull_be(type->GetName()),
  498. stoull_be(title_id->GetName()),
  499. title_id->GetSize(),
  500. {},
  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. {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
  575. {80, nullptr, "OpenSaveDataMetaFile"},
  576. {81, nullptr, "OpenSaveDataTransferManager"},
  577. {82, nullptr, "OpenSaveDataTransferManagerVersion2"},
  578. {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
  579. {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
  580. {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"},
  581. {86, nullptr, "OpenSaveDataMover"},
  582. {100, nullptr, "OpenImageDirectoryFileSystem"},
  583. {110, nullptr, "OpenContentStorageFileSystem"},
  584. {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
  585. {130, nullptr, "OpenCustomStorageFileSystem"},
  586. {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
  587. {201, nullptr, "OpenDataStorageByProgramId"},
  588. {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
  589. {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
  590. {204, nullptr, "OpenDataFileSystemByProgramIndex"},
  591. {205, nullptr, "OpenDataStorageByProgramIndex"},
  592. {400, nullptr, "OpenDeviceOperator"},
  593. {500, nullptr, "OpenSdCardDetectionEventNotifier"},
  594. {501, nullptr, "OpenGameCardDetectionEventNotifier"},
  595. {510, nullptr, "OpenSystemDataUpdateEventNotifier"},
  596. {511, nullptr, "NotifySystemDataUpdateEvent"},
  597. {520, nullptr, "SimulateGameCardDetectionEvent"},
  598. {600, nullptr, "SetCurrentPosixTime"},
  599. {601, nullptr, "QuerySaveDataTotalSize"},
  600. {602, nullptr, "VerifySaveDataFileSystem"},
  601. {603, nullptr, "CorruptSaveDataFileSystem"},
  602. {604, nullptr, "CreatePaddingFile"},
  603. {605, nullptr, "DeleteAllPaddingFiles"},
  604. {606, nullptr, "GetRightsId"},
  605. {607, nullptr, "RegisterExternalKey"},
  606. {608, nullptr, "UnregisterAllExternalKey"},
  607. {609, nullptr, "GetRightsIdByPath"},
  608. {610, nullptr, "GetRightsIdAndKeyGenerationByPath"},
  609. {611, nullptr, "SetCurrentPosixTimeWithTimeDifference"},
  610. {612, nullptr, "GetFreeSpaceSizeForSaveData"},
  611. {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"},
  612. {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"},
  613. {615, nullptr, "QuerySaveDataInternalStorageTotalSize"},
  614. {616, nullptr, "GetSaveDataCommitId"},
  615. {617, nullptr, "UnregisterExternalKey"},
  616. {620, nullptr, "SetSdCardEncryptionSeed"},
  617. {630, nullptr, "SetSdCardAccessibility"},
  618. {631, nullptr, "IsSdCardAccessible"},
  619. {640, nullptr, "IsSignedSystemPartitionOnSdCardValid"},
  620. {700, nullptr, "OpenAccessFailureResolver"},
  621. {701, nullptr, "GetAccessFailureDetectionEvent"},
  622. {702, nullptr, "IsAccessFailureDetected"},
  623. {710, nullptr, "ResolveAccessFailure"},
  624. {720, nullptr, "AbandonAccessFailure"},
  625. {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
  626. {810, nullptr, "RegisterProgramIndexMapInfo"},
  627. {1000, nullptr, "SetBisRootForHost"},
  628. {1001, nullptr, "SetSaveDataSize"},
  629. {1002, nullptr, "SetSaveDataRootPath"},
  630. {1003, nullptr, "DisableAutoSaveDataCreation"},
  631. {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"},
  632. {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"},
  633. {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"},
  634. {1007, nullptr, "RegisterUpdatePartition"},
  635. {1008, nullptr, "OpenRegisteredUpdatePartition"},
  636. {1009, nullptr, "GetAndClearMemoryReportInfo"},
  637. {1010, nullptr, "SetDataStorageRedirectTarget"},
  638. {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"},
  639. {1012, nullptr, "GetFsStackUsage"},
  640. {1013, nullptr, "UnsetSaveDataRootPath"},
  641. {1014, nullptr, "OutputMultiProgramTagAccessLog"},
  642. {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
  643. {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
  644. {1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
  645. {1300, nullptr, "OpenBisWiper"},
  646. };
  647. // clang-format on
  648. RegisterHandlers(functions);
  649. }
  650. FSP_SRV::~FSP_SRV() = default;
  651. void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
  652. IPC::RequestParser rp{ctx};
  653. current_process_id = rp.Pop<u64>();
  654. LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
  655. IPC::ResponseBuilder rb{ctx, 2};
  656. rb.Push(RESULT_SUCCESS);
  657. }
  658. void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
  659. IPC::RequestParser rp{ctx};
  660. const auto type = rp.PopRaw<FileSystemType>();
  661. const auto title_id = rp.PopRaw<u64>();
  662. LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}",
  663. static_cast<u8>(type), title_id);
  664. IPC::ResponseBuilder rb{ctx, 2, 0, 0};
  665. rb.Push(RESULT_UNKNOWN);
  666. }
  667. void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
  668. LOG_DEBUG(Service_FS, "called");
  669. auto filesystem = std::make_shared<IFileSystem>(
  670. fsc.OpenSDMC().Unwrap(), SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
  671. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  672. rb.Push(RESULT_SUCCESS);
  673. rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
  674. }
  675. void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
  676. IPC::RequestParser rp{ctx};
  677. auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
  678. [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
  679. u128 uid = rp.PopRaw<u128>();
  680. LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
  681. uid[1], uid[0]);
  682. fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
  683. IPC::ResponseBuilder rb{ctx, 2};
  684. rb.Push(RESULT_SUCCESS);
  685. }
  686. void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
  687. LOG_INFO(Service_FS, "called.");
  688. struct Parameters {
  689. FileSys::SaveDataSpaceId save_data_space_id;
  690. FileSys::SaveDataDescriptor descriptor;
  691. };
  692. IPC::RequestParser rp{ctx};
  693. const auto parameters = rp.PopRaw<Parameters>();
  694. auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
  695. if (dir.Failed()) {
  696. IPC::ResponseBuilder rb{ctx, 2, 0, 0};
  697. rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
  698. return;
  699. }
  700. FileSys::StorageId id;
  701. if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
  702. id = FileSys::StorageId::NandUser;
  703. } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
  704. parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
  705. id = FileSys::StorageId::SdCard;
  706. } else {
  707. id = FileSys::StorageId::NandSystem;
  708. }
  709. auto filesystem =
  710. std::make_shared<IFileSystem>(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
  711. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  712. rb.Push(RESULT_SUCCESS);
  713. rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
  714. }
  715. void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
  716. LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
  717. OpenSaveDataFileSystem(ctx);
  718. }
  719. void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
  720. IPC::RequestParser rp{ctx};
  721. const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
  722. LOG_INFO(Service_FS, "called, space={}", static_cast<u8>(space));
  723. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  724. rb.Push(RESULT_SUCCESS);
  725. rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
  726. }
  727. void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
  728. IPC::RequestParser rp{ctx};
  729. log_mode = rp.PopEnum<LogMode>();
  730. LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
  731. IPC::ResponseBuilder rb{ctx, 2};
  732. rb.Push(RESULT_SUCCESS);
  733. }
  734. void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
  735. LOG_DEBUG(Service_FS, "called");
  736. IPC::ResponseBuilder rb{ctx, 3};
  737. rb.Push(RESULT_SUCCESS);
  738. rb.PushEnum(log_mode);
  739. }
  740. void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
  741. LOG_DEBUG(Service_FS, "called");
  742. auto romfs = fsc.OpenRomFSCurrentProcess();
  743. if (romfs.Failed()) {
  744. // TODO (bunnei): Find the right error code to use here
  745. LOG_CRITICAL(Service_FS, "no file system interface available!");
  746. IPC::ResponseBuilder rb{ctx, 2};
  747. rb.Push(RESULT_UNKNOWN);
  748. return;
  749. }
  750. auto storage = std::make_shared<IStorage>(std::move(romfs.Unwrap()));
  751. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  752. rb.Push(RESULT_SUCCESS);
  753. rb.PushIpcInterface<IStorage>(std::move(storage));
  754. }
  755. void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
  756. IPC::RequestParser rp{ctx};
  757. const auto storage_id = rp.PopRaw<FileSys::StorageId>();
  758. const auto unknown = rp.PopRaw<u32>();
  759. const auto title_id = rp.PopRaw<u64>();
  760. LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
  761. static_cast<u8>(storage_id), unknown, title_id);
  762. auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
  763. if (data.Failed()) {
  764. const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
  765. if (archive != nullptr) {
  766. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  767. rb.Push(RESULT_SUCCESS);
  768. rb.PushIpcInterface(std::make_shared<IStorage>(archive));
  769. return;
  770. }
  771. // TODO(DarkLordZach): Find the right error code to use here
  772. LOG_ERROR(Service_FS,
  773. "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
  774. static_cast<u8>(storage_id));
  775. IPC::ResponseBuilder rb{ctx, 2};
  776. rb.Push(RESULT_UNKNOWN);
  777. return;
  778. }
  779. FileSys::PatchManager pm{title_id};
  780. auto storage = std::make_shared<IStorage>(
  781. pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
  782. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  783. rb.Push(RESULT_SUCCESS);
  784. rb.PushIpcInterface<IStorage>(std::move(storage));
  785. }
  786. void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
  787. IPC::RequestParser rp{ctx};
  788. auto storage_id = rp.PopRaw<FileSys::StorageId>();
  789. auto title_id = rp.PopRaw<u64>();
  790. LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
  791. static_cast<u8>(storage_id), title_id);
  792. IPC::ResponseBuilder rb{ctx, 2};
  793. rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
  794. }
  795. void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
  796. const auto raw = ctx.ReadBuffer();
  797. auto log = Common::StringFromFixedZeroTerminatedBuffer(
  798. reinterpret_cast<const char*>(raw.data()), raw.size());
  799. LOG_DEBUG(Service_FS, "called, log='{}'", log);
  800. reporter.SaveFilesystemAccessReport(log_mode, std::move(log));
  801. IPC::ResponseBuilder rb{ctx, 2};
  802. rb.Push(RESULT_SUCCESS);
  803. }
  804. void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
  805. LOG_DEBUG(Service_FS, "called");
  806. IPC::ResponseBuilder rb{ctx, 4};
  807. rb.Push(RESULT_SUCCESS);
  808. rb.PushEnum(AccessLogVersion::Latest);
  809. rb.Push(access_log_program_index);
  810. }
  811. class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
  812. public:
  813. explicit IMultiCommitManager() : ServiceFramework("IMultiCommitManager") {
  814. static const FunctionInfo functions[] = {
  815. {1, &IMultiCommitManager::Add, "Add"},
  816. {2, &IMultiCommitManager::Commit, "Commit"},
  817. };
  818. RegisterHandlers(functions);
  819. }
  820. private:
  821. FileSys::VirtualFile backend;
  822. void Add(Kernel::HLERequestContext& ctx) {
  823. LOG_WARNING(Service_FS, "(STUBBED) called");
  824. IPC::ResponseBuilder rb{ctx, 2};
  825. rb.Push(RESULT_SUCCESS);
  826. }
  827. void Commit(Kernel::HLERequestContext& ctx) {
  828. LOG_WARNING(Service_FS, "(STUBBED) called");
  829. IPC::ResponseBuilder rb{ctx, 2};
  830. rb.Push(RESULT_SUCCESS);
  831. }
  832. };
  833. void FSP_SRV::OpenMultiCommitManager(Kernel::HLERequestContext& ctx) {
  834. LOG_DEBUG(Service_FS, "called");
  835. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  836. rb.Push(RESULT_SUCCESS);
  837. rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>());
  838. }
  839. } // namespace Service::FileSystem