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