vfs.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <numeric>
  5. #include <string>
  6. #include "common/fs/path_util.h"
  7. #include "core/file_sys/mode.h"
  8. #include "core/file_sys/vfs.h"
  9. namespace FileSys {
  10. VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
  11. VfsFilesystem::~VfsFilesystem() = default;
  12. std::string VfsFilesystem::GetName() const {
  13. return root->GetName();
  14. }
  15. bool VfsFilesystem::IsReadable() const {
  16. return root->IsReadable();
  17. }
  18. bool VfsFilesystem::IsWritable() const {
  19. return root->IsWritable();
  20. }
  21. VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
  22. const auto path = Common::FS::SanitizePath(path_);
  23. if (root->GetFileRelative(path) != nullptr)
  24. return VfsEntryType::File;
  25. if (root->GetDirectoryRelative(path) != nullptr)
  26. return VfsEntryType::Directory;
  27. return VfsEntryType::None;
  28. }
  29. VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
  30. const auto path = Common::FS::SanitizePath(path_);
  31. return root->GetFileRelative(path);
  32. }
  33. VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
  34. const auto path = Common::FS::SanitizePath(path_);
  35. return root->CreateFileRelative(path);
  36. }
  37. VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
  38. const auto old_path = Common::FS::SanitizePath(old_path_);
  39. const auto new_path = Common::FS::SanitizePath(new_path_);
  40. // VfsDirectory impls are only required to implement copy across the current directory.
  41. if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
  42. if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
  43. return nullptr;
  44. return OpenFile(new_path, Mode::ReadWrite);
  45. }
  46. // Do it using RawCopy. Non-default impls are encouraged to optimize this.
  47. const auto old_file = OpenFile(old_path, Mode::Read);
  48. if (old_file == nullptr)
  49. return nullptr;
  50. auto new_file = OpenFile(new_path, Mode::Read);
  51. if (new_file != nullptr)
  52. return nullptr;
  53. new_file = CreateFile(new_path, Mode::Write);
  54. if (new_file == nullptr)
  55. return nullptr;
  56. if (!VfsRawCopy(old_file, new_file))
  57. return nullptr;
  58. return new_file;
  59. }
  60. VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
  61. const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
  62. const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
  63. // Again, non-default impls are highly encouraged to provide a more optimized version of this.
  64. auto out = CopyFile(sanitized_old_path, sanitized_new_path);
  65. if (out == nullptr)
  66. return nullptr;
  67. if (DeleteFile(sanitized_old_path))
  68. return out;
  69. return nullptr;
  70. }
  71. bool VfsFilesystem::DeleteFile(std::string_view path_) {
  72. const auto path = Common::FS::SanitizePath(path_);
  73. auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
  74. if (parent == nullptr)
  75. return false;
  76. return parent->DeleteFile(Common::FS::GetFilename(path));
  77. }
  78. VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
  79. const auto path = Common::FS::SanitizePath(path_);
  80. return root->GetDirectoryRelative(path);
  81. }
  82. VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
  83. const auto path = Common::FS::SanitizePath(path_);
  84. return root->CreateDirectoryRelative(path);
  85. }
  86. VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
  87. const auto old_path = Common::FS::SanitizePath(old_path_);
  88. const auto new_path = Common::FS::SanitizePath(new_path_);
  89. // Non-default impls are highly encouraged to provide a more optimized version of this.
  90. auto old_dir = OpenDirectory(old_path, Mode::Read);
  91. if (old_dir == nullptr)
  92. return nullptr;
  93. auto new_dir = OpenDirectory(new_path, Mode::Read);
  94. if (new_dir != nullptr)
  95. return nullptr;
  96. new_dir = CreateDirectory(new_path, Mode::Write);
  97. if (new_dir == nullptr)
  98. return nullptr;
  99. for (const auto& file : old_dir->GetFiles()) {
  100. const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName());
  101. if (x == nullptr)
  102. return nullptr;
  103. }
  104. for (const auto& dir : old_dir->GetSubdirectories()) {
  105. const auto x =
  106. CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName());
  107. if (x == nullptr)
  108. return nullptr;
  109. }
  110. return new_dir;
  111. }
  112. VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
  113. const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
  114. const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
  115. // Non-default impls are highly encouraged to provide a more optimized version of this.
  116. auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
  117. if (out == nullptr)
  118. return nullptr;
  119. if (DeleteDirectory(sanitized_old_path))
  120. return out;
  121. return nullptr;
  122. }
  123. bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
  124. const auto path = Common::FS::SanitizePath(path_);
  125. auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
  126. if (parent == nullptr)
  127. return false;
  128. return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
  129. }
  130. VfsFile::~VfsFile() = default;
  131. std::string VfsFile::GetExtension() const {
  132. return std::string(Common::FS::GetExtensionFromFilename(GetName()));
  133. }
  134. VfsDirectory::~VfsDirectory() = default;
  135. std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
  136. u8 out{};
  137. const std::size_t size = Read(&out, sizeof(u8), offset);
  138. if (size == 1) {
  139. return out;
  140. }
  141. return std::nullopt;
  142. }
  143. std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
  144. std::vector<u8> out(size);
  145. std::size_t read_size = Read(out.data(), size, offset);
  146. out.resize(read_size);
  147. return out;
  148. }
  149. std::vector<u8> VfsFile::ReadAllBytes() const {
  150. return ReadBytes(GetSize());
  151. }
  152. bool VfsFile::WriteByte(u8 data, std::size_t offset) {
  153. return Write(&data, 1, offset) == 1;
  154. }
  155. std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) {
  156. return Write(data.data(), data.size(), offset);
  157. }
  158. std::string VfsFile::GetFullPath() const {
  159. if (GetContainingDirectory() == nullptr)
  160. return '/' + GetName();
  161. return GetContainingDirectory()->GetFullPath() + '/' + GetName();
  162. }
  163. VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
  164. auto vec = Common::FS::SplitPathComponents(path);
  165. if (vec.empty()) {
  166. return nullptr;
  167. }
  168. if (vec.size() == 1) {
  169. return GetFile(vec[0]);
  170. }
  171. auto dir = GetSubdirectory(vec[0]);
  172. for (std::size_t component = 1; component < vec.size() - 1; ++component) {
  173. if (dir == nullptr) {
  174. return nullptr;
  175. }
  176. dir = dir->GetSubdirectory(vec[component]);
  177. }
  178. if (dir == nullptr) {
  179. return nullptr;
  180. }
  181. return dir->GetFile(vec.back());
  182. }
  183. VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
  184. if (IsRoot()) {
  185. return GetFileRelative(path);
  186. }
  187. return GetParentDirectory()->GetFileAbsolute(path);
  188. }
  189. VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const {
  190. auto vec = Common::FS::SplitPathComponents(path);
  191. if (vec.empty()) {
  192. // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
  193. // because of const-ness
  194. return nullptr;
  195. }
  196. auto dir = GetSubdirectory(vec[0]);
  197. for (std::size_t component = 1; component < vec.size(); ++component) {
  198. if (dir == nullptr) {
  199. return nullptr;
  200. }
  201. dir = dir->GetSubdirectory(vec[component]);
  202. }
  203. return dir;
  204. }
  205. VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const {
  206. if (IsRoot()) {
  207. return GetDirectoryRelative(path);
  208. }
  209. return GetParentDirectory()->GetDirectoryAbsolute(path);
  210. }
  211. VirtualFile VfsDirectory::GetFile(std::string_view name) const {
  212. const auto& files = GetFiles();
  213. const auto iter = std::find_if(files.begin(), files.end(),
  214. [&name](const auto& file1) { return name == file1->GetName(); });
  215. return iter == files.end() ? nullptr : *iter;
  216. }
  217. FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const {
  218. return {};
  219. }
  220. VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const {
  221. const auto& subs = GetSubdirectories();
  222. const auto iter = std::find_if(subs.begin(), subs.end(),
  223. [&name](const auto& file1) { return name == file1->GetName(); });
  224. return iter == subs.end() ? nullptr : *iter;
  225. }
  226. bool VfsDirectory::IsRoot() const {
  227. return GetParentDirectory() == nullptr;
  228. }
  229. std::size_t VfsDirectory::GetSize() const {
  230. const auto& files = GetFiles();
  231. const auto sum_sizes = [](const auto& range) {
  232. return std::accumulate(range.begin(), range.end(), 0ULL,
  233. [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
  234. };
  235. const auto file_total = sum_sizes(files);
  236. const auto& sub_dir = GetSubdirectories();
  237. const auto subdir_total = sum_sizes(sub_dir);
  238. return file_total + subdir_total;
  239. }
  240. VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) {
  241. auto vec = Common::FS::SplitPathComponents(path);
  242. if (vec.empty()) {
  243. return nullptr;
  244. }
  245. if (vec.size() == 1) {
  246. return CreateFile(vec[0]);
  247. }
  248. auto dir = GetSubdirectory(vec[0]);
  249. if (dir == nullptr) {
  250. dir = CreateSubdirectory(vec[0]);
  251. if (dir == nullptr) {
  252. return nullptr;
  253. }
  254. }
  255. return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
  256. }
  257. VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) {
  258. if (IsRoot()) {
  259. return CreateFileRelative(path);
  260. }
  261. return GetParentDirectory()->CreateFileAbsolute(path);
  262. }
  263. VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) {
  264. auto vec = Common::FS::SplitPathComponents(path);
  265. if (vec.empty()) {
  266. return nullptr;
  267. }
  268. if (vec.size() == 1) {
  269. return CreateSubdirectory(vec[0]);
  270. }
  271. auto dir = GetSubdirectory(vec[0]);
  272. if (dir == nullptr) {
  273. dir = CreateSubdirectory(vec[0]);
  274. if (dir == nullptr) {
  275. return nullptr;
  276. }
  277. }
  278. return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
  279. }
  280. VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
  281. if (IsRoot()) {
  282. return CreateDirectoryRelative(path);
  283. }
  284. return GetParentDirectory()->CreateDirectoryAbsolute(path);
  285. }
  286. bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
  287. auto dir = GetSubdirectory(name);
  288. if (dir == nullptr) {
  289. return false;
  290. }
  291. bool success = true;
  292. for (const auto& file : dir->GetFiles()) {
  293. if (!DeleteFile(file->GetName())) {
  294. success = false;
  295. }
  296. }
  297. for (const auto& sdir : dir->GetSubdirectories()) {
  298. if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
  299. success = false;
  300. }
  301. }
  302. return success;
  303. }
  304. bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
  305. auto dir = GetSubdirectory(name);
  306. if (dir == nullptr) {
  307. return false;
  308. }
  309. bool success = true;
  310. for (const auto& file : dir->GetFiles()) {
  311. if (!dir->DeleteFile(file->GetName())) {
  312. success = false;
  313. }
  314. }
  315. for (const auto& sdir : dir->GetSubdirectories()) {
  316. if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
  317. success = false;
  318. }
  319. }
  320. return success;
  321. }
  322. bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
  323. const auto f1 = GetFile(src);
  324. auto f2 = CreateFile(dest);
  325. if (f1 == nullptr || f2 == nullptr) {
  326. return false;
  327. }
  328. if (!f2->Resize(f1->GetSize())) {
  329. DeleteFile(dest);
  330. return false;
  331. }
  332. return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
  333. }
  334. std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
  335. std::map<std::string, VfsEntryType, std::less<>> out;
  336. for (const auto& dir : GetSubdirectories())
  337. out.emplace(dir->GetName(), VfsEntryType::Directory);
  338. for (const auto& file : GetFiles())
  339. out.emplace(file->GetName(), VfsEntryType::File);
  340. return out;
  341. }
  342. std::string VfsDirectory::GetFullPath() const {
  343. if (IsRoot())
  344. return GetName();
  345. return GetParentDirectory()->GetFullPath() + '/' + GetName();
  346. }
  347. bool ReadOnlyVfsDirectory::IsWritable() const {
  348. return false;
  349. }
  350. bool ReadOnlyVfsDirectory::IsReadable() const {
  351. return true;
  352. }
  353. VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
  354. return nullptr;
  355. }
  356. VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
  357. return nullptr;
  358. }
  359. VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) {
  360. return nullptr;
  361. }
  362. VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) {
  363. return nullptr;
  364. }
  365. VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
  366. return nullptr;
  367. }
  368. VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) {
  369. return nullptr;
  370. }
  371. bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
  372. return false;
  373. }
  374. bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
  375. return false;
  376. }
  377. bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
  378. return false;
  379. }
  380. bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
  381. return false;
  382. }
  383. bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
  384. return false;
  385. }
  386. bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) {
  387. if (file1->GetSize() != file2->GetSize())
  388. return false;
  389. std::vector<u8> f1_v(block_size);
  390. std::vector<u8> f2_v(block_size);
  391. for (std::size_t i = 0; i < file1->GetSize(); i += block_size) {
  392. auto f1_vs = file1->Read(f1_v.data(), block_size, i);
  393. auto f2_vs = file2->Read(f2_v.data(), block_size, i);
  394. if (f1_vs != f2_vs)
  395. return false;
  396. auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
  397. if (iters.first != f1_v.end() && iters.second != f2_v.end())
  398. return false;
  399. }
  400. return true;
  401. }
  402. bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
  403. if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
  404. return false;
  405. if (!dest->Resize(src->GetSize()))
  406. return false;
  407. std::vector<u8> temp(std::min(block_size, src->GetSize()));
  408. for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
  409. const auto read = std::min(block_size, src->GetSize() - i);
  410. if (src->Read(temp.data(), read, i) != read) {
  411. return false;
  412. }
  413. if (dest->Write(temp.data(), read, i) != read) {
  414. return false;
  415. }
  416. }
  417. return true;
  418. }
  419. bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
  420. if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
  421. return false;
  422. for (const auto& file : src->GetFiles()) {
  423. const auto out = dest->CreateFile(file->GetName());
  424. if (!VfsRawCopy(file, out, block_size))
  425. return false;
  426. }
  427. for (const auto& dir : src->GetSubdirectories()) {
  428. const auto out = dest->CreateSubdirectory(dir->GetName());
  429. if (!VfsRawCopyD(dir, out, block_size))
  430. return false;
  431. }
  432. return true;
  433. }
  434. VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
  435. const auto res = rel->GetDirectoryRelative(path);
  436. if (res == nullptr)
  437. return rel->CreateDirectoryRelative(path);
  438. return res;
  439. }
  440. } // namespace FileSys