diff options
| author | 2016-10-16 22:19:13 +0800 | |
|---|---|---|
| committer | 2016-11-19 17:17:19 +0200 | |
| commit | 75ee2f8c67022b0731e438b34fa65216531eccd1 (patch) | |
| tree | df4293ddc6a1712e102a7f2d8d71205364b02d6b /src | |
| parent | FileSys: make Archive interfaces return error code (diff) | |
| download | yuzu-75ee2f8c67022b0731e438b34fa65216531eccd1.tar.gz yuzu-75ee2f8c67022b0731e438b34fa65216531eccd1.tar.xz yuzu-75ee2f8c67022b0731e438b34fa65216531eccd1.zip | |
FileSys: add PathParser
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/file_sys/path_parser.cpp | 98 | ||||
| -rw-r--r-- | src/core/file_sys/path_parser.h | 61 | ||||
| -rw-r--r-- | src/tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/tests/core/file_sys/path_parser.cpp | 38 |
5 files changed, 200 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4a9c6fd2f..ae9e8dcea 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -24,6 +24,7 @@ set(SRCS | |||
| 24 | file_sys/archive_systemsavedata.cpp | 24 | file_sys/archive_systemsavedata.cpp |
| 25 | file_sys/disk_archive.cpp | 25 | file_sys/disk_archive.cpp |
| 26 | file_sys/ivfc_archive.cpp | 26 | file_sys/ivfc_archive.cpp |
| 27 | file_sys/path_parser.cpp | ||
| 27 | gdbstub/gdbstub.cpp | 28 | gdbstub/gdbstub.cpp |
| 28 | hle/config_mem.cpp | 29 | hle/config_mem.cpp |
| 29 | hle/hle.cpp | 30 | hle/hle.cpp |
| @@ -168,6 +169,7 @@ set(HEADERS | |||
| 168 | file_sys/disk_archive.h | 169 | file_sys/disk_archive.h |
| 169 | file_sys/file_backend.h | 170 | file_sys/file_backend.h |
| 170 | file_sys/ivfc_archive.h | 171 | file_sys/ivfc_archive.h |
| 172 | file_sys/path_parser.h | ||
| 171 | gdbstub/gdbstub.h | 173 | gdbstub/gdbstub.h |
| 172 | hle/config_mem.h | 174 | hle/config_mem.h |
| 173 | hle/function_wrappers.h | 175 | hle/function_wrappers.h |
diff --git a/src/core/file_sys/path_parser.cpp b/src/core/file_sys/path_parser.cpp new file mode 100644 index 000000000..5a89b02b8 --- /dev/null +++ b/src/core/file_sys/path_parser.cpp | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <set> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/file_sys/path_parser.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | PathParser::PathParser(const Path& path) { | ||
| 14 | if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) { | ||
| 15 | is_valid = false; | ||
| 16 | return; | ||
| 17 | } | ||
| 18 | |||
| 19 | auto path_string = path.AsString(); | ||
| 20 | if (path_string.size() == 0 || path_string[0] != '/') { | ||
| 21 | is_valid = false; | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | |||
| 25 | // Filter out invalid characters for the host system. | ||
| 26 | // Although some of these characters are valid on 3DS, they are unlikely to be used by games. | ||
| 27 | if (std::find_if(path_string.begin(), path_string.end(), [](char c) { | ||
| 28 | static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'}; | ||
| 29 | return invalid_chars.find(c) != invalid_chars.end(); | ||
| 30 | }) != path_string.end()) { | ||
| 31 | is_valid = false; | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | |||
| 35 | Common::SplitString(path_string, '/', path_sequence); | ||
| 36 | |||
| 37 | auto begin = path_sequence.begin(); | ||
| 38 | auto end = path_sequence.end(); | ||
| 39 | end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; }); | ||
| 40 | path_sequence = std::vector<std::string>(begin, end); | ||
| 41 | |||
| 42 | // checks if the path is out of bounds. | ||
| 43 | int level = 0; | ||
| 44 | for (auto& node : path_sequence) { | ||
| 45 | if (node == "..") { | ||
| 46 | --level; | ||
| 47 | if (level < 0) { | ||
| 48 | is_valid = false; | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | } else { | ||
| 52 | ++level; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | is_valid = true; | ||
| 57 | is_root = level == 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const { | ||
| 61 | auto path = mount_point; | ||
| 62 | if (!FileUtil::IsDirectory(path)) | ||
| 63 | return InvalidMountPoint; | ||
| 64 | if (path_sequence.empty()) { | ||
| 65 | return DirectoryFound; | ||
| 66 | } | ||
| 67 | |||
| 68 | for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) { | ||
| 69 | if (path.back() != '/') | ||
| 70 | path += '/'; | ||
| 71 | path += *iter; | ||
| 72 | |||
| 73 | if (!FileUtil::Exists(path)) | ||
| 74 | return PathNotFound; | ||
| 75 | if (FileUtil::IsDirectory(path)) | ||
| 76 | continue; | ||
| 77 | return FileInPath; | ||
| 78 | } | ||
| 79 | |||
| 80 | path += "/" + path_sequence.back(); | ||
| 81 | if (!FileUtil::Exists(path)) | ||
| 82 | return NotFound; | ||
| 83 | if (FileUtil::IsDirectory(path)) | ||
| 84 | return DirectoryFound; | ||
| 85 | return FileFound; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::string PathParser::BuildHostPath(const std::string& mount_point) const { | ||
| 89 | std::string path = mount_point; | ||
| 90 | for (auto& node : path_sequence) { | ||
| 91 | if (path.back() != '/') | ||
| 92 | path += '/'; | ||
| 93 | path += node; | ||
| 94 | } | ||
| 95 | return path; | ||
| 96 | } | ||
| 97 | |||
| 98 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h new file mode 100644 index 000000000..990802579 --- /dev/null +++ b/src/core/file_sys/path_parser.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include <vector> | ||
| 9 | #include "core/file_sys/archive_backend.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A helper class parsing and verifying a string-type Path. | ||
| 15 | * Every archives with a sub file system should use this class to parse the path argument and check | ||
| 16 | * the status of the file / directory in question on the host file system. | ||
| 17 | */ | ||
| 18 | class PathParser { | ||
| 19 | public: | ||
| 20 | PathParser(const Path& path); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Checks if the Path is valid. | ||
| 24 | * This function should be called once a PathParser is constructed. | ||
| 25 | * A Path is valid if: | ||
| 26 | * - it is a string path (with type LowPathType::Char or LowPathType::Wchar), | ||
| 27 | * - it starts with "/" (this seems a hard requirement in real 3DS), | ||
| 28 | * - it doesn't contain invalid characters, and | ||
| 29 | * - it doesn't go out of the root directory using "..". | ||
| 30 | */ | ||
| 31 | bool IsValid() const { | ||
| 32 | return is_valid; | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Checks if the Path represents the root directory. | ||
| 36 | bool IsRootDirectory() const { | ||
| 37 | return is_root; | ||
| 38 | } | ||
| 39 | |||
| 40 | enum HostStatus { | ||
| 41 | InvalidMountPoint, | ||
| 42 | PathNotFound, // "/a/b/c" when "a" doesn't exist | ||
| 43 | FileInPath, // "/a/b/c" when "a" is a file | ||
| 44 | FileFound, // "/a/b/c" when "c" is a file | ||
| 45 | DirectoryFound, // "/a/b/c" when "c" is a directory | ||
| 46 | NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist | ||
| 47 | }; | ||
| 48 | |||
| 49 | /// Checks the status of the specified file / directory by the Path on the host file system. | ||
| 50 | HostStatus GetHostStatus(const std::string& mount_point) const; | ||
| 51 | |||
| 52 | /// Builds a full path on the host file system. | ||
| 53 | std::string BuildHostPath(const std::string& mount_point) const; | ||
| 54 | |||
| 55 | private: | ||
| 56 | std::vector<std::string> path_sequence; | ||
| 57 | bool is_valid{}; | ||
| 58 | bool is_root{}; | ||
| 59 | }; | ||
| 60 | |||
| 61 | } // namespace FileSys | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 457c55571..47799e1c2 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | tests.cpp | 2 | tests.cpp |
| 3 | core/file_sys/path_parser.cpp | ||
| 3 | ) | 4 | ) |
| 4 | 5 | ||
| 5 | set(HEADERS | 6 | set(HEADERS |
diff --git a/src/tests/core/file_sys/path_parser.cpp b/src/tests/core/file_sys/path_parser.cpp new file mode 100644 index 000000000..2b543e438 --- /dev/null +++ b/src/tests/core/file_sys/path_parser.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "core/file_sys/path_parser.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | TEST_CASE("PathParser", "[core][file_sys]") { | ||
| 12 | REQUIRE(!PathParser(Path(std::vector<u8>{})).IsValid()); | ||
| 13 | REQUIRE(!PathParser(Path("a")).IsValid()); | ||
| 14 | REQUIRE(!PathParser(Path("/|")).IsValid()); | ||
| 15 | REQUIRE(PathParser(Path("/a")).IsValid()); | ||
| 16 | REQUIRE(!PathParser(Path("/a/b/../../c/../../d")).IsValid()); | ||
| 17 | REQUIRE(PathParser(Path("/a/b/../c/../../d")).IsValid()); | ||
| 18 | REQUIRE(PathParser(Path("/")).IsRootDirectory()); | ||
| 19 | REQUIRE(!PathParser(Path("/a")).IsRootDirectory()); | ||
| 20 | REQUIRE(PathParser(Path("/a/..")).IsRootDirectory()); | ||
| 21 | } | ||
| 22 | |||
| 23 | TEST_CASE("PathParser - Host file system", "[core][file_sys]") { | ||
| 24 | std::string test_dir = "./test"; | ||
| 25 | FileUtil::CreateDir(test_dir); | ||
| 26 | FileUtil::CreateDir(test_dir + "/z"); | ||
| 27 | FileUtil::CreateEmptyFile(test_dir + "/a"); | ||
| 28 | |||
| 29 | REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound); | ||
| 30 | REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound); | ||
| 31 | REQUIRE(PathParser(Path("/z")).GetHostStatus(test_dir) == PathParser::DirectoryFound); | ||
| 32 | REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath); | ||
| 33 | REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound); | ||
| 34 | |||
| 35 | FileUtil::DeleteDirRecursively(test_dir); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace FileSys | ||