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