Branch data Line data Source code
1 : : // Copyright (c) 2017-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #ifndef BITCOIN_UTIL_FS_H
6 : : #define BITCOIN_UTIL_FS_H
7 : :
8 : : #include <tinyformat.h>
9 : :
10 : : #include <cstdio>
11 : : #include <filesystem> // IWYU pragma: export
12 : : #include <functional>
13 : : #include <iomanip>
14 : : #include <ios>
15 : : #include <ostream>
16 : : #include <string>
17 : : #include <string_view>
18 : : #include <system_error>
19 : : #include <type_traits>
20 : : #include <utility>
21 : :
22 : : /** Filesystem operations and types */
23 : : namespace fs {
24 : :
25 : : using namespace std::filesystem;
26 : :
27 : : /**
28 : : * Path class wrapper to block calls to the fs::path(std::string) implicit
29 : : * constructor and the fs::path::string() method, which have unsafe and
30 : : * unpredictable behavior on Windows (see implementation note in
31 : : * \ref PathToString for details)
32 : : */
33 [ + - + - : 450651 : class path : public std::filesystem::path
+ - + + +
+ ][ + + +
+ # # # #
# # ][ + -
+ - + - -
+ + - + -
+ - - + -
+ - - - -
- - + - +
- # # #
# ][ + - +
- + - + -
+ - + - +
- + - +
- ][ + - +
- + - + -
+ - + - +
- + - ][ +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ][ + -
+ - - - -
- - + - +
- + # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ][ +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ][ + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - ]
[ + + + +
+ + + - +
+ + + ][ +
+ + - + -
+ + # # #
# # # # #
# # # # ]
[ + - - +
+ - - - -
- - - + -
+ - - - -
- ][ + + #
# # # # #
# # # # ]
[ + - - -
- - + - +
- + - + -
+ + + - +
- + - + -
+ + + - +
- + - + -
+ - - - +
- + - - -
- - - - +
- + - + -
+ + ][ + -
+ - + - +
- + - + -
+ + + + +
+ + - + -
+ - + - +
+ + - + -
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
[ + - + -
+ - - + -
- - - - -
- - - - -
- + - - +
+ - + - +
- - + + -
+ + + - +
- ][ + - +
- + + # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
34 : : {
35 : : public:
36 [ + - ][ + - : 6 : using std::filesystem::path::path;
+ - + - ]
37 : :
38 : : // Convenience method for accessing standard path type without needing a cast.
39 [ + - + - ]: 81 : std::filesystem::path& std_path() { return *this; }
40 [ + - + - ]: 229 : const std::filesystem::path& std_path() const { return *this; }
[ + - ]
41 : :
42 : : // Allow path objects arguments for compatibility.
43 : 29107 : path(std::filesystem::path path) : std::filesystem::path::path(std::move(path)) {}
44 : 658 : path& operator=(std::filesystem::path path) { std::filesystem::path::operator=(std::move(path)); return *this; }
45 [ # # ]: 51079 : path& operator/=(const std::filesystem::path& path) { std::filesystem::path::operator/=(path); return *this; }
[ + - + - ]
46 : :
47 : : // Allow literal string arguments, which are safe as long as the literals are ASCII.
48 [ + - ][ + - : 697 : path(const char* c) : std::filesystem::path(c) {}
+ - + - +
- ][ + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - ]
49 [ + - ][ # # : 7 : path& operator=(const char* c) { std::filesystem::path::operator=(c); return *this; }
# # # # #
# ]
50 [ + - ][ + - : 5908 : path& operator/=(const char* c) { std::filesystem::path::operator/=(c); return *this; }
+ - - - ]
51 : : path& append(const char* c) { std::filesystem::path::append(c); return *this; }
52 : :
53 : : // Disallow std::string arguments to avoid locale-dependent decoding on windows.
54 : : path(std::string) = delete;
55 : : path& operator=(std::string) = delete;
56 : : path& operator/=(std::string) = delete;
57 : : path& append(std::string) = delete;
58 : :
59 : : // Disallow std::string conversion method to avoid locale-dependent encoding on windows.
60 : : std::string string() const = delete;
61 : :
62 : : // Disallow implicit string conversion to ensure code is portable.
63 : : // `string_type` may be `string` or `wstring` depending on the platform, so
64 : : // using this conversion could result in code that compiles on unix but
65 : : // fails to compile on windows, or vice versa.
66 : : operator string_type() const = delete;
67 : :
68 : : /**
69 : : * Return a UTF-8 representation of the path as a std::string, for
70 : : * compatibility with code using std::string. For code using the newer
71 : : * std::u8string type, it is more efficient to call the inherited
72 : : * std::filesystem::path::u8string method instead.
73 : : */
74 : 36 : std::string utf8string() const
75 : : {
76 : 36 : const std::u8string& utf8_str{std::filesystem::path::u8string()};
77 [ - + + - ]: 72 : return std::string{utf8_str.begin(), utf8_str.end()};
78 : 36 : }
79 : : };
80 : :
81 : 24304 : static inline path u8path(std::string_view utf8_str)
82 : : {
83 [ + - ]: 72912 : return std::filesystem::path(std::u8string{utf8_str.begin(), utf8_str.end()});
84 : : }
85 : :
86 : : // Disallow implicit std::string conversion for absolute to avoid
87 : : // locale-dependent encoding on windows.
88 : 1750 : static inline path absolute(const path& p)
89 : : {
90 : 1750 : return std::filesystem::absolute(p);
91 : : }
92 : :
93 : : // Disallow implicit std::string conversion for exists to avoid
94 : : // locale-dependent encoding on windows.
95 : 884 : static inline bool exists(const path& p)
96 : : {
97 [ + - ][ # # : 884 : return std::filesystem::exists(p);
# # # # #
# ][ + - +
- # # #
# ][ + - +
- + - + -
+ - + - +
- + - + -
+ - + - ]
[ + - + -
+ - # # #
# # # # #
# # # # #
# # # ]
98 : : }
99 : 0 : static inline bool exists(const std::filesystem::file_status& s)
100 : : {
101 [ # # ]: 0 : return std::filesystem::exists(s);
102 : : }
103 : :
104 : : // Allow explicit quoted stream I/O.
105 : 2 : static inline auto quoted(const std::string& s)
106 : : {
107 [ # # # # ]: 2 : return std::quoted(s, '"', '&');
[ + - ][ # #
# # # # #
# # # # #
# # ][ - -
+ - - - -
- ][ # # #
# # # # #
# # ]
108 : : }
109 : :
110 : : // Allow safe path append operations.
111 : 25034 : static inline path operator/(path p1, const path& p2)
112 : : {
113 [ + - ]: 25034 : p1 /= p2;
[ + - + - ]
[ + - + -
+ - ][ + -
+ - + - +
- + - +
- ]
114 [ - + ]: 25034 : return p1;
[ # # # # ]
115 : : }
116 : 2550 : static inline path operator/(path p1, const char* p2)
117 : : {
118 [ + - ]: 2550 : p1 /= p2;
[ + - + - ]
[ + - + -
+ - + - +
- ][ + - +
- + - # #
# # ][ + -
+ - + - +
- + - +
- ][ + - +
- + - +
- ][ + - +
- + - + -
+ - + - +
- ][ + - +
- + - + -
+ - + - +
- + - + -
+ - ][ + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
119 [ + - ][ + - : 1679 : return p1;
+ - + - ]
[ + - + -
+ - + - ]
[ + - + -
# # # # ]
[ + - + -
+ - + - +
- + - ][ +
- + - + -
+ - + - +
- + - + -
+ - - + -
+ - + - +
- + ]
120 : : }
121 : 385 : static inline path operator+(path p1, const char* p2)
122 : : {
123 [ + - ][ + - : 385 : p1 += p2;
+ - # # ]
[ - - + -
+ - ]
124 [ + - ]: 385 : return p1;
125 : : }
126 : 21 : static inline path operator+(path p1, path::value_type p2)
127 : : {
128 : 21 : p1 += p2;
129 : 21 : return p1;
130 : : }
131 : :
132 : : // Disallow unsafe path append operations.
133 : : template<typename T> static inline path operator/(path p1, T p2) = delete;
134 : : template<typename T> static inline path operator+(path p1, T p2) = delete;
135 : :
136 : : // Disallow implicit std::string conversion for copy_file
137 : : // to avoid locale-dependent encoding on Windows.
138 : 0 : static inline bool copy_file(const path& from, const path& to, copy_options options)
139 : : {
140 [ # # ]: 0 : return std::filesystem::copy_file(from, to, options);
141 : : }
142 : :
143 : : /**
144 : : * Convert path object to a byte string. On POSIX, paths natively are byte
145 : : * strings, so this is trivial. On Windows, paths natively are Unicode, so an
146 : : * encoding step is necessary. The inverse of \ref PathToString is \ref
147 : : * PathFromString. The strings returned and parsed by these functions can be
148 : : * used to call POSIX APIs, and for roundtrip conversion, logging, and
149 : : * debugging.
150 : : *
151 : : * Because \ref PathToString and \ref PathFromString functions don't specify an
152 : : * encoding, they are meant to be used internally, not externally. They are not
153 : : * appropriate to use in applications requiring UTF-8, where
154 : : * fs::path::u8string() / fs::path::utf8string() and fs::u8path() methods should be used instead. Other
155 : : * applications could require still different encodings. For example, JSON, XML,
156 : : * or URI applications might prefer to use higher-level escapes (\uXXXX or
157 : : * &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications
158 : : * may require encoding paths with their respective UTF-8 derivatives WTF-8,
159 : : * PEP-383, and CESU-8 (see https://en.wikipedia.org/wiki/UTF-8#Derivatives).
160 : : */
161 : 4921 : static inline std::string PathToString(const path& path)
162 : : {
163 : : // Implementation note: On Windows, the std::filesystem::path(string)
164 : : // constructor and std::filesystem::path::string() method are not safe to
165 : : // use here, because these methods encode the path using C++'s narrow
166 : : // multibyte encoding, which on Windows corresponds to the current "code
167 : : // page", which is unpredictable and typically not able to represent all
168 : : // valid paths. So fs::path::utf8string() and
169 : : // fs::u8path() functions are used instead on Windows. On
170 : : // POSIX, u8string/utf8string/u8path functions are not safe to use because paths are
171 : : // not always valid UTF-8, so plain string methods which do not transform
172 : : // the path there are used.
173 : : #ifdef WIN32
174 : : return path.utf8string();
175 : : #else
176 [ # # # # ]: 4856 : static_assert(std::is_same_v<path::string_type, std::string>, "PathToString not implemented on this platform");
[ - + ][ - +
- - - - -
+ ]
177 [ # # # # : 9806 : return path.std::filesystem::path::string();
# # # # #
# # # ][ -
+ + + - +
+ + - + +
- ][ - + +
- - + + -
- + + - -
+ + - - +
+ + + + +
- ][ - + +
- - + + -
- + + - -
+ + - ][ #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
[ - + - +
+ - - + +
- - + + -
- + + - -
- - - - -
- - - + +
- - + + -
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ][ - + +
- - + - +
+ - - - -
- - + + -
- - - - -
- - - - -
- - - - -
- - + - +
+ - - + +
- - - - -
- + + - -
+ + - - -
- - ][ - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - + + -
- - - - -
- - - - -
- - - - -
- - - ][ -
+ + - - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + ][ - +
- - - - -
+ + - - +
+ - - + +
- # # #
# ][ - + +
- - + + -
- + + - -
+ + - - +
+ - ]
[ - + + - ]
[ # # # #
# # # # #
# # # # #
# # # # #
# # # ][ -
+ + - - +
+ - - + +
- - + + -
- + + - -
+ + - - +
+ - ][ # #
# # # # #
# # # # #
# # ][ - +
- + + - #
# # # # #
# # # # #
# # # # #
# # # # #
# ][ - + ]
178 : : #endif
179 : : }
180 : :
181 : : /**
182 : : * Convert byte string to path object. Inverse of \ref PathToString.
183 : : */
184 : 2848 : static inline path PathFromString(const std::string& string)
185 : : {
186 : : #ifdef WIN32
187 : : return u8path(string);
188 : : #else
189 : 2848 : return std::filesystem::path(string);
190 : : #endif
191 : : }
192 : : } // namespace fs
193 : :
194 : : /** Bridge operations to C stdio */
195 : : namespace fsbridge {
196 : : using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
197 : : FILE *fopen(const fs::path& p, const char *mode);
198 : :
199 : : /**
200 : : * Helper function for joining two paths
201 : : *
202 : : * @param[in] base Base path
203 : : * @param[in] path Path to combine with base
204 : : * @returns path unchanged if it is an absolute path, otherwise returns base joined with path. Returns base unchanged if path is empty.
205 : : * @pre Base path must be absolute
206 : : * @post Returned path will always be absolute
207 : : */
208 : : fs::path AbsPathJoin(const fs::path& base, const fs::path& path);
209 : :
210 : : class FileLock
211 : : {
212 : : public:
213 : : FileLock() = delete;
214 : : FileLock(const FileLock&) = delete;
215 : : FileLock(FileLock&&) = delete;
216 : : explicit FileLock(const fs::path& file);
217 : : ~FileLock();
218 : : bool TryLock();
219 [ - + - + ]: 4 : std::string GetReason() { return reason; }
220 : :
221 : : private:
222 : : std::string reason;
223 : : #ifndef WIN32
224 : : int fd = -1;
225 : : #else
226 : : void* hFile = (void*)-1; // INVALID_HANDLE_VALUE
227 : : #endif
228 : : };
229 : : };
230 : :
231 : : // Disallow path operator<< formatting in tinyformat to avoid locale-dependent
232 : : // encoding on windows.
233 : : namespace tinyformat {
234 : : template<> inline void formatValue(std::ostream&, const char*, const char*, int, const std::filesystem::path&) = delete;
235 : : template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
236 : : } // namespace tinyformat
237 : :
238 : : #endif // BITCOIN_UTIL_FS_H
|