mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +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/archive_systemsavedata.cpp | ||||||
|             file_sys/disk_archive.cpp |             file_sys/disk_archive.cpp | ||||||
|             file_sys/ivfc_archive.cpp |             file_sys/ivfc_archive.cpp | ||||||
|  |             file_sys/path_parser.cpp | ||||||
|             gdbstub/gdbstub.cpp |             gdbstub/gdbstub.cpp | ||||||
|             hle/config_mem.cpp |             hle/config_mem.cpp | ||||||
|             hle/hle.cpp |             hle/hle.cpp | ||||||
|  | @ -168,6 +169,7 @@ set(HEADERS | ||||||
|             file_sys/disk_archive.h |             file_sys/disk_archive.h | ||||||
|             file_sys/file_backend.h |             file_sys/file_backend.h | ||||||
|             file_sys/ivfc_archive.h |             file_sys/ivfc_archive.h | ||||||
|  |             file_sys/path_parser.h | ||||||
|             gdbstub/gdbstub.h |             gdbstub/gdbstub.h | ||||||
|             hle/config_mem.h |             hle/config_mem.h | ||||||
|             hle/function_wrappers.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 | set(SRCS | ||||||
|             tests.cpp |             tests.cpp | ||||||
|  |             core/file_sys/path_parser.cpp | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
| set(HEADERS | 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