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 [ + + + - : 3990514 : class path : public std::filesystem::path
+ + + + ]
[ + + + +
# # # # #
# # # # #
# # # # ]
[ + - + -
+ + + - +
+ + + + +
- + + - +
+ + - + -
+ + + - +
- + - + +
+ - - - +
- + - + -
+ - + - +
- + - + -
+ + ][ + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ][ + -
- - + - -
- + - - -
+ - - - +
- - - + -
- - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ][ + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - ]
[ + - + -
+ - - + -
- - - - -
- - - - -
- + - - +
+ - + - +
- + - + -
- + + - +
- + - ][ +
- + - + -
- + + - +
- + - - +
- + - - -
- - - + -
+ - # # #
# # # # #
# # # # #
# ][ - - +
- - - - -
+ - - + -
+ - + - -
+ - - - -
- - + + -
+ - ][ + -
- - - - +
- + - + -
+ - + + +
- + - + -
+ - + + +
- + - + -
+ - + - -
- + - + -
- - - - -
- + - + -
+ - + + ]
[ + - + +
+ - + + +
+ # # ]
[ + + ][ + +
+ - + - +
+ + - + -
+ - + - +
- ][ + + +
+ + - + +
+ + + + ]
[ + - + -
+ + - - -
- - - - -
- - - - -
- ][ + - +
- + - + +
+ - + - +
- + - + -
+ + + - +
- + - + -
+ - + - +
- + + + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ][ + -
+ - + + -
+ + - + -
+ - - + +
+ + - + -
- + + - +
- ][ + - +
- + - + +
+ - + + +
+ + + - +
+ - - + -
+ + + + -
+ - ]
34 : : {
35 : : public:
36 [ + - ][ + - : 1120 : using std::filesystem::path::path;
+ - + - ]
37 : :
38 : : // Convenience method for accessing standard path type without needing a cast.
39 [ + - + - ]: 82 : std::filesystem::path& std_path() { return *this; }
40 [ + - + - ]: 7451 : const std::filesystem::path& std_path() const { return *this; }
[ + - + - ]
41 : :
42 : : // Allow path objects arguments for compatibility.
43 : 550920 : path(std::filesystem::path path) : std::filesystem::path::path(std::move(path)) {}
44 : 645 : path& operator=(std::filesystem::path path) { std::filesystem::path::operator=(std::move(path)); return *this; }
45 [ # # ]: 1054757 : 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 [ + - ][ + - : 22434 : path(const char* c) : std::filesystem::path(c) {}
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - ]
[ - - - -
- - - - -
- - - + -
+ - # # #
# # # # #
# # ]
49 [ + - ][ - - : 8 : path& operator=(const char* c) { std::filesystem::path::operator=(c); return *this; }
- - + - +
- ]
50 [ + - ][ + - : 51802 : 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 : 1715 : std::string utf8string() const
75 : : {
76 : 1715 : const std::u8string& utf8_str{std::filesystem::path::u8string()};
77 [ - + + - ]: 3430 : return std::string{utf8_str.begin(), utf8_str.end()};
78 : 1715 : }
79 : : };
80 : :
81 : 502624 : static inline path u8path(std::string_view utf8_str)
82 : : {
83 [ + - ]: 1507872 : 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 : 11746 : static inline path absolute(const path& p)
89 : : {
90 : 11746 : return std::filesystem::absolute(p);
91 : : }
92 : :
93 : : // Disallow implicit std::string conversion for exists to avoid
94 : : // locale-dependent encoding on windows.
95 : 18858 : static inline bool exists(const path& p)
96 : : {
97 [ + - # # : 18858 : return std::filesystem::exists(p);
# # ][ + -
+ - + - ]
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - ][ +
- + - # #
# # # # #
# # # # #
# # # # #
# ]
98 : : }
99 : :
100 : : // Allow explicit quoted stream I/O.
101 : 556 : static inline auto quoted(const std::string& s)
102 : : {
103 [ + - ]: 552 : return std::quoted(s, '"', '&');
[ + - + - ]
[ - + + -
+ - - - ]
[ + - + -
- - - - +
- ][ - + +
- + - + -
+ - - + +
- ]
104 : : }
105 : :
106 : : // Allow safe path append operations.
107 : 525222 : static inline path operator/(path p1, const path& p2)
108 : : {
109 [ + - + - ]: 525222 : p1 /= p2;
[ + - ][ + -
+ - + - +
- + - +
- ][ + - +
- + - ]
110 [ + + ]: 525222 : return p1;
[ + - + - ]
111 : : }
112 : 27418 : static inline path operator/(path p1, const char* p2)
113 : : {
114 [ + - ]: 27419 : p1 /= p2;
[ + - + - ]
[ + - + -
+ - + - +
- ][ + - +
- + - + -
+ - + - +
- + - + -
+ - ][ + -
+ - + - ]
[ + - + -
+ - + - +
- + - ][ +
- + - + -
+ - # # #
# ][ + - +
- + - + -
+ - + - +
- ]
115 [ + - ][ + - : 24275 : return p1;
+ - + - +
- ][ + - +
+ + - ][ +
- + - + +
+ - + - +
- ][ + - +
- # # # #
# # # # ]
116 : : }
117 : 5659 : static inline path operator+(path p1, const char* p2)
118 : : {
119 [ + - ]: 5659 : p1 += p2;
[ + - + - ]
[ + - + -
+ - ]
120 [ + - ]: 5659 : return p1;
121 : : }
122 : 21 : static inline path operator+(path p1, path::value_type p2)
123 : : {
124 : 21 : p1 += p2;
125 : 21 : return p1;
126 : : }
127 : :
128 : : // Disallow unsafe path append operations.
129 : : template<typename T> static inline path operator/(path p1, T p2) = delete;
130 : : template<typename T> static inline path operator+(path p1, T p2) = delete;
131 : :
132 : : // Disallow implicit std::string conversion for copy_file
133 : : // to avoid locale-dependent encoding on Windows.
134 : 67 : static inline bool copy_file(const path& from, const path& to, copy_options options)
135 : : {
136 [ + - ]: 67 : return std::filesystem::copy_file(from, to, options);
137 : : }
138 : :
139 : : /**
140 : : * Convert path object to a byte string. On POSIX, paths natively are byte
141 : : * strings, so this is trivial. On Windows, paths natively are Unicode, so an
142 : : * encoding step is necessary. The inverse of \ref PathToString is \ref
143 : : * PathFromString. The strings returned and parsed by these functions can be
144 : : * used to call POSIX APIs, and for roundtrip conversion, logging, and
145 : : * debugging.
146 : : *
147 : : * Because \ref PathToString and \ref PathFromString functions don't specify an
148 : : * encoding, they are meant to be used internally, not externally. They are not
149 : : * appropriate to use in applications requiring UTF-8, where
150 : : * fs::path::u8string() / fs::path::utf8string() and fs::u8path() methods should be used instead. Other
151 : : * applications could require still different encodings. For example, JSON, XML,
152 : : * or URI applications might prefer to use higher-level escapes (\uXXXX or
153 : : * &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications
154 : : * may require encoding paths with their respective UTF-8 derivatives WTF-8,
155 : : * PEP-383, and CESU-8 (see https://en.wikipedia.org/wiki/UTF-8#Derivatives).
156 : : */
157 : 33740 : static inline std::string PathToString(const path& path)
158 : : {
159 : : // Implementation note: On Windows, the std::filesystem::path(string)
160 : : // constructor and std::filesystem::path::string() method are not safe to
161 : : // use here, because these methods encode the path using C++'s narrow
162 : : // multibyte encoding, which on Windows corresponds to the current "code
163 : : // page", which is unpredictable and typically not able to represent all
164 : : // valid paths. So fs::path::utf8string() and
165 : : // fs::u8path() functions are used instead on Windows. On
166 : : // POSIX, u8string/utf8string/u8path functions are not safe to use because paths are
167 : : // not always valid UTF-8, so plain string methods which do not transform
168 : : // the path there are used.
169 : : #ifdef WIN32
170 : : return path.utf8string();
171 : : #else
172 [ - + - - ]: 32403 : static_assert(std::is_same_v<path::string_type, std::string>, "PathToString not implemented on this platform");
[ - + ][ - +
- - - - -
+ - - ]
173 [ - + + + : 67313 : return path.std::filesystem::path::string();
- + + + -
+ + - ][ -
+ - - - +
- + + - -
+ + - - +
+ + ][ - +
- - - - -
- - - - -
- - - - -
- - - - -
- - - + -
+ - + + -
- + + - -
+ - - - -
- - - - -
- - - ][ -
+ + - - +
+ + + + +
- - - - +
+ - - - -
+ + - - -
- - - + +
- - - - +
- + + - -
- - - - +
+ - - - -
- - + + -
- + + - -
- - - ][ -
+ - + + -
- + + - -
+ + - - +
+ - - - -
- - - - -
- + + + +
+ + - - -
- - - - -
- - - - -
- - - - ]
[ - - - -
- + + - -
- - - - -
- - - - -
+ + - # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ][ - + +
+ + + + +
+ + + + +
+ + + + -
- - - - -
- - - - +
+ - - + +
- # # # #
# # ][ # #
# # # # #
# # # # #
# # # # #
# # # #
# ][ - + -
+ + - # #
# # # # #
# # # # #
# # ][ - +
+ - - + +
- - + + -
- + + - -
+ + - - +
+ - - + +
- ][ - + +
- - + - +
+ - - + +
+ # # # #
# # # # #
# # # #
# ][ - + +
- - + + -
- + + - -
+ + - - +
+ - ][ - +
+ - # # #
# # # # #
# # # # #
# ][ - + +
- - + +
- ][ - + +
- - + + -
- + + - -
+ + + + +
+ + + + +
+ ][ - + +
- - + + +
+ + + - -
+ + + + +
+ + + + +
- - - - -
- - - - -
- - - - -
- - - - -
- - + + -
- - - - -
+ + - - +
+ - - - -
- ]
174 : : #endif
175 : : }
176 : :
177 : : /**
178 : : * Convert byte string to path object. Inverse of \ref PathToString.
179 : : */
180 : 19224 : static inline path PathFromString(const std::string& string)
181 : : {
182 : : #ifdef WIN32
183 : : return u8path(string);
184 : : #else
185 : 19224 : return std::filesystem::path(string);
186 : : #endif
187 : : }
188 : :
189 : : /**
190 : : * Create directory (and if necessary its parents), unless the leaf directory
191 : : * already exists or is a symlink to an existing directory.
192 : : * This is a temporary workaround for an issue in libstdc++ that has been fixed
193 : : * upstream [PR101510].
194 : : * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101510
195 : : */
196 : 505782 : static inline bool create_directories(const std::filesystem::path& p)
197 : : {
198 [ + + - + ]: 505782 : if (std::filesystem::is_symlink(p) && std::filesystem::is_directory(p)) {
199 : : return false;
200 : : }
201 : 505744 : return std::filesystem::create_directories(p);
202 : : }
203 : :
204 : : /**
205 : : * This variant is not used. Delete it to prevent it from accidentally working
206 : : * around the workaround. If it is needed, add a workaround in the same pattern
207 : : * as above.
208 : : */
209 : : bool create_directories(const std::filesystem::path& p, std::error_code& ec) = delete;
210 : :
211 : : } // namespace fs
212 : :
213 : : /** Bridge operations to C stdio */
214 : : namespace fsbridge {
215 : : using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
216 : : FILE *fopen(const fs::path& p, const char *mode);
217 : :
218 : : /**
219 : : * Helper function for joining two paths
220 : : *
221 : : * @param[in] base Base path
222 : : * @param[in] path Path to combine with base
223 : : * @returns path unchanged if it is an absolute path, otherwise returns base joined with path. Returns base unchanged if path is empty.
224 : : * @pre Base path must be absolute
225 : : * @post Returned path will always be absolute
226 : : */
227 : : fs::path AbsPathJoin(const fs::path& base, const fs::path& path);
228 : :
229 : : class FileLock
230 : : {
231 : : public:
232 : : FileLock() = delete;
233 : : FileLock(const FileLock&) = delete;
234 : : FileLock(FileLock&&) = delete;
235 : : explicit FileLock(const fs::path& file);
236 : : ~FileLock();
237 : : bool TryLock();
238 [ - + - + ]: 8 : std::string GetReason() { return reason; }
239 : :
240 : : private:
241 : : std::string reason;
242 : : #ifndef WIN32
243 : : int fd = -1;
244 : : #else
245 : : void* hFile = (void*)-1; // INVALID_HANDLE_VALUE
246 : : #endif
247 : : };
248 : : };
249 : :
250 : : // Disallow path operator<< formatting in tinyformat to avoid locale-dependent
251 : : // encoding on windows.
252 : : namespace tinyformat {
253 : : template<> inline void formatValue(std::ostream&, const char*, const char*, int, const std::filesystem::path&) = delete;
254 : : template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
255 : : } // namespace tinyformat
256 : :
257 : : #endif // BITCOIN_UTIL_FS_H
|