mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	FileSys: add PathParser
This commit is contained in:
		
							parent
							
								
									4dd8a831bd
								
							
						
					
					
						commit
						75ee2f8c67
					
				
					 5 changed files with 200 additions and 0 deletions
				
			
		|  | @ -24,6 +24,7 @@ set(SRCS | |||
|             file_sys/archive_systemsavedata.cpp | ||||
|             file_sys/disk_archive.cpp | ||||
|             file_sys/ivfc_archive.cpp | ||||
|             file_sys/path_parser.cpp | ||||
|             gdbstub/gdbstub.cpp | ||||
|             hle/config_mem.cpp | ||||
|             hle/hle.cpp | ||||
|  | @ -168,6 +169,7 @@ set(HEADERS | |||
|             file_sys/disk_archive.h | ||||
|             file_sys/file_backend.h | ||||
|             file_sys/ivfc_archive.h | ||||
|             file_sys/path_parser.h | ||||
|             gdbstub/gdbstub.h | ||||
|             hle/config_mem.h | ||||
|             hle/function_wrappers.h | ||||
|  |  | |||
							
								
								
									
										98
									
								
								src/core/file_sys/path_parser.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/core/file_sys/path_parser.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <set> | ||||
| #include "common/file_util.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/file_sys/path_parser.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| PathParser::PathParser(const Path& path) { | ||||
|     if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) { | ||||
|         is_valid = false; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto path_string = path.AsString(); | ||||
|     if (path_string.size() == 0 || path_string[0] != '/') { | ||||
|         is_valid = false; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Filter out invalid characters for the host system.
 | ||||
|     // Although some of these characters are valid on 3DS, they are unlikely to be used by games.
 | ||||
|     if (std::find_if(path_string.begin(), path_string.end(), [](char c) { | ||||
|             static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'}; | ||||
|             return invalid_chars.find(c) != invalid_chars.end(); | ||||
|         }) != path_string.end()) { | ||||
|         is_valid = false; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Common::SplitString(path_string, '/', path_sequence); | ||||
| 
 | ||||
|     auto begin = path_sequence.begin(); | ||||
|     auto end = path_sequence.end(); | ||||
|     end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; }); | ||||
|     path_sequence = std::vector<std::string>(begin, end); | ||||
| 
 | ||||
|     // checks if the path is out of bounds.
 | ||||
|     int level = 0; | ||||
|     for (auto& node : path_sequence) { | ||||
|         if (node == "..") { | ||||
|             --level; | ||||
|             if (level < 0) { | ||||
|                 is_valid = false; | ||||
|                 return; | ||||
|             } | ||||
|         } else { | ||||
|             ++level; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     is_valid = true; | ||||
|     is_root = level == 0; | ||||
| } | ||||
| 
 | ||||
| PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const { | ||||
|     auto path = mount_point; | ||||
|     if (!FileUtil::IsDirectory(path)) | ||||
|         return InvalidMountPoint; | ||||
|     if (path_sequence.empty()) { | ||||
|         return DirectoryFound; | ||||
|     } | ||||
| 
 | ||||
|     for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) { | ||||
|         if (path.back() != '/') | ||||
|             path += '/'; | ||||
|         path += *iter; | ||||
| 
 | ||||
|         if (!FileUtil::Exists(path)) | ||||
|             return PathNotFound; | ||||
|         if (FileUtil::IsDirectory(path)) | ||||
|             continue; | ||||
|         return FileInPath; | ||||
|     } | ||||
| 
 | ||||
|     path += "/" + path_sequence.back(); | ||||
|     if (!FileUtil::Exists(path)) | ||||
|         return NotFound; | ||||
|     if (FileUtil::IsDirectory(path)) | ||||
|         return DirectoryFound; | ||||
|     return FileFound; | ||||
| } | ||||
| 
 | ||||
| std::string PathParser::BuildHostPath(const std::string& mount_point) const { | ||||
|     std::string path = mount_point; | ||||
|     for (auto& node : path_sequence) { | ||||
|         if (path.back() != '/') | ||||
|             path += '/'; | ||||
|         path += node; | ||||
|     } | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
							
								
								
									
										61
									
								
								src/core/file_sys/path_parser.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/core/file_sys/path_parser.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "core/file_sys/archive_backend.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| /**
 | ||||
|  * A helper class parsing and verifying a string-type Path. | ||||
|  * Every archives with a sub file system should use this class to parse the path argument and check | ||||
|  * the status of the file / directory in question on the host file system. | ||||
|  */ | ||||
| class PathParser { | ||||
| public: | ||||
|     PathParser(const Path& path); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Checks if the Path is valid. | ||||
|      * This function should be called once a PathParser is constructed. | ||||
|      * A Path is valid if: | ||||
|      *  - it is a string path (with type LowPathType::Char or LowPathType::Wchar), | ||||
|      *  - it starts with "/" (this seems a hard requirement in real 3DS), | ||||
|      *  - it doesn't contain invalid characters, and | ||||
|      *  - it doesn't go out of the root directory using "..". | ||||
|      */ | ||||
|     bool IsValid() const { | ||||
|         return is_valid; | ||||
|     } | ||||
| 
 | ||||
|     /// Checks if the Path represents the root directory.
 | ||||
|     bool IsRootDirectory() const { | ||||
|         return is_root; | ||||
|     } | ||||
| 
 | ||||
|     enum HostStatus { | ||||
|         InvalidMountPoint, | ||||
|         PathNotFound,   // "/a/b/c" when "a" doesn't exist
 | ||||
|         FileInPath,     // "/a/b/c" when "a" is a file
 | ||||
|         FileFound,      // "/a/b/c" when "c" is a file
 | ||||
|         DirectoryFound, // "/a/b/c" when "c" is a directory
 | ||||
|         NotFound        // "/a/b/c" when "a/b/" exists but "c" doesn't exist
 | ||||
|     }; | ||||
| 
 | ||||
|     /// Checks the status of the specified file / directory by the Path on the host file system.
 | ||||
|     HostStatus GetHostStatus(const std::string& mount_point) const; | ||||
| 
 | ||||
|     /// Builds a full path on the host file system.
 | ||||
|     std::string BuildHostPath(const std::string& mount_point) const; | ||||
| 
 | ||||
| private: | ||||
|     std::vector<std::string> path_sequence; | ||||
|     bool is_valid{}; | ||||
|     bool is_root{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | @ -1,5 +1,6 @@ | |||
| set(SRCS | ||||
|             tests.cpp | ||||
|             core/file_sys/path_parser.cpp | ||||
|             ) | ||||
| 
 | ||||
| set(HEADERS | ||||
|  |  | |||
							
								
								
									
										38
									
								
								src/tests/core/file_sys/path_parser.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/tests/core/file_sys/path_parser.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <catch.hpp> | ||||
| #include "common/file_util.h" | ||||
| #include "core/file_sys/path_parser.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| TEST_CASE("PathParser", "[core][file_sys]") { | ||||
|     REQUIRE(!PathParser(Path(std::vector<u8>{})).IsValid()); | ||||
|     REQUIRE(!PathParser(Path("a")).IsValid()); | ||||
|     REQUIRE(!PathParser(Path("/|")).IsValid()); | ||||
|     REQUIRE(PathParser(Path("/a")).IsValid()); | ||||
|     REQUIRE(!PathParser(Path("/a/b/../../c/../../d")).IsValid()); | ||||
|     REQUIRE(PathParser(Path("/a/b/../c/../../d")).IsValid()); | ||||
|     REQUIRE(PathParser(Path("/")).IsRootDirectory()); | ||||
|     REQUIRE(!PathParser(Path("/a")).IsRootDirectory()); | ||||
|     REQUIRE(PathParser(Path("/a/..")).IsRootDirectory()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("PathParser - Host file system", "[core][file_sys]") { | ||||
|     std::string test_dir = "./test"; | ||||
|     FileUtil::CreateDir(test_dir); | ||||
|     FileUtil::CreateDir(test_dir + "/z"); | ||||
|     FileUtil::CreateEmptyFile(test_dir + "/a"); | ||||
| 
 | ||||
|     REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound); | ||||
|     REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound); | ||||
|     REQUIRE(PathParser(Path("/z")).GetHostStatus(test_dir) == PathParser::DirectoryFound); | ||||
|     REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath); | ||||
|     REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound); | ||||
| 
 | ||||
|     FileUtil::DeleteDirRecursively(test_dir); | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue