archive_sdmc.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <memory>
  6. #include "common/file_util.h"
  7. #include "common/logging/log.h"
  8. #include "core/file_sys/archive_sdmc.h"
  9. #include "core/file_sys/disk_archive.h"
  10. #include "core/file_sys/errors.h"
  11. #include "core/file_sys/path_parser.h"
  12. #include "core/settings.h"
  13. ////////////////////////////////////////////////////////////////////////////////////////////////////
  14. // FileSys namespace
  15. namespace FileSys {
  16. ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
  17. const Mode& mode) const {
  18. LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
  19. const PathParser path_parser(path);
  20. if (!path_parser.IsValid()) {
  21. LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
  22. return ERROR_INVALID_PATH;
  23. }
  24. if (mode.hex == 0) {
  25. LOG_ERROR(Service_FS, "Empty open mode");
  26. return ERROR_INVALID_OPEN_FLAGS;
  27. }
  28. if (mode.create_flag && !mode.write_flag) {
  29. LOG_ERROR(Service_FS, "Create flag set but write flag not set");
  30. return ERROR_INVALID_OPEN_FLAGS;
  31. }
  32. const auto full_path = path_parser.BuildHostPath(mount_point);
  33. switch (path_parser.GetHostStatus(mount_point)) {
  34. case PathParser::InvalidMountPoint:
  35. LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
  36. return ERROR_NOT_FOUND;
  37. case PathParser::PathNotFound:
  38. case PathParser::FileInPath:
  39. LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
  40. return ERROR_NOT_FOUND;
  41. case PathParser::DirectoryFound:
  42. LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
  43. return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
  44. case PathParser::NotFound:
  45. if (!mode.create_flag) {
  46. LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
  47. full_path.c_str());
  48. return ERROR_NOT_FOUND;
  49. } else {
  50. // Create the file
  51. FileUtil::CreateEmptyFile(full_path);
  52. }
  53. break;
  54. }
  55. FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
  56. if (!file.IsOpen()) {
  57. LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
  58. return ERROR_NOT_FOUND;
  59. }
  60. auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
  61. return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
  62. }
  63. ResultCode SDMCArchive::DeleteFile(const Path& path) const {
  64. const PathParser path_parser(path);
  65. if (!path_parser.IsValid()) {
  66. LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
  67. return ERROR_INVALID_PATH;
  68. }
  69. const auto full_path = path_parser.BuildHostPath(mount_point);
  70. switch (path_parser.GetHostStatus(mount_point)) {
  71. case PathParser::InvalidMountPoint:
  72. LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
  73. return ERROR_NOT_FOUND;
  74. case PathParser::PathNotFound:
  75. case PathParser::FileInPath:
  76. case PathParser::NotFound:
  77. LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
  78. return ERROR_NOT_FOUND;
  79. case PathParser::DirectoryFound:
  80. LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
  81. return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
  82. }
  83. if (FileUtil::Delete(full_path)) {
  84. return RESULT_SUCCESS;
  85. }
  86. LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
  87. return ERROR_NOT_FOUND;
  88. }
  89. ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
  90. if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) {
  91. return RESULT_SUCCESS;
  92. }
  93. // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
  94. // exist or similar. Verify.
  95. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  96. ErrorSummary::NothingHappened, ErrorLevel::Status);
  97. }
  98. template <typename T>
  99. static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
  100. T deleter) {
  101. const PathParser path_parser(path);
  102. if (!path_parser.IsValid()) {
  103. LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
  104. return ERROR_INVALID_PATH;
  105. }
  106. if (path_parser.IsRootDirectory())
  107. return ERROR_NOT_FOUND;
  108. const auto full_path = path_parser.BuildHostPath(mount_point);
  109. switch (path_parser.GetHostStatus(mount_point)) {
  110. case PathParser::InvalidMountPoint:
  111. LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
  112. return ERROR_NOT_FOUND;
  113. case PathParser::PathNotFound:
  114. case PathParser::NotFound:
  115. LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
  116. return ERROR_NOT_FOUND;
  117. case PathParser::FileInPath:
  118. case PathParser::FileFound:
  119. LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
  120. return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
  121. }
  122. if (deleter(full_path)) {
  123. return RESULT_SUCCESS;
  124. }
  125. LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
  126. return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
  127. }
  128. ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
  129. return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
  130. }
  131. ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
  132. return DeleteDirectoryHelper(
  133. path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
  134. }
  135. ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
  136. const PathParser path_parser(path);
  137. if (!path_parser.IsValid()) {
  138. LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
  139. return ERROR_INVALID_PATH;
  140. }
  141. const auto full_path = path_parser.BuildHostPath(mount_point);
  142. switch (path_parser.GetHostStatus(mount_point)) {
  143. case PathParser::InvalidMountPoint:
  144. LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
  145. return ERROR_NOT_FOUND;
  146. case PathParser::PathNotFound:
  147. case PathParser::FileInPath:
  148. LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
  149. return ERROR_NOT_FOUND;
  150. case PathParser::DirectoryFound:
  151. LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
  152. return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
  153. case PathParser::FileFound:
  154. LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
  155. return ERROR_ALREADY_EXISTS;
  156. }
  157. if (size == 0) {
  158. FileUtil::CreateEmptyFile(full_path);
  159. return RESULT_SUCCESS;
  160. }
  161. FileUtil::IOFile file(full_path, "wb");
  162. // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
  163. // We do this by seeking to the right size, then writing a single null byte.
  164. if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
  165. return RESULT_SUCCESS;
  166. }
  167. LOG_ERROR(Service_FS, "Too large file");
  168. return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
  169. ErrorLevel::Info);
  170. }
  171. ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
  172. const PathParser path_parser(path);
  173. if (!path_parser.IsValid()) {
  174. LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
  175. return ERROR_INVALID_PATH;
  176. }
  177. const auto full_path = path_parser.BuildHostPath(mount_point);
  178. switch (path_parser.GetHostStatus(mount_point)) {
  179. case PathParser::InvalidMountPoint:
  180. LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
  181. return ERROR_NOT_FOUND;
  182. case PathParser::PathNotFound:
  183. case PathParser::FileInPath:
  184. LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
  185. return ERROR_NOT_FOUND;
  186. case PathParser::DirectoryFound:
  187. case PathParser::FileFound:
  188. LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
  189. return ERROR_ALREADY_EXISTS;
  190. }
  191. if (FileUtil::CreateDir(mount_point + path.AsString())) {
  192. return RESULT_SUCCESS;
  193. }
  194. LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
  195. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
  196. ErrorLevel::Status);
  197. }
  198. ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
  199. if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()))
  200. return RESULT_SUCCESS;
  201. // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
  202. // exist or similar. Verify.
  203. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  204. ErrorSummary::NothingHappened, ErrorLevel::Status);
  205. }
  206. ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const {
  207. const PathParser path_parser(path);
  208. if (!path_parser.IsValid()) {
  209. LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
  210. return ERROR_INVALID_PATH;
  211. }
  212. const auto full_path = path_parser.BuildHostPath(mount_point);
  213. switch (path_parser.GetHostStatus(mount_point)) {
  214. case PathParser::InvalidMountPoint:
  215. LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
  216. return ERROR_NOT_FOUND;
  217. case PathParser::PathNotFound:
  218. case PathParser::NotFound:
  219. case PathParser::FileFound:
  220. LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
  221. return ERROR_NOT_FOUND;
  222. case PathParser::FileInPath:
  223. LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
  224. return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
  225. }
  226. auto directory = std::make_unique<DiskDirectory>(full_path);
  227. return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
  228. }
  229. u64 SDMCArchive::GetFreeBytes() const {
  230. // TODO: Stubbed to return 1GiB
  231. return 1024 * 1024 * 1024;
  232. }
  233. ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
  234. : sdmc_directory(sdmc_directory) {
  235. LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
  236. }
  237. bool ArchiveFactory_SDMC::Initialize() {
  238. if (!Settings::values.use_virtual_sd) {
  239. LOG_WARNING(Service_FS, "SDMC disabled by config.");
  240. return false;
  241. }
  242. if (!FileUtil::CreateFullPath(sdmc_directory)) {
  243. LOG_ERROR(Service_FS, "Unable to create SDMC path.");
  244. return false;
  245. }
  246. return true;
  247. }
  248. ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
  249. auto archive = std::make_unique<SDMCArchive>(sdmc_directory);
  250. return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
  251. }
  252. ResultCode ArchiveFactory_SDMC::Format(const Path& path,
  253. const FileSys::ArchiveFormatInfo& format_info) {
  254. // This is kind of an undesirable operation, so let's just ignore it. :)
  255. return RESULT_SUCCESS;
  256. }
  257. ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const {
  258. // TODO(Subv): Implement
  259. LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
  260. return ResultCode(-1);
  261. }
  262. } // namespace FileSys