file.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <vector>
  4. #include "common/fs/file.h"
  5. #include "common/fs/fs.h"
  6. #ifdef ANDROID
  7. #include "common/fs/fs_android.h"
  8. #endif
  9. #include "common/logging/log.h"
  10. #ifdef _WIN32
  11. #include <io.h>
  12. #include <share.h>
  13. #else
  14. #include <unistd.h>
  15. #endif
  16. #ifdef _MSC_VER
  17. #define fileno _fileno
  18. #define fseeko _fseeki64
  19. #define ftello _ftelli64
  20. #endif
  21. namespace Common::FS {
  22. namespace fs = std::filesystem;
  23. namespace {
  24. #ifdef _WIN32
  25. /**
  26. * Converts the file access mode and file type enums to a file access mode wide string.
  27. *
  28. * @param mode File access mode
  29. * @param type File type
  30. *
  31. * @returns A pointer to a wide string representing the file access mode.
  32. */
  33. [[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) {
  34. switch (type) {
  35. case FileType::BinaryFile:
  36. switch (mode) {
  37. case FileAccessMode::Read:
  38. return L"rb";
  39. case FileAccessMode::Write:
  40. return L"wb";
  41. case FileAccessMode::Append:
  42. return L"ab";
  43. case FileAccessMode::ReadWrite:
  44. return L"r+b";
  45. case FileAccessMode::ReadAppend:
  46. return L"a+b";
  47. }
  48. break;
  49. case FileType::TextFile:
  50. switch (mode) {
  51. case FileAccessMode::Read:
  52. return L"r";
  53. case FileAccessMode::Write:
  54. return L"w";
  55. case FileAccessMode::Append:
  56. return L"a";
  57. case FileAccessMode::ReadWrite:
  58. return L"r+";
  59. case FileAccessMode::ReadAppend:
  60. return L"a+";
  61. }
  62. break;
  63. }
  64. return L"";
  65. }
  66. /**
  67. * Converts the file-share access flag enum to a Windows defined file-share access flag.
  68. *
  69. * @param flag File-share access flag
  70. *
  71. * @returns Windows defined file-share access flag.
  72. */
  73. [[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) {
  74. switch (flag) {
  75. case FileShareFlag::ShareNone:
  76. default:
  77. return _SH_DENYRW;
  78. case FileShareFlag::ShareReadOnly:
  79. return _SH_DENYWR;
  80. case FileShareFlag::ShareWriteOnly:
  81. return _SH_DENYRD;
  82. case FileShareFlag::ShareReadWrite:
  83. return _SH_DENYNO;
  84. }
  85. }
  86. #else
  87. /**
  88. * Converts the file access mode and file type enums to a file access mode string.
  89. *
  90. * @param mode File access mode
  91. * @param type File type
  92. *
  93. * @returns A pointer to a string representing the file access mode.
  94. */
  95. [[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) {
  96. switch (type) {
  97. case FileType::BinaryFile:
  98. switch (mode) {
  99. case FileAccessMode::Read:
  100. return "rb";
  101. case FileAccessMode::Write:
  102. return "wb";
  103. case FileAccessMode::Append:
  104. return "ab";
  105. case FileAccessMode::ReadWrite:
  106. return "r+b";
  107. case FileAccessMode::ReadAppend:
  108. return "a+b";
  109. }
  110. break;
  111. case FileType::TextFile:
  112. switch (mode) {
  113. case FileAccessMode::Read:
  114. return "r";
  115. case FileAccessMode::Write:
  116. return "w";
  117. case FileAccessMode::Append:
  118. return "a";
  119. case FileAccessMode::ReadWrite:
  120. return "r+";
  121. case FileAccessMode::ReadAppend:
  122. return "a+";
  123. }
  124. break;
  125. }
  126. return "";
  127. }
  128. #endif
  129. /**
  130. * Converts the seek origin enum to a seek origin integer.
  131. *
  132. * @param origin Seek origin
  133. *
  134. * @returns Seek origin integer.
  135. */
  136. [[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
  137. switch (origin) {
  138. case SeekOrigin::SetOrigin:
  139. default:
  140. return SEEK_SET;
  141. case SeekOrigin::CurrentPosition:
  142. return SEEK_CUR;
  143. case SeekOrigin::End:
  144. return SEEK_END;
  145. }
  146. }
  147. } // Anonymous namespace
  148. std::string ReadStringFromFile(const std::filesystem::path& path, FileType type) {
  149. if (!IsFile(path)) {
  150. return "";
  151. }
  152. IOFile io_file{path, FileAccessMode::Read, type};
  153. return io_file.ReadString(io_file.GetSize());
  154. }
  155. size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
  156. std::string_view string) {
  157. if (Exists(path) && !IsFile(path)) {
  158. return 0;
  159. }
  160. IOFile io_file{path, FileAccessMode::Write, type};
  161. return io_file.WriteString(string);
  162. }
  163. size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
  164. std::string_view string) {
  165. if (Exists(path) && !IsFile(path)) {
  166. return 0;
  167. }
  168. IOFile io_file{path, FileAccessMode::Append, type};
  169. return io_file.WriteString(string);
  170. }
  171. IOFile::IOFile() = default;
  172. IOFile::IOFile(const std::string& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
  173. Open(path, mode, type, flag);
  174. }
  175. IOFile::IOFile(std::string_view path, FileAccessMode mode, FileType type, FileShareFlag flag) {
  176. Open(path, mode, type, flag);
  177. }
  178. IOFile::IOFile(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
  179. Open(path, mode, type, flag);
  180. }
  181. IOFile::~IOFile() {
  182. Close();
  183. }
  184. IOFile::IOFile(IOFile&& other) noexcept {
  185. std::swap(file_path, other.file_path);
  186. std::swap(file_access_mode, other.file_access_mode);
  187. std::swap(file_type, other.file_type);
  188. std::swap(file, other.file);
  189. }
  190. IOFile& IOFile::operator=(IOFile&& other) noexcept {
  191. std::swap(file_path, other.file_path);
  192. std::swap(file_access_mode, other.file_access_mode);
  193. std::swap(file_type, other.file_type);
  194. std::swap(file, other.file);
  195. return *this;
  196. }
  197. fs::path IOFile::GetPath() const {
  198. return file_path;
  199. }
  200. FileAccessMode IOFile::GetAccessMode() const {
  201. return file_access_mode;
  202. }
  203. FileType IOFile::GetType() const {
  204. return file_type;
  205. }
  206. void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
  207. Close();
  208. file_path = path;
  209. file_access_mode = mode;
  210. file_type = type;
  211. errno = 0;
  212. #ifdef _WIN32
  213. if (flag != FileShareFlag::ShareNone) {
  214. file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
  215. } else {
  216. _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
  217. }
  218. #elif ANDROID
  219. if (Android::IsContentUri(path)) {
  220. ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
  221. const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
  222. if (fd != -1) {
  223. file = fdopen(fd, "r");
  224. const auto error_num = errno;
  225. if (error_num != 0 && file == nullptr) {
  226. LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
  227. strerror(error_num));
  228. }
  229. } else {
  230. LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
  231. }
  232. } else {
  233. file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
  234. }
  235. #else
  236. file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
  237. #endif
  238. if (!IsOpen()) {
  239. const auto ec = std::error_code{errno, std::generic_category()};
  240. LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
  241. PathToUTF8String(file_path), ec.message());
  242. }
  243. }
  244. void IOFile::Close() {
  245. if (!IsOpen()) {
  246. return;
  247. }
  248. errno = 0;
  249. const auto close_result = std::fclose(file) == 0;
  250. if (!close_result) {
  251. const auto ec = std::error_code{errno, std::generic_category()};
  252. LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
  253. PathToUTF8String(file_path), ec.message());
  254. }
  255. file = nullptr;
  256. }
  257. bool IOFile::IsOpen() const {
  258. return file != nullptr;
  259. }
  260. std::string IOFile::ReadString(size_t length) const {
  261. std::vector<char> string_buffer(length);
  262. const auto chars_read = ReadSpan<char>(string_buffer);
  263. const auto string_size = chars_read != length ? chars_read : length;
  264. return std::string{string_buffer.data(), string_size};
  265. }
  266. size_t IOFile::WriteString(std::span<const char> string) const {
  267. return WriteSpan(string);
  268. }
  269. bool IOFile::Flush() const {
  270. if (!IsOpen()) {
  271. return false;
  272. }
  273. errno = 0;
  274. #ifdef _WIN32
  275. const auto flush_result = std::fflush(file) == 0;
  276. #else
  277. const auto flush_result = std::fflush(file) == 0;
  278. #endif
  279. if (!flush_result) {
  280. const auto ec = std::error_code{errno, std::generic_category()};
  281. LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
  282. PathToUTF8String(file_path), ec.message());
  283. }
  284. return flush_result;
  285. }
  286. bool IOFile::Commit() const {
  287. if (!IsOpen()) {
  288. return false;
  289. }
  290. errno = 0;
  291. #ifdef _WIN32
  292. const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
  293. #else
  294. const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
  295. #endif
  296. if (!commit_result) {
  297. const auto ec = std::error_code{errno, std::generic_category()};
  298. LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
  299. PathToUTF8String(file_path), ec.message());
  300. }
  301. return commit_result;
  302. }
  303. bool IOFile::SetSize(u64 size) const {
  304. if (!IsOpen()) {
  305. return false;
  306. }
  307. errno = 0;
  308. #ifdef _WIN32
  309. const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0;
  310. #else
  311. const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0;
  312. #endif
  313. if (!set_size_result) {
  314. const auto ec = std::error_code{errno, std::generic_category()};
  315. LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
  316. PathToUTF8String(file_path), size, ec.message());
  317. }
  318. return set_size_result;
  319. }
  320. u64 IOFile::GetSize() const {
  321. if (!IsOpen()) {
  322. return 0;
  323. }
  324. // Flush any unwritten buffered data into the file prior to retrieving the file size.
  325. std::fflush(file);
  326. #if ANDROID
  327. u64 file_size = 0;
  328. if (Android::IsContentUri(file_path)) {
  329. file_size = Android::GetSize(file_path);
  330. } else {
  331. std::error_code ec;
  332. file_size = fs::file_size(file_path, ec);
  333. if (ec) {
  334. LOG_ERROR(Common_Filesystem,
  335. "Failed to retrieve the file size of path={}, ec_message={}",
  336. PathToUTF8String(file_path), ec.message());
  337. return 0;
  338. }
  339. }
  340. #else
  341. std::error_code ec;
  342. const auto file_size = fs::file_size(file_path, ec);
  343. if (ec) {
  344. LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
  345. PathToUTF8String(file_path), ec.message());
  346. return 0;
  347. }
  348. #endif
  349. return file_size;
  350. }
  351. bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
  352. if (!IsOpen()) {
  353. return false;
  354. }
  355. errno = 0;
  356. const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
  357. if (!seek_result) {
  358. const auto ec = std::error_code{errno, std::generic_category()};
  359. LOG_ERROR(Common_Filesystem,
  360. "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
  361. PathToUTF8String(file_path), offset, origin, ec.message());
  362. }
  363. return seek_result;
  364. }
  365. s64 IOFile::Tell() const {
  366. if (!IsOpen()) {
  367. return 0;
  368. }
  369. errno = 0;
  370. return ftello(file);
  371. }
  372. } // namespace Common::FS