vfs.cpp 16 KB

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