fs.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/fs/file.h"
  4. #include "common/fs/fs.h"
  5. #ifdef ANDROID
  6. #include "common/fs/fs_android.h"
  7. #endif
  8. #include "common/fs/path_util.h"
  9. #include "common/logging/log.h"
  10. namespace Common::FS {
  11. namespace fs = std::filesystem;
  12. // File Operations
  13. bool NewFile(const fs::path& path, u64 size) {
  14. if (!ValidatePath(path)) {
  15. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  16. return false;
  17. }
  18. if (!Exists(path.parent_path())) {
  19. LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
  20. PathToUTF8String(path));
  21. return false;
  22. }
  23. if (Exists(path)) {
  24. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} exists", PathToUTF8String(path));
  25. return false;
  26. }
  27. IOFile io_file{path, FileAccessMode::Write};
  28. if (!io_file.IsOpen()) {
  29. LOG_ERROR(Common_Filesystem, "Failed to create a file at path={}", PathToUTF8String(path));
  30. return false;
  31. }
  32. if (!io_file.SetSize(size)) {
  33. LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={} to size={}",
  34. PathToUTF8String(path), size);
  35. return false;
  36. }
  37. io_file.Close();
  38. LOG_DEBUG(Common_Filesystem, "Successfully created a file at path={} with size={}",
  39. PathToUTF8String(path), size);
  40. return true;
  41. }
  42. bool RemoveFile(const fs::path& path) {
  43. if (!ValidatePath(path)) {
  44. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  45. return false;
  46. }
  47. if (!Exists(path)) {
  48. LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
  49. PathToUTF8String(path));
  50. return true;
  51. }
  52. if (!IsFile(path)) {
  53. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file",
  54. PathToUTF8String(path));
  55. return false;
  56. }
  57. std::error_code ec;
  58. fs::remove(path, ec);
  59. if (ec) {
  60. LOG_ERROR(Common_Filesystem, "Failed to remove the file at path={}, ec_message={}",
  61. PathToUTF8String(path), ec.message());
  62. return false;
  63. }
  64. LOG_DEBUG(Common_Filesystem, "Successfully removed the file at path={}",
  65. PathToUTF8String(path));
  66. return true;
  67. }
  68. bool RenameFile(const fs::path& old_path, const fs::path& new_path) {
  69. if (!ValidatePath(old_path) || !ValidatePath(new_path)) {
  70. LOG_ERROR(Common_Filesystem,
  71. "One or both input path(s) is not valid, old_path={}, new_path={}",
  72. PathToUTF8String(old_path), PathToUTF8String(new_path));
  73. return false;
  74. }
  75. if (!Exists(old_path)) {
  76. LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist",
  77. PathToUTF8String(old_path));
  78. return false;
  79. }
  80. if (!IsFile(old_path)) {
  81. LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a file",
  82. PathToUTF8String(old_path));
  83. return false;
  84. }
  85. if (Exists(new_path)) {
  86. LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists",
  87. PathToUTF8String(new_path));
  88. return false;
  89. }
  90. std::error_code ec;
  91. fs::rename(old_path, new_path, ec);
  92. if (ec) {
  93. LOG_ERROR(Common_Filesystem,
  94. "Failed to rename the file from old_path={} to new_path={}, ec_message={}",
  95. PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message());
  96. return false;
  97. }
  98. LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}",
  99. PathToUTF8String(old_path), PathToUTF8String(new_path));
  100. return true;
  101. }
  102. std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, FileType type,
  103. FileShareFlag flag) {
  104. if (!ValidatePath(path)) {
  105. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  106. return nullptr;
  107. }
  108. if (Exists(path) && !IsFile(path)) {
  109. LOG_ERROR(Common_Filesystem,
  110. "Filesystem object at path={} exists and is not a regular file",
  111. PathToUTF8String(path));
  112. return nullptr;
  113. }
  114. auto io_file = std::make_shared<IOFile>(path, mode, type, flag);
  115. if (!io_file->IsOpen()) {
  116. io_file.reset();
  117. LOG_ERROR(Common_Filesystem,
  118. "Failed to open the file at path={} with mode={}, type={}, flag={}",
  119. PathToUTF8String(path), mode, type, flag);
  120. return nullptr;
  121. }
  122. LOG_DEBUG(Common_Filesystem,
  123. "Successfully opened the file at path={} with mode={}, type={}, flag={}",
  124. PathToUTF8String(path), mode, type, flag);
  125. return io_file;
  126. }
  127. // Directory Operations
  128. bool CreateDir(const fs::path& path) {
  129. if (!ValidatePath(path)) {
  130. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  131. return false;
  132. }
  133. if (!Exists(path.parent_path())) {
  134. LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
  135. PathToUTF8String(path));
  136. return false;
  137. }
  138. if (IsDir(path)) {
  139. LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
  140. PathToUTF8String(path));
  141. return true;
  142. }
  143. std::error_code ec;
  144. fs::create_directory(path, ec);
  145. if (ec) {
  146. LOG_ERROR(Common_Filesystem, "Failed to create the directory at path={}, ec_message={}",
  147. PathToUTF8String(path), ec.message());
  148. return false;
  149. }
  150. LOG_DEBUG(Common_Filesystem, "Successfully created the directory at path={}",
  151. PathToUTF8String(path));
  152. return true;
  153. }
  154. bool CreateDirs(const fs::path& path) {
  155. if (!ValidatePath(path)) {
  156. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  157. return false;
  158. }
  159. if (IsDir(path)) {
  160. LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
  161. PathToUTF8String(path));
  162. return true;
  163. }
  164. std::error_code ec;
  165. fs::create_directories(path, ec);
  166. if (ec) {
  167. LOG_ERROR(Common_Filesystem, "Failed to create the directories at path={}, ec_message={}",
  168. PathToUTF8String(path), ec.message());
  169. return false;
  170. }
  171. LOG_DEBUG(Common_Filesystem, "Successfully created the directories at path={}",
  172. PathToUTF8String(path));
  173. return true;
  174. }
  175. bool CreateParentDir(const fs::path& path) {
  176. return CreateDir(path.parent_path());
  177. }
  178. bool CreateParentDirs(const fs::path& path) {
  179. return CreateDirs(path.parent_path());
  180. }
  181. bool RemoveDir(const fs::path& path) {
  182. if (!ValidatePath(path)) {
  183. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  184. return false;
  185. }
  186. if (!Exists(path)) {
  187. LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
  188. PathToUTF8String(path));
  189. return true;
  190. }
  191. if (!IsDir(path)) {
  192. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
  193. PathToUTF8String(path));
  194. return false;
  195. }
  196. std::error_code ec;
  197. fs::remove(path, ec);
  198. if (ec) {
  199. LOG_ERROR(Common_Filesystem, "Failed to remove the directory at path={}, ec_message={}",
  200. PathToUTF8String(path), ec.message());
  201. return false;
  202. }
  203. LOG_DEBUG(Common_Filesystem, "Successfully removed the directory at path={}",
  204. PathToUTF8String(path));
  205. return true;
  206. }
  207. bool RemoveDirRecursively(const fs::path& path) {
  208. if (!ValidatePath(path)) {
  209. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  210. return false;
  211. }
  212. if (!Exists(path)) {
  213. LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
  214. PathToUTF8String(path));
  215. return true;
  216. }
  217. if (!IsDir(path)) {
  218. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
  219. PathToUTF8String(path));
  220. return false;
  221. }
  222. std::error_code ec;
  223. fs::remove_all(path, ec);
  224. if (ec) {
  225. LOG_ERROR(Common_Filesystem,
  226. "Failed to remove the directory and its contents at path={}, ec_message={}",
  227. PathToUTF8String(path), ec.message());
  228. return false;
  229. }
  230. LOG_DEBUG(Common_Filesystem, "Successfully removed the directory and its contents at path={}",
  231. PathToUTF8String(path));
  232. return true;
  233. }
  234. bool RemoveDirContentsRecursively(const fs::path& path) {
  235. if (!ValidatePath(path)) {
  236. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  237. return false;
  238. }
  239. if (!Exists(path)) {
  240. LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
  241. PathToUTF8String(path));
  242. return true;
  243. }
  244. if (!IsDir(path)) {
  245. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
  246. PathToUTF8String(path));
  247. return false;
  248. }
  249. std::error_code ec;
  250. // TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
  251. for (const auto& entry : fs::directory_iterator(path, ec)) {
  252. if (ec) {
  253. LOG_ERROR(Common_Filesystem,
  254. "Failed to completely enumerate the directory at path={}, ec_message={}",
  255. PathToUTF8String(path), ec.message());
  256. break;
  257. }
  258. fs::remove(entry.path(), ec);
  259. if (ec) {
  260. LOG_ERROR(Common_Filesystem,
  261. "Failed to remove the filesystem object at path={}, ec_message={}",
  262. PathToUTF8String(entry.path()), ec.message());
  263. break;
  264. }
  265. // TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
  266. // recursive_directory_iterator throws an exception despite passing in a std::error_code.
  267. if (entry.status().type() == fs::file_type::directory) {
  268. return RemoveDirContentsRecursively(entry.path());
  269. }
  270. }
  271. if (ec) {
  272. LOG_ERROR(Common_Filesystem,
  273. "Failed to remove all the contents of the directory at path={}, ec_message={}",
  274. PathToUTF8String(path), ec.message());
  275. return false;
  276. }
  277. LOG_DEBUG(Common_Filesystem,
  278. "Successfully removed all the contents of the directory at path={}",
  279. PathToUTF8String(path));
  280. return true;
  281. }
  282. bool RenameDir(const fs::path& old_path, const fs::path& new_path) {
  283. if (!ValidatePath(old_path) || !ValidatePath(new_path)) {
  284. LOG_ERROR(Common_Filesystem,
  285. "One or both input path(s) is not valid, old_path={}, new_path={}",
  286. PathToUTF8String(old_path), PathToUTF8String(new_path));
  287. return false;
  288. }
  289. if (!Exists(old_path)) {
  290. LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist",
  291. PathToUTF8String(old_path));
  292. return false;
  293. }
  294. if (!IsDir(old_path)) {
  295. LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a directory",
  296. PathToUTF8String(old_path));
  297. return false;
  298. }
  299. if (Exists(new_path)) {
  300. LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists",
  301. PathToUTF8String(new_path));
  302. return false;
  303. }
  304. std::error_code ec;
  305. fs::rename(old_path, new_path, ec);
  306. if (ec) {
  307. LOG_ERROR(Common_Filesystem,
  308. "Failed to rename the file from old_path={} to new_path={}, ec_message={}",
  309. PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message());
  310. return false;
  311. }
  312. LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}",
  313. PathToUTF8String(old_path), PathToUTF8String(new_path));
  314. return true;
  315. }
  316. void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback,
  317. DirEntryFilter filter) {
  318. if (!ValidatePath(path)) {
  319. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  320. return;
  321. }
  322. if (!Exists(path)) {
  323. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
  324. PathToUTF8String(path));
  325. return;
  326. }
  327. if (!IsDir(path)) {
  328. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
  329. PathToUTF8String(path));
  330. return;
  331. }
  332. bool callback_error = false;
  333. std::error_code ec;
  334. for (const auto& entry : fs::directory_iterator(path, ec)) {
  335. if (ec) {
  336. break;
  337. }
  338. if (True(filter & DirEntryFilter::File) &&
  339. entry.status().type() == fs::file_type::regular) {
  340. if (!callback(entry.path())) {
  341. callback_error = true;
  342. break;
  343. }
  344. }
  345. if (True(filter & DirEntryFilter::Directory) &&
  346. entry.status().type() == fs::file_type::directory) {
  347. if (!callback(entry.path())) {
  348. callback_error = true;
  349. break;
  350. }
  351. }
  352. }
  353. if (callback_error || ec) {
  354. LOG_ERROR(Common_Filesystem,
  355. "Failed to visit all the directory entries of path={}, ec_message={}",
  356. PathToUTF8String(path), ec.message());
  357. return;
  358. }
  359. LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
  360. PathToUTF8String(path));
  361. }
  362. void IterateDirEntriesRecursively(const std::filesystem::path& path,
  363. const DirEntryCallable& callback, DirEntryFilter filter) {
  364. if (!ValidatePath(path)) {
  365. LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
  366. return;
  367. }
  368. if (!Exists(path)) {
  369. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
  370. PathToUTF8String(path));
  371. return;
  372. }
  373. if (!IsDir(path)) {
  374. LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
  375. PathToUTF8String(path));
  376. return;
  377. }
  378. bool callback_error = false;
  379. std::error_code ec;
  380. // TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
  381. for (const auto& entry : fs::directory_iterator(path, ec)) {
  382. if (ec) {
  383. break;
  384. }
  385. if (True(filter & DirEntryFilter::File) &&
  386. entry.status().type() == fs::file_type::regular) {
  387. if (!callback(entry.path())) {
  388. callback_error = true;
  389. break;
  390. }
  391. }
  392. if (True(filter & DirEntryFilter::Directory) &&
  393. entry.status().type() == fs::file_type::directory) {
  394. if (!callback(entry.path())) {
  395. callback_error = true;
  396. break;
  397. }
  398. }
  399. // TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
  400. // recursive_directory_iterator throws an exception despite passing in a std::error_code.
  401. if (entry.status().type() == fs::file_type::directory) {
  402. IterateDirEntriesRecursively(entry.path(), callback, filter);
  403. }
  404. }
  405. if (callback_error || ec) {
  406. LOG_ERROR(Common_Filesystem,
  407. "Failed to visit all the directory entries of path={}, ec_message={}",
  408. PathToUTF8String(path), ec.message());
  409. return;
  410. }
  411. LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
  412. PathToUTF8String(path));
  413. }
  414. // Generic Filesystem Operations
  415. bool Exists(const fs::path& path) {
  416. #ifdef ANDROID
  417. if (Android::IsContentUri(path)) {
  418. return Android::Exists(path);
  419. } else {
  420. return fs::exists(path);
  421. }
  422. #else
  423. return fs::exists(path);
  424. #endif
  425. }
  426. bool IsFile(const fs::path& path) {
  427. #ifdef ANDROID
  428. if (Android::IsContentUri(path)) {
  429. return !Android::IsDirectory(path);
  430. } else {
  431. return fs::is_regular_file(path);
  432. }
  433. #else
  434. return fs::is_regular_file(path);
  435. #endif
  436. }
  437. bool IsDir(const fs::path& path) {
  438. #ifdef ANDROID
  439. if (Android::IsContentUri(path)) {
  440. return Android::IsDirectory(path);
  441. } else {
  442. return fs::is_directory(path);
  443. }
  444. #else
  445. return fs::is_directory(path);
  446. #endif
  447. }
  448. fs::path GetCurrentDir() {
  449. std::error_code ec;
  450. const auto current_path = fs::current_path(ec);
  451. if (ec) {
  452. LOG_ERROR(Common_Filesystem, "Failed to get the current path, ec_message={}", ec.message());
  453. return {};
  454. }
  455. return current_path;
  456. }
  457. bool SetCurrentDir(const fs::path& path) {
  458. std::error_code ec;
  459. fs::current_path(path, ec);
  460. if (ec) {
  461. LOG_ERROR(Common_Filesystem, "Failed to set the current path to path={}, ec_message={}",
  462. PathToUTF8String(path), ec.message());
  463. return false;
  464. }
  465. return true;
  466. }
  467. fs::file_type GetEntryType(const fs::path& path) {
  468. std::error_code ec;
  469. const auto file_status = fs::status(path, ec);
  470. if (ec) {
  471. LOG_ERROR(Common_Filesystem, "Failed to retrieve the entry type of path={}, ec_message={}",
  472. PathToUTF8String(path), ec.message());
  473. return fs::file_type::not_found;
  474. }
  475. return file_status.type();
  476. }
  477. u64 GetSize(const fs::path& path) {
  478. std::error_code ec;
  479. const auto file_size = fs::file_size(path, ec);
  480. if (ec) {
  481. LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
  482. PathToUTF8String(path), ec.message());
  483. return 0;
  484. }
  485. return file_size;
  486. }
  487. u64 GetFreeSpaceSize(const fs::path& path) {
  488. std::error_code ec;
  489. const auto space_info = fs::space(path, ec);
  490. if (ec) {
  491. LOG_ERROR(Common_Filesystem,
  492. "Failed to retrieve the available free space of path={}, ec_message={}",
  493. PathToUTF8String(path), ec.message());
  494. return 0;
  495. }
  496. return space_info.free;
  497. }
  498. u64 GetTotalSpaceSize(const fs::path& path) {
  499. std::error_code ec;
  500. const auto space_info = fs::space(path, ec);
  501. if (ec) {
  502. LOG_ERROR(Common_Filesystem,
  503. "Failed to retrieve the total capacity of path={}, ec_message={}",
  504. PathToUTF8String(path), ec.message());
  505. return 0;
  506. }
  507. return space_info.capacity;
  508. }
  509. } // namespace Common::FS