vfs.cpp 17 KB

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