vfs.cpp 17 KB

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