summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar wwylele2016-10-16 22:19:13 +0800
committerGravatar wwylele2016-11-19 17:17:19 +0200
commit75ee2f8c67022b0731e438b34fa65216531eccd1 (patch)
treedf4293ddc6a1712e102a7f2d8d71205364b02d6b /src
parentFileSys: make Archive interfaces return error code (diff)
downloadyuzu-75ee2f8c67022b0731e438b34fa65216531eccd1.tar.gz
yuzu-75ee2f8c67022b0731e438b34fa65216531eccd1.tar.xz
yuzu-75ee2f8c67022b0731e438b34fa65216531eccd1.zip
FileSys: add PathParser
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/file_sys/path_parser.cpp98
-rw-r--r--src/core/file_sys/path_parser.h61
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/core/file_sys/path_parser.cpp38
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
11namespace FileSys {
12
13PathParser::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
60PathParser::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
88std::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
11namespace 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 */
18class PathParser {
19public:
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
55private:
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 @@
1set(SRCS 1set(SRCS
2 tests.cpp 2 tests.cpp
3 core/file_sys/path_parser.cpp
3 ) 4 )
4 5
5set(HEADERS 6set(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
9namespace FileSys {
10
11TEST_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
23TEST_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