file.cpp 10 KB

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