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