From 28b96a071d1630ac7b2c5266009d8c1e979e3897 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:42:48 +0000 Subject: [PATCH 01/18] merge bitcoin#22840: fix unoptimized libraries in depends --- depends/packages/bdb.mk | 2 +- depends/packages/boost.mk | 2 +- depends/packages/zeromq.mk | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 34180e571f..dc536fd399 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -15,7 +15,7 @@ $(package)_config_opts_netbsd=--with-pic $(package)_config_opts_openbsd=--with-pic $(package)_config_opts_android=--with-pic $(package)_cflags+=-Wno-error=implicit-function-declaration -$(package)_cxxflags=-std=c++17 +$(package)_cxxflags+=-std=c++17 $(package)_cppflags_mingw32=-DUNICODE -D_UNICODE endef diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 77ada65e0f..2dc955b99f 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -27,7 +27,7 @@ else $(package)_toolset_$(host_os)=gcc endif $(package)_config_libraries=filesystem,test -$(package)_cxxflags=-std=c++11 +$(package)_cxxflags+=-std=c++11 $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_freebsd=-fPIC $(package)_cxxflags_openbsd=-fPIC diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index b5cca1dae8..c74ae15b31 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -16,7 +16,7 @@ define $(package)_set_vars $(package)_config_opts_netbsd=--with-pic $(package)_config_opts_openbsd=--with-pic $(package)_config_opts_android=--with-pic - $(package)_cxxflags=-std=c++17 + $(package)_cxxflags+=-std=c++17 endef define $(package)_preprocess_cmds From 23fe7e2f07c0d9caa6d0ddb6a330971bf479f261 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:51:10 +0000 Subject: [PATCH 02/18] chore: dashify symbols in some unit tests --- src/test/dbwrapper_tests.cpp | 2 +- src/test/fs_tests.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index b6f5938892..23dfad87dc 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(unicodepath) // On Windows this test will fail if the directory is created using // the ANSI CreateDirectoryA call and the code page isn't UTF8. // It will succeed if created with CreateDirectoryW. - fs::path ph = m_args.GetDataDirBase() / "test_runner_₿_🏃_20191128_104644"; + fs::path ph = m_args.GetDataDirBase() / "test_runner_∋_🏃_20191128_104644"; CDBWrapper dbw(ph, (1 << 20)); fs::path lockPath = ph / "LOCK"; diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp index 3f12830fc9..2e47207ed5 100644 --- a/src/test/fs_tests.cpp +++ b/src/test/fs_tests.cpp @@ -15,8 +15,8 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream) { fs::path tmpfolder = m_args.GetDataDirBase(); // tmpfile1 should be the same as tmpfile2 - fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃"; - fs::path tmpfile2 = tmpfolder / "fs_tests_₿_🏃"; + fs::path tmpfile1 = tmpfolder / "fs_tests_∋_🏃"; + fs::path tmpfile2 = tmpfolder / "fs_tests_∋_🏃"; { fsbridge::ofstream file(tmpfile1); file << "bitcoin"; @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream) } { // Join an absolute path and a relative path. - fs::path p = fsbridge::AbsPathJoin(tmpfolder, "fs_tests_₿_🏃"); + fs::path p = fsbridge::AbsPathJoin(tmpfolder, "fs_tests_∋_🏃"); BOOST_CHECK(p.is_absolute()); BOOST_CHECK_EQUAL(tmpfile1, p); } From ecfac10b8e4265f2dd234241afcfcbcf0857c54d Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:39:26 +0000 Subject: [PATCH 03/18] merge bitcoin#22937: Forbid calling unsafe fs::path(std::string) constructor and fs::path::string() method --- src/addrdb.cpp | 20 ++-- src/bitcoin-cli.cpp | 2 +- src/dbwrapper.cpp | 18 ++-- src/flat-database.h | 8 +- src/flatfile.cpp | 4 +- src/fs.cpp | 8 +- src/fs.h | 132 +++++++++++++++++++++++++- src/i2p.cpp | 2 +- src/init.cpp | 34 +++---- src/init/common.cpp | 14 +-- src/node/blockstorage.cpp | 15 +-- src/policy/fees.cpp | 4 +- src/qt/bitcoin.cpp | 6 +- src/qt/guiutil.cpp | 4 +- src/qt/intro.cpp | 2 +- src/qt/rpcconsole.cpp | 4 +- src/rpc/blockchain.cpp | 8 +- src/rpc/request.cpp | 8 +- src/rpc/server.cpp | 2 +- src/stacktraces.cpp | 4 +- src/test/fs_tests.cpp | 27 ++++++ src/test/fuzz/banman.cpp | 4 +- src/test/settings_tests.cpp | 6 +- src/test/util/chainstate.h | 2 +- src/test/util/setup_common.cpp | 4 +- src/test/util_tests.cpp | 14 +-- src/torcontrol.cpp | 8 +- src/util/asmap.cpp | 4 +- src/util/settings.cpp | 16 ++-- src/util/system.cpp | 33 +++---- src/wallet/bdb.cpp | 38 ++++---- src/wallet/bdb.h | 4 +- src/wallet/db.cpp | 15 +-- src/wallet/dump.cpp | 10 +- src/wallet/interfaces.cpp | 4 +- src/wallet/load.cpp | 16 ++-- src/wallet/rpcdump.cpp | 8 +- src/wallet/rpcwallet.cpp | 6 +- src/wallet/sqlite.cpp | 4 +- src/wallet/test/db_tests.cpp | 8 +- src/wallet/test/init_test_fixture.cpp | 8 +- src/wallet/test/init_tests.cpp | 8 +- src/wallet/test/wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 32 +++---- src/wallet/walletdb.cpp | 16 ++-- src/wallet/wallettool.cpp | 2 +- src/wallet/walletutil.cpp | 2 +- 47 files changed, 376 insertions(+), 224 deletions(-) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index c167c762a2..dbca156cb8 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -58,7 +58,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data if (fileout.IsNull()) { fileout.fclose(); remove(pathTmp); - return error("%s: Failed to open file %s", __func__, pathTmp.string()); + return error("%s: Failed to open file %s", __func__, fs::PathToString(pathTmp)); } // Serialize @@ -70,7 +70,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data if (!FileCommit(fileout.Get())) { fileout.fclose(); remove(pathTmp); - return error("%s: Failed to flush file %s", __func__, pathTmp.string()); + return error("%s: Failed to flush file %s", __func__, fs::PathToString(pathTmp)); } fileout.fclose(); @@ -122,8 +122,8 @@ void DeserializeFileDB(const fs::path& path, Data& data, int version) } // namespace CBanDB::CBanDB(fs::path ban_list_path) - : m_banlist_dat(ban_list_path.string() + ".dat"), - m_banlist_json(ban_list_path.string() + ".json") + : m_banlist_dat(ban_list_path + ".dat"), + m_banlist_json(ban_list_path + ".json") { } @@ -143,7 +143,7 @@ bool CBanDB::Write(const banmap_t& banSet) bool CBanDB::Read(banmap_t& banSet) { if (fs::exists(m_banlist_dat)) { - LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 19.x. Remove %s to silence this warning.\n", m_banlist_dat); + LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 19.x. Remove %s to silence this warning.\n", fs::quoted(fs::PathToString(m_banlist_dat))); } // If the JSON banlist does not exist, then recreate it if (!fs::exists(m_banlist_json)) { @@ -155,7 +155,7 @@ bool CBanDB::Read(banmap_t& banSet) if (!util::ReadSettings(m_banlist_json, settings, errors)) { for (const auto& err : errors) { - LogPrintf("Cannot load banlist %s: %s\n", m_banlist_json.string(), err); + LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err); } return false; } @@ -163,7 +163,7 @@ bool CBanDB::Read(banmap_t& banSet) try { BanMapFromJson(settings[JSON_KEY], banSet); } catch (const std::runtime_error& e) { - LogPrintf("Cannot parse banlist %s: %s\n", m_banlist_json.string(), e.what()); + LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what()); return false; } @@ -194,7 +194,7 @@ std::optional LoadAddrman(const std::vector& asmap, const A } catch (const DbNotFoundError&) { // Addrman can be in an inconsistent state after failure, reset it addrman = std::make_unique(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); - LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr); + LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr))); DumpPeerAddresses(args, *addrman); } catch (const DbInconsistentError& e) { // Addrman has shown a tendency to corrupt itself even with graceful shutdowns on known-good @@ -208,7 +208,7 @@ std::optional LoadAddrman(const std::vector& asmap, const A } catch (const std::exception& e) { addrman = nullptr; return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."), - e.what(), PACKAGE_BUGREPORT, path_addr); + e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr))); } return std::nullopt; } @@ -224,7 +224,7 @@ std::vector ReadAnchors(const fs::path& anchors_db_path) std::vector anchors; try { DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT); - LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename()); + LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename()))); } catch (const std::exception&) { anchors.clear(); } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 2b5a77c73f..1c2d4c71b4 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -818,7 +818,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co if (failedToGetAuthCookie) { throw std::runtime_error(strprintf( "Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)", - GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string())); + fs::PathToString(GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME))))); } else { throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword"); } diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index e680e9099c..77ecd87845 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -116,7 +116,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) } CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate) - : m_name{path.stem().string()} + : m_name{fs::PathToString(path.stem())} { penv = nullptr; readoptions.verify_checksums = true; @@ -130,21 +130,21 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo options.env = penv; } else { if (fWipe) { - LogPrintf("Wiping LevelDB in %s\n", path.string()); - leveldb::Status result = leveldb::DestroyDB(path.string(), options); + LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(path)); + leveldb::Status result = leveldb::DestroyDB(fs::PathToString(path), options); dbwrapper_private::HandleError(result); } TryCreateDirectories(path); - LogPrintf("Opening LevelDB in %s\n", path.string()); + LogPrintf("Opening LevelDB in %s\n", fs::PathToString(path)); } - leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); + leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(path), &pdb); dbwrapper_private::HandleError(status); LogPrintf("Opened LevelDB successfully\n"); if (gArgs.GetBoolArg("-forcecompactdb", false)) { - LogPrintf("Starting database compaction of %s\n", path.string()); + LogPrintf("Starting database compaction of %s\n", fs::PathToString(path)); pdb->CompactRange(nullptr, nullptr); - LogPrintf("Finished database compaction of %s\n", path.string()); + LogPrintf("Finished database compaction of %s\n", fs::PathToString(path)); } // The base-case obfuscation key, which is a noop. @@ -161,10 +161,10 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo Write(OBFUSCATE_KEY_KEY, new_key); obfuscate_key = new_key; - LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key)); + LogPrintf("Wrote new obfuscate key for %s: %s\n", fs::PathToString(path), HexStr(obfuscate_key)); } - LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key)); + LogPrintf("Using obfuscation key for %s: %s\n", fs::PathToString(path), HexStr(obfuscate_key)); } CDBWrapper::~CDBWrapper() diff --git a/src/flat-database.h b/src/flat-database.h index 47e85abed9..8ad452c554 100644 --- a/src/flat-database.h +++ b/src/flat-database.h @@ -51,10 +51,10 @@ class CFlatDB ssObj << hash; // open output file, and associate with CAutoFile - FILE *file = fopen(pathDB.string().c_str(), "wb"); + FILE *file = fsbridge::fopen(pathDB, "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathDB.string()); + return error("%s: Failed to open file %s", __func__, fs::PathToString(pathDB)); // Write and commit header, data try { @@ -77,11 +77,11 @@ class CFlatDB int64_t nStart = GetTimeMillis(); // open input file, and associate with CAutoFile - FILE *file = fopen(pathDB.string().c_str(), "rb"); + FILE *file = fsbridge::fopen(pathDB, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { - error("%s: Failed to open file %s", __func__, pathDB.string()); + error("%s: Failed to open file %s", __func__, fs::PathToString(pathDB)); return FileError; } diff --git a/src/flatfile.cpp b/src/flatfile.cpp index 151f1a38f1..929808c7fa 100644 --- a/src/flatfile.cpp +++ b/src/flatfile.cpp @@ -41,11 +41,11 @@ FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only) if (!file && !read_only) file = fsbridge::fopen(path, "wb+"); if (!file) { - LogPrintf("Unable to open file %s\n", path.string()); + LogPrintf("Unable to open file %s\n", fs::PathToString(path)); return nullptr; } if (pos.nPos && fseek(file, pos.nPos, SEEK_SET)) { - LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); + LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, fs::PathToString(path)); fclose(file); return nullptr; } diff --git a/src/fs.cpp b/src/fs.cpp index 10b5416054..160be41f2b 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -25,7 +25,7 @@ namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode) { #ifndef WIN32 - return ::fopen(p.string().c_str(), mode); + return ::fopen(p.c_str(), mode); #else std::wstring_convert,wchar_t> utf8_cvt; return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str()); @@ -47,7 +47,7 @@ static std::string GetErrorReason() FileLock::FileLock(const fs::path& file) { - fd = open(file.string().c_str(), O_RDWR); + fd = open(file.c_str(), O_RDWR); if (fd == -1) { reason = GetErrorReason(); } @@ -244,9 +244,9 @@ void ofstream::close() #else // __GLIBCXX__ #if BOOST_VERSION >= 107700 -static_assert(sizeof(*BOOST_FILESYSTEM_C_STR(fs::path())) == sizeof(wchar_t), +static_assert(sizeof(*BOOST_FILESYSTEM_C_STR(boost::filesystem::path())) == sizeof(wchar_t), #else -static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t), +static_assert(sizeof(*boost::filesystem::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t), #endif // BOOST_VERSION >= 107700 "Warning: This build is using boost::filesystem ofstream and ifstream " "implementations which will fail to open paths containing multibyte " diff --git a/src/fs.h b/src/fs.h index d77b90be66..4a0bf39e95 100644 --- a/src/fs.h +++ b/src/fs.h @@ -13,9 +13,132 @@ #include #include +#include /** Filesystem operations and types */ -namespace fs = boost::filesystem; +namespace fs { + +using namespace boost::filesystem; + +/** + * Path class wrapper to prepare application code for transition from + * boost::filesystem library to std::filesystem implementation. The main + * purpose of the class is to define fs::path::u8string() and fs::u8path() + * functions not present in boost. It also blocks calls to the + * fs::path(std::string) implicit constructor and the fs::path::string() + * method, which worked well in the boost::filesystem implementation, but have + * unsafe and unpredictable behavior on Windows in the std::filesystem + * implementation (see implementation note in \ref PathToString for details). + */ +class path : public boost::filesystem::path +{ +public: + using boost::filesystem::path::path; + + // Allow path objects arguments for compatibility. + path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {} + path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; } + path& operator/=(boost::filesystem::path path) { boost::filesystem::path::operator/=(std::move(path)); return *this; } + + // Allow literal string arguments, which are safe as long as the literals are ASCII. + path(const char* c) : boost::filesystem::path(c) {} + path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; } + path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; } + path& append(const char* c) { boost::filesystem::path::append(c); return *this; } + + // Disallow std::string arguments to avoid locale-dependent decoding on windows. + path(std::string) = delete; + path& operator=(std::string) = delete; + path& operator/=(std::string) = delete; + path& append(std::string) = delete; + + // Disallow std::string conversion method to avoid locale-dependent encoding on windows. + std::string string() const = delete; + + // Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem. + std::string u8string() const { return boost::filesystem::path::string(); } +}; + +// Define UTF-8 string conversion function not present in boost::filesystem but present in std::filesystem. +static inline path u8path(const std::string& string) +{ + return boost::filesystem::path(string); +} + +// Disallow implicit std::string conversion for system_complete to avoid +// locale-dependent encoding on windows. +static inline path system_complete(const path& p) +{ + return boost::filesystem::system_complete(p); +} + +// Disallow implicit std::string conversion for exists to avoid +// locale-dependent encoding on windows. +static inline bool exists(const path& p) +{ + return boost::filesystem::exists(p); +} + +// Allow explicit quoted stream I/O. +static inline auto quoted(const std::string& s) +{ + return boost::io::quoted(s, '&'); +} + +// Allow safe path append operations. +static inline path operator+(path p1, path p2) +{ + p1 += std::move(p2); + return p1; +} + +/** + * Convert path object to byte string. On POSIX, paths natively are byte + * strings so this is trivial. On Windows, paths natively are Unicode, so an + * encoding step is necessary. + * + * The inverse of \ref PathToString is \ref PathFromString. The strings + * returned and parsed by these functions can be used to call POSIX APIs, and + * for roundtrip conversion, logging, and debugging. But they are not + * guaranteed to be valid UTF-8, and are generally meant to be used internally, + * not externally. When communicating with external programs and libraries that + * require UTF-8, fs::path::u8string() and fs::u8path() methods can be used. + * For other applications, if support for non UTF-8 paths is required, or if + * higher-level JSON or XML or URI or C-style escapes are preferred, it may be + * also be appropriate to use different path encoding functions. + * + * Implementation note: On Windows, the std::filesystem::path(string) + * constructor and std::filesystem::path::string() method are not safe to use + * here, because these methods encode the path using C++'s narrow multibyte + * encoding, which on Windows corresponds to the current "code page", which is + * unpredictable and typically not able to represent all valid paths. So + * std::filesystem::path::u8string() and std::filesystem::u8path() functions + * are used instead on Windows. On POSIX, u8string/u8path functions are not + * safe to use because paths are not always valid UTF-8, so plain string + * methods which do not transform the path there are used. + */ +static inline std::string PathToString(const path& path) +{ +#ifdef WIN32 + return path.u8string(); +#else + static_assert(std::is_same::value, "PathToString not implemented on this platform"); + return path.boost::filesystem::path::string(); +#endif +} + +/** + * Convert byte string to path object. Inverse of \ref PathToString. + */ +static inline path PathFromString(const std::string& string) +{ +#ifdef WIN32 + return u8path(string); +#else + return boost::filesystem::path(string); +#endif +} +} // namespace fs /** Bridge operations to C stdio */ namespace fsbridge { @@ -103,4 +226,11 @@ namespace fsbridge { #endif // WIN32 && __GLIBCXX__ }; +// Disallow path operator<< formatting in tinyformat to avoid locale-dependent +// encoding on windows. +namespace tinyformat { +template<> inline void formatValue(std::ostream&, const char*, const char*, int, const boost::filesystem::path&) = delete; +template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete; +} // namespace tinyformat + #endif // BITCOIN_FS_H diff --git a/src/i2p.cpp b/src/i2p.cpp index 8dfab826ef..fbb15f7260 100644 --- a/src/i2p.cpp +++ b/src/i2p.cpp @@ -340,7 +340,7 @@ void Session::GenerateAndSavePrivateKey(const Sock& sock) if (!WriteBinaryFile(m_private_key_file, std::string(m_private_key.begin(), m_private_key.end()))) { throw std::runtime_error( - strprintf("Cannot save I2P private key to %s", m_private_key_file)); + strprintf("Cannot save I2P private key to %s", fs::quoted(fs::PathToString(m_private_key_file)))); } } diff --git a/src/init.cpp b/src/init.cpp index 1189c0c6ca..6c69b4a4f9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -154,7 +154,7 @@ static const char* BITCOIN_PID_FILENAME = "dashd.pid"; static fs::path GetPidFile(const ArgsManager& args) { - return AbsPathForConfigVal(fs::path(args.GetArg("-pid", BITCOIN_PID_FILENAME))); + return AbsPathForConfigVal(fs::PathFromString(args.GetArg("-pid", BITCOIN_PID_FILENAME))); } [[nodiscard]] static bool CreatePidFile(const ArgsManager& args) @@ -168,7 +168,7 @@ static fs::path GetPidFile(const ArgsManager& args) #endif return true; } else { - return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile(args).string(), std::strerror(errno))); + return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), std::strerror(errno))); } } @@ -1369,10 +1369,10 @@ static bool LockDataDirectory(bool probeOnly) // Make sure only a single Dash Core process is using the data directory. fs::path datadir = gArgs.GetDataDirNet(); if (!DirIsWritable(datadir)) { - return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string())); + return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), fs::PathToString(datadir))); } if (!LockDirectory(datadir, ".lock", probeOnly)) { - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), PACKAGE_NAME)); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), fs::PathToString(datadir), PACKAGE_NAME)); } return true; } @@ -1433,12 +1433,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. - if (args.IsArgSet("-datadir") && !fs::path(args.GetArg("-datadir", "")).is_absolute()) { + if (args.IsArgSet("-datadir") && !fs::PathFromString(args.GetArg("-datadir", "")).is_absolute()) { LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */ "current working directory '%s'. This is fragile, because if Dash Core is started in the future " "from a different location, it will be unable to locate the current data files. There could " "also be data loss if Dash Core is started while in a temporary directory.\n", - args.GetArg("-datadir", ""), fs::current_path().string()); + args.GetArg("-datadir", ""), fs::PathToString(fs::current_path())); } InitSignatureCache(); @@ -1526,20 +1526,20 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Read asmap file if configured std::vector asmap; if (args.IsArgSet("-asmap")) { - fs::path asmap_path = fs::path(args.GetArg("-asmap", "")); + fs::path asmap_path = fs::PathFromString(args.GetArg("-asmap", "")); if (asmap_path.empty()) { - asmap_path = DEFAULT_ASMAP_FILENAME; + asmap_path = fs::PathFromString(DEFAULT_ASMAP_FILENAME); } if (!asmap_path.is_absolute()) { asmap_path = gArgs.GetDataDirNet() / asmap_path; } if (!fs::exists(asmap_path)) { - InitError(strprintf(_("Could not find asmap file %s"), asmap_path)); + InitError(strprintf(_("Could not find asmap file %s"), fs::quoted(fs::PathToString(asmap_path)))); return false; } asmap = DecodeAsmap(asmap_path); if (asmap.size() == 0) { - InitError(strprintf(_("Could not parse asmap file %s"), asmap_path)); + InitError(strprintf(_("Could not parse asmap file %s"), fs::quoted(fs::PathToString(asmap_path)))); return false; } const uint256 asmap_version = SerializeHash(asmap); @@ -1773,7 +1773,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7a: Load sporks if (!node.sporkman->LoadCache()) { - auto file_path = (gArgs.GetDataDirNet() / "sporks.dat").string(); + auto file_path = fs::PathToString(gArgs.GetDataDirNet() / "sporks.dat"); return InitError(strprintf(_("Failed to load sporks cache from %s"), file_path)); } @@ -2128,7 +2128,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) bool fLoadCacheFiles = !(fReindex || fReindexChainState) && (chainman.ActiveChain().Tip() != nullptr); if (!node.netfulfilledman->LoadCache(fLoadCacheFiles)) { - auto file_path = (gArgs.GetDataDirNet() / "netfulfilled.dat").string(); + auto file_path = fs::PathToString(gArgs.GetDataDirNet() / "netfulfilled.dat"); if (fLoadCacheFiles) { return InitError(strprintf(_("Failed to load fulfilled requests cache from %s"), file_path)); } @@ -2136,7 +2136,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } if (!node.mn_metaman->LoadCache(fLoadCacheFiles)) { - auto file_path = (gArgs.GetDataDirNet() / "mncache.dat").string(); + auto file_path = fs::PathToString(gArgs.GetDataDirNet() / "mncache.dat"); if (fLoadCacheFiles) { return InitError(strprintf(_("Failed to load masternode cache from %s"), file_path)); } @@ -2145,7 +2145,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (is_governance_enabled) { if (!node.govman->LoadCache(fLoadCacheFiles)) { - auto file_path = (gArgs.GetDataDirNet() / "governance.dat").string(); + auto file_path = fs::PathToString(gArgs.GetDataDirNet() / "governance.dat"); if (fLoadCacheFiles) { return InitError(strprintf(_("Failed to load governance cache from %s"), file_path)); } @@ -2243,11 +2243,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 11: import blocks if (!CheckDiskSpace(gArgs.GetDataDirNet())) { - InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetDataDirNet())); + InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(gArgs.GetDataDirNet())))); return false; } if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) { - InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetBlocksDirPath())); + InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(gArgs.GetBlocksDirPath())))); return false; } @@ -2284,7 +2284,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) std::vector vImportFiles; for (const std::string& strFile : args.GetArgs("-loadblock")) { - vImportFiles.push_back(strFile); + vImportFiles.push_back(fs::PathFromString(strFile)); } chainman.m_load_block = std::thread(&util::TraceThread, "loadblk", [=, &args, &chainman, &node] { diff --git a/src/init/common.cpp b/src/init/common.cpp index 7da776f029..6d16776964 100644 --- a/src/init/common.cpp +++ b/src/init/common.cpp @@ -78,7 +78,7 @@ void AddLoggingArgs(ArgsManager& argsman) void SetLoggingOptions(const ArgsManager& args) { LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile"); - LogInstance().m_file_path = AbsPathForConfigVal(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + LogInstance().m_file_path = AbsPathForConfigVal(fs::PathFromString(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE))); LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false)); LogInstance().m_log_timestamps = args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); LogInstance().m_log_time_micros = args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); @@ -125,24 +125,24 @@ bool StartLogging(const ArgsManager& args) } if (!LogInstance().StartLogging()) { return InitError(strprintf(Untranslated("Could not open debug log file %s"), - LogInstance().m_file_path.string())); + fs::PathToString(LogInstance().m_file_path))); } if (!LogInstance().m_log_timestamps) LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); - LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); - LogPrintf("Using data directory %s\n", gArgs.GetDataDirNet().string()); + LogPrintf("Default data directory %s\n", fs::PathToString(GetDefaultDataDir())); + LogPrintf("Using data directory %s\n", fs::PathToString(gArgs.GetDataDirNet())); // Only log conf file usage message if conf file actually exists. fs::path config_file_path = GetConfigFile(args.GetArg("-conf", BITCOIN_CONF_FILENAME)); if (fs::exists(config_file_path)) { - LogPrintf("Config file: %s\n", config_file_path.string()); + LogPrintf("Config file: %s\n", fs::PathToString(config_file_path)); } else if (args.IsArgSet("-conf")) { // Warn if no conf file exists at path provided by user - InitWarning(strprintf(_("The specified config file %s does not exist"), config_file_path.string())); + InitWarning(strprintf(_("The specified config file %s does not exist"), fs::PathToString(config_file_path))); } else { // Not categorizing as "Warning" because it's the default behavior - LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string()); + LogPrintf("Config file: %s (not found, skipping)\n", fs::PathToString(config_file_path)); } // Log the config arguments to debug.log diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 120b86d23e..6e43cb59c1 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -450,13 +450,14 @@ void CleanupBlockRevFiles() LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); fs::path blocksdir = gArgs.GetBlocksDirPath(); for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) { + const std::string path = fs::PathToString(it->path().filename()); if (fs::is_regular_file(*it) && - it->path().filename().string().length() == 12 && - it->path().filename().string().substr(8,4) == ".dat") + path.length() == 12 && + path.substr(8,4) == ".dat") { - if (it->path().filename().string().substr(0, 3) == "blk") { - mapBlockFiles[it->path().filename().string().substr(3, 5)] = it->path(); - } else if (it->path().filename().string().substr(0, 3) == "rev") { + if (path.substr(0, 3) == "blk") { + mapBlockFiles[path.substr(3, 5)] = it->path(); + } else if (path.substr(0, 3) == "rev") { remove(it->path()); } } @@ -860,14 +861,14 @@ void ThreadImport(ChainstateManager& chainman, CDeterministicMNManager& dmnman, for (const fs::path& path : vImportFiles) { FILE *file = fsbridge::fopen(path, "rb"); if (file) { - LogPrintf("Importing blocks file %s...\n", path.string()); + LogPrintf("Importing blocks file %s...\n", fs::PathToString(path)); chainman.ActiveChainstate().LoadExternalBlockFile(file); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; } } else { - LogPrintf("Warning: Could not open blocks file %s\n", path.string()); + LogPrintf("Warning: Could not open blocks file %s\n", fs::PathToString(path)); } } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 6ec7ae0ddf..e1ce1bfdb6 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -533,7 +533,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator() fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME; CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION); if (est_file.IsNull() || !Read(est_file)) { - LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", est_filepath.string()); + LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(est_filepath)); } } @@ -893,7 +893,7 @@ void CBlockPolicyEstimator::Flush() { fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME; CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION); if (est_file.IsNull() || !Write(est_file)) { - LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", est_filepath.string()); + LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(est_filepath)); } } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index f3155c596e..b1e425ddd6 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -758,8 +758,8 @@ int GuiMain(int argc, char* argv[]) } // Validate/set custom css directory if (gArgs.IsArgSet("-custom-css-dir")) { - fs::path customDir = fs::path(gArgs.GetArg("-custom-css-dir", "")); - QString strCustomDir = QString::fromStdString(customDir.string()); + fs::path customDir = fs::PathFromString(gArgs.GetArg("-custom-css-dir", "")); + QString strCustomDir = GUIUtil::PathToQString(customDir); std::vector vecRequiredFiles = GUIUtil::listStyleSheets(); QString strFile; @@ -771,7 +771,7 @@ int GuiMain(int argc, char* argv[]) for (auto itCustomDir = fs::directory_iterator(customDir); itCustomDir != fs::directory_iterator(); ++itCustomDir) { if (fs::is_regular_file(*itCustomDir) && itCustomDir->path().extension() == ".css") { - strFile = QString::fromStdString(itCustomDir->path().filename().string()); + strFile = GUIUtil::PathToQString(itCustomDir->path().filename()); auto itFile = std::find(vecRequiredFiles.begin(), vecRequiredFiles.end(), strFile); if (itFile != vecRequiredFiles.end()) { vecRequiredFiles.erase(itFile); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 983ae83752..ca82f017cb 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1652,12 +1652,12 @@ void setClipboard(const QString& str) fs::path QStringToPath(const QString &path) { - return fs::path(path.toStdString()); + return fs::u8path(path.toStdString()); } QString PathToQString(const fs::path &path) { - return QString::fromStdString(path.string()); + return QString::fromStdString(path.u8string()); } QString NetworkToQString(Network net) diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index ed522c61f4..8468a1ce5c 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -269,7 +269,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB) * (to be consistent with dashd behavior) */ if(dataDir != GUIUtil::getDefaultDataDirectory()) { - gArgs.SoftSetArg("-datadir", GUIUtil::QStringToPath(dataDir).string()); // use OS locale for path setting + gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(dataDir))); // use OS locale for path setting } return true; } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index ecd53638ed..c3e6dc1c6d 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -814,7 +814,7 @@ void RPCConsole::addWallet(WalletModel * const walletModel) // The only loaded wallet ui->btn_rescan1->setEnabled(true); ui->btn_rescan2->setEnabled(true); - QString wallet_path = QString::fromStdString(GetWalletDir().string() + QDir::separator().toLatin1()); + QString wallet_path = GUIUtil::PathToQString(GetWalletDir()) + QDir::separator().toLatin1(); QString wallet_name = walletModel->getWalletName().isEmpty() ? "wallet.dat" : walletModel->getWalletName(); ui->wallet_path->setText(wallet_path + wallet_name); } else { @@ -837,7 +837,7 @@ void RPCConsole::removeWallet(WalletModel * const walletModel) ui->btn_rescan1->setEnabled(true); ui->btn_rescan2->setEnabled(true); WalletModel* wallet_model = ui->WalletSelector->itemData(1).value(); - QString wallet_path = QString::fromStdString(GetWalletDir().string() + QDir::separator().toLatin1()); + QString wallet_path = GUIUtil::PathToQString(GetWalletDir()) + QDir::separator().toLatin1(); QString wallet_name = wallet_model->getWalletName().isEmpty() ? "wallet.dat" : wallet_model->getWalletName(); ui->wallet_path->setText(wallet_path + wallet_name); } else { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index accb196633..e6b7a49492 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2970,15 +2970,15 @@ static RPCHelpMan dumptxoutset() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), request.params[0].get_str()); + const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str())); // Write to a temporary path and then move into `path` on completion // to avoid confusion due to an interruption. - const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), request.params[0].get_str() + ".incomplete"); + const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete")); if (fs::exists(path)) { throw JSONRPCError( RPC_INVALID_PARAMETER, - path.string() + " already exists. If you are sure this is what you want, " + path.u8string() + " already exists. If you are sure this is what you want, " "move it out of the way first"); } @@ -2989,7 +2989,7 @@ static RPCHelpMan dumptxoutset() UniValue result = CreateUTXOSnapshot(node, chainman.ActiveChainstate(), afile); fs::rename(temppath, path); - result.pushKV("path", path.string()); + result.pushKV("path", path.u8string()); return result; }, }; diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp index 4c45c33c43..dd83c45a59 100644 --- a/src/rpc/request.cpp +++ b/src/rpc/request.cpp @@ -70,7 +70,7 @@ static fs::path GetAuthCookieFile(bool temp=false) if (temp) { arg += ".tmp"; } - return AbsPathForConfigVal(fs::path(arg)); + return AbsPathForConfigVal(fs::PathFromString(arg)); } bool GenerateAuthCookie(std::string *cookie_out) @@ -87,7 +87,7 @@ bool GenerateAuthCookie(std::string *cookie_out) fs::path filepath_tmp = GetAuthCookieFile(true); file.open(filepath_tmp); if (!file.is_open()) { - LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); + LogPrintf("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp)); return false; } file << cookie; @@ -95,10 +95,10 @@ bool GenerateAuthCookie(std::string *cookie_out) fs::path filepath = GetAuthCookieFile(false); if (!RenameOver(filepath_tmp, filepath)) { - LogPrintf("Unable to rename cookie authentication file %s to %s\n", filepath_tmp.string(), filepath.string()); + LogPrintf("Unable to rename cookie authentication file %s to %s\n", fs::PathToString(filepath_tmp), fs::PathToString(filepath)); return false; } - LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); + LogPrintf("Generated RPC authentication cookie %s\n", fs::PathToString(filepath)); if (cookie_out) *cookie_out = cookie; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index eb179b143b..7af6478c0f 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -279,7 +279,7 @@ static RPCHelpMan getrpcinfo() UniValue result(UniValue::VOBJ); result.pushKV("active_commands", active_commands); - const std::string path = LogInstance().m_file_path.string(); + const std::string path = LogInstance().m_file_path.u8string(); UniValue log_path(UniValue::VSTR, path); result.pushKV("logpath", log_path); diff --git a/src/stacktraces.cpp b/src/stacktraces.cpp index 3ffcb23f38..c1493ce293 100644 --- a/src/stacktraces.cpp +++ b/src/stacktraces.cpp @@ -117,7 +117,7 @@ static std::string GetExeFileName() } static std::string g_exeFileName = GetExeFileName(); -static std::string g_exeFileBaseName = fs::path(g_exeFileName).filename().string(); +static std::string g_exeFileBaseName = fs::PathToString(fs::PathFromString(g_exeFileName).filename()); #ifdef ENABLE_STACKTRACES static void my_backtrace_error_callback (void *data, const char *msg, @@ -485,7 +485,7 @@ static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces) for (const auto& si : ci.stackframeInfos) { std::string lstr; if (!si.filename.empty()) { - lstr += fs::path(si.filename).filename().string(); + lstr += fs::PathToString(fs::PathFromString(si.filename).filename()); } else { lstr += ""; } diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp index 2e47207ed5..dc0dc659e2 100644 --- a/src/test/fs_tests.cpp +++ b/src/test/fs_tests.cpp @@ -11,6 +11,33 @@ BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(fsbridge_pathtostring) +{ + std::string u8_str = "fs_tests_∋_🏃"; + BOOST_CHECK_EQUAL(fs::PathToString(fs::PathFromString(u8_str)), u8_str); + BOOST_CHECK_EQUAL(fs::u8path(u8_str).u8string(), u8_str); + BOOST_CHECK_EQUAL(fs::PathFromString(u8_str).u8string(), u8_str); + BOOST_CHECK_EQUAL(fs::PathToString(fs::u8path(u8_str)), u8_str); +#ifndef WIN32 + // On non-windows systems, verify that arbitrary byte strings containing + // invalid UTF-8 can be round tripped successfully with PathToString and + // PathFromString. On non-windows systems, paths are just byte strings so + // these functions do not do any encoding. On windows, paths are Unicode, + // and these functions do encoding and decoding, so the behavior of this + // test would be undefined. + std::string invalid_u8_str = "\xf0"; + BOOST_CHECK_EQUAL(invalid_u8_str.size(), 1); + BOOST_CHECK_EQUAL(fs::PathToString(fs::PathFromString(invalid_u8_str)), invalid_u8_str); +#endif +} + +BOOST_AUTO_TEST_CASE(fsbridge_stem) +{ + std::string test_filename = "fs_tests_∋_🏃.dat"; + std::string expected_stem = "fs_tests_∋_🏃"; + BOOST_CHECK_EQUAL(fs::PathToString(fs::PathFromString(test_filename).stem()), expected_stem); +} + BOOST_AUTO_TEST_CASE(fsbridge_fstream) { fs::path tmpfolder = m_args.GetDataDirBase(); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index 339f286143..8c613d93ab 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -48,7 +48,7 @@ FUZZ_TARGET_INIT(banman, initialize_banman) const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()}; bool force_read_and_write_to_err{false}; if (start_with_corrupted_banlist) { - assert(WriteBinaryFile(banlist_file.string() + ".json", + assert(WriteBinaryFile(banlist_file + ".json", fuzzed_data_provider.ConsumeRandomLengthString())); } else { force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool(); @@ -111,5 +111,5 @@ FUZZ_TARGET_INIT(banman, initialize_banman) assert(banmap == banmap_read); } } - fs::remove(banlist_file.string() + ".json"); + fs::remove(fs::PathToString(banlist_file + ".json")); } diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index 340ce33d91..15cba9e3e5 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -80,19 +80,19 @@ BOOST_AUTO_TEST_CASE(ReadWrite) "dupe": "dupe" })"); BOOST_CHECK(!util::ReadSettings(path, values, errors)); - std::vector dup_keys = {strprintf("Found duplicate key dupe in settings file %s", path.string())}; + std::vector dup_keys = {strprintf("Found duplicate key dupe in settings file %s", fs::PathToString(path))}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), dup_keys.begin(), dup_keys.end()); // Check non-kv json files not allowed WriteText(path, R"("non-kv")"); BOOST_CHECK(!util::ReadSettings(path, values, errors)); - std::vector non_kv = {strprintf("Found non-object value \"non-kv\" in settings file %s", path.string())}; + std::vector non_kv = {strprintf("Found non-object value \"non-kv\" in settings file %s", fs::PathToString(path))}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), non_kv.begin(), non_kv.end()); // Check invalid json not allowed WriteText(path, R"(invalid json)"); BOOST_CHECK(!util::ReadSettings(path, values, errors)); - std::vector fail_parse = {strprintf("Unable to parse settings file %s", path.string())}; + std::vector fail_parse = {strprintf("Unable to parse settings file %s", fs::PathToString(path))}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), fail_parse.begin(), fail_parse.end()); } diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h index 81ea4c38f5..e95573022c 100644 --- a/src/test/util/chainstate.h +++ b/src/test/util/chainstate.h @@ -36,7 +36,7 @@ CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleati UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile); BOOST_TEST_MESSAGE( - "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write()); + "Wrote UTXO snapshot to " << fs::PathToString(snapshot_path.make_preferred()) << ": " << result.write()); // Read the written snapshot in and then activate it. // diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index bb8ef10a67..7ab9ed9eb1 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -153,8 +153,8 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve extra_args); util::ThreadRename("test"); fs::create_directories(m_path_root); - m_args.ForceSetArg("-datadir", m_path_root.string()); - gArgs.ForceSetArg("-datadir", m_path_root.string()); + m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root)); + gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root)); gArgs.ClearPathCache(); { SetupServerArgs(m_node); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 467ef4edac..997b0e2033 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -56,23 +56,23 @@ BOOST_AUTO_TEST_CASE(util_datadir) { // Use local args variable instead of m_args to avoid making assumptions about test setup ArgsManager args; - args.ForceSetArg("-datadir", m_path_root.string()); + args.ForceSetArg("-datadir", fs::PathToString(m_path_root)); const fs::path dd_norm = args.GetDataDirBase(); - args.ForceSetArg("-datadir", dd_norm.string() + "/"); + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/"); args.ClearPathCache(); BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); - args.ForceSetArg("-datadir", dd_norm.string() + "/."); + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/."); args.ClearPathCache(); BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); - args.ForceSetArg("-datadir", dd_norm.string() + "/./"); + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./"); args.ClearPathCache(); BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); - args.ForceSetArg("-datadir", dd_norm.string() + "/.//"); + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//"); args.ClearPathCache(); BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); } @@ -1259,13 +1259,13 @@ BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) { // Test writing setting. TestArgsManager args1; - args1.ForceSetArg("-datadir", m_path_root.string()); + args1.ForceSetArg("-datadir", fs::PathToString(m_path_root)); args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; }); args1.WriteSettingsFile(); // Test reading setting. TestArgsManager args2; - args2.ForceSetArg("-datadir", m_path_root.string()); + args2.ForceSetArg("-datadir", fs::PathToString(m_path_root)); args2.ReadSettingsFile(); args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); }); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 837b8d2698..98144b791f 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -314,7 +314,7 @@ TorController::TorController(struct event_base* _base, const std::string& tor_co // Read service private key if cached std::pair pkf = ReadBinaryFile(GetPrivateKeyFile()); if (pkf.first) { - LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string()); + LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile())); private_key = pkf.second; } } @@ -352,9 +352,9 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe service = LookupNumeric(std::string(service_id+".onion"), Params().GetDefaultPort()); LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { - LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile().string()); + LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile())); } else { - LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile().string()); + LogPrintf("tor: Error writing service private key to %s\n", fs::PathToString(GetPrivateKeyFile())); } AddLocal(service, LOCAL_MANUAL); // ... onion requested - keep connection open @@ -521,7 +521,7 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro } else if (methods.count("SAFECOOKIE")) { // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); - std::pair status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); + std::pair status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE); if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); cookie = std::vector(status_cookie.second.begin(), status_cookie.second.end()); diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp index 5695c62012..b696c65e9d 100644 --- a/src/util/asmap.cpp +++ b/src/util/asmap.cpp @@ -201,7 +201,7 @@ std::vector DecodeAsmap(fs::path path) } fseek(filestr, 0, SEEK_END); int length = ftell(filestr); - LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length); + LogPrintf("Opened asmap file %s (%d bytes) from disk\n", fs::quoted(fs::PathToString(path)), length); fseek(filestr, 0, SEEK_SET); uint8_t cur_byte; for (int i = 0; i < length; ++i) { @@ -211,7 +211,7 @@ std::vector DecodeAsmap(fs::path path) } } if (!SanityCheckASMap(bits, 128)) { - LogPrintf("Sanity check of asmap file %s failed\n", path); + LogPrintf("Sanity check of asmap file %s failed\n", fs::quoted(fs::PathToString(path))); return {}; } return bits; diff --git a/src/util/settings.cpp b/src/util/settings.cpp index 690fa0e76b..db82e983d2 100644 --- a/src/util/settings.cpp +++ b/src/util/settings.cpp @@ -66,7 +66,7 @@ bool ReadSettings(const fs::path& path, std::map& va fsbridge::ifstream file; file.open(path); if (!file.is_open()) { - errors.emplace_back(strprintf("%s. Please check permissions.", path.string())); + errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path))); return false; } @@ -74,9 +74,9 @@ bool ReadSettings(const fs::path& path, std::map& va if (file.peek() == std::ifstream::traits_type::eof()) { // In that case delete it and return true: it will be created with default value later file.close(); - if (!boost::filesystem::remove(path)) { + if (!fs::remove(path)) { // Return false only if it failed to delete the empty settings file - errors.emplace_back(strprintf("Unable to delete empty settings file %s", path.string())); + errors.emplace_back(strprintf("Unable to delete empty settings file %s", fs::PathToString(path))); return false; } return true; @@ -84,18 +84,18 @@ bool ReadSettings(const fs::path& path, std::map& va SettingsValue in; if (!in.read(std::string{std::istreambuf_iterator(file), std::istreambuf_iterator()})) { - errors.emplace_back(strprintf("Unable to parse settings file %s", path.string())); + errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path))); return false; } if (file.fail()) { - errors.emplace_back(strprintf("Failed reading settings file %s", path.string())); + errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path))); return false; } file.close(); // Done with file descriptor. Release while copying data. if (!in.isObject()) { - errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string())); + errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path))); return false; } @@ -104,7 +104,7 @@ bool ReadSettings(const fs::path& path, std::map& va for (size_t i = 0; i < in_keys.size(); ++i) { auto inserted = values.emplace(in_keys[i], in_values[i]); if (!inserted.second) { - errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string())); + errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path))); } } return errors.empty(); @@ -121,7 +121,7 @@ bool WriteSettings(const fs::path& path, fsbridge::ofstream file; file.open(path); if (file.fail()) { - errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string())); + errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path))); return false; } file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl; diff --git a/src/util/system.cpp b/src/util/system.cpp index 5b451baa26..6ad54b010a 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -114,7 +115,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b fs::path pathLockFile = directory / lockfile_name; // If a lock for this directory already exists in the map, don't try to re-lock it - if (dir_locks.count(pathLockFile.string())) { + if (dir_locks.count(fs::PathToString(pathLockFile))) { return true; } @@ -123,11 +124,11 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b if (file) fclose(file); auto lock = std::make_unique(pathLockFile); if (!lock->TryLock()) { - return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason()); + return error("Error while attempting to lock directory %s: %s", fs::PathToString(directory), lock->GetReason()); } if (!probe_only) { // Lock successful and we're not just probing, put it into the map - dir_locks.emplace(pathLockFile.string(), std::move(lock)); + dir_locks.emplace(fs::PathToString(pathLockFile), std::move(lock)); } return true; } @@ -135,7 +136,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name) { LOCK(cs_dir_locks); - dir_locks.erase((directory / lockfile_name).string()); + dir_locks.erase(fs::PathToString(directory / lockfile_name)); } void ReleaseDirectoryLocks() @@ -258,7 +259,7 @@ namespace { fs::path StripRedundantLastElementsOfPath(const fs::path& path) { auto result = path; - while (result.filename().string() == ".") { + while (fs::PathToString(result.filename()) == ".") { result = result.parent_path(); } @@ -423,7 +424,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const if (!path.empty()) return path; if (IsArgSet("-blocksdir")) { - path = fs::system_complete(GetArg("-blocksdir", "")); + path = fs::system_complete(fs::PathFromString(GetArg("-blocksdir", ""))); if (!fs::is_directory(path)) { path = ""; return path; @@ -432,7 +433,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const path = GetDataDirBase(); } - path /= BaseParams().DataDir(); + path /= fs::PathFromString(BaseParams().DataDir()); path /= "blocks"; fs::create_directories(path); path = StripRedundantLastElementsOfPath(path); @@ -450,7 +451,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const std::string datadir = GetArg("-datadir", ""); if (!datadir.empty()) { - path = fs::system_complete(datadir); + path = fs::system_complete(fs::PathFromString(datadir)); if (!fs::is_directory(path)) { path = ""; return path; @@ -459,7 +460,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const path = GetDefaultDataDir(); } if (net_specific) - path /= BaseParams().DataDir(); + path /= fs::PathFromString(BaseParams().DataDir()); if (fs::create_directories(path)) { // This is the first run, create wallets subdirectory too @@ -475,7 +476,7 @@ fs::path ArgsManager::GetBackupsDirPath() if (!IsArgSet("-walletbackupsdir")) return GetDataDirNet() / "backups"; - return fs::absolute(GetArg("-walletbackupsdir", "")); + return fs::absolute(fs::PathFromString(GetArg("-walletbackupsdir", ""))); } void ArgsManager::ClearPathCache() @@ -546,7 +547,7 @@ bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const } if (filepath) { std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME); - *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings); + *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), fs::PathFromString(temp ? settings + ".tmp" : settings)); } return true; } @@ -601,7 +602,7 @@ bool ArgsManager::WriteSettingsFile(std::vector* errors) const return false; } if (!RenameOver(path_tmp, path)) { - SaveErrors({strprintf("Failed renaming settings file %s to %s\n", path_tmp.string(), path.string())}, errors); + SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors); return false; } return true; @@ -862,12 +863,12 @@ fs::path GetBackupsDir() bool CheckDataDirOption() { std::string datadir = gArgs.GetArg("-datadir", ""); - return datadir.empty() || fs::is_directory(fs::system_complete(datadir)); + return datadir.empty() || fs::is_directory(fs::system_complete(fs::PathFromString(datadir))); } fs::path GetConfigFile(const std::string& confPath) { - return AbsPathForConfigVal(fs::path(confPath), false); + return AbsPathForConfigVal(fs::PathFromString(confPath), false); } static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector>& options, std::list& sections) @@ -1027,7 +1028,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } } else { // Create an empty dash.conf if it does not exist - FILE* configFile = fopen(GetConfigFile(confPath).string().c_str(), "a"); + FILE* configFile = fsbridge::fopen(GetConfigFile(confPath), "a"); if (configFile != nullptr) fclose(configFile); return true; // Nothing to read, so just return @@ -1131,7 +1132,7 @@ bool RenameOver(fs::path src, fs::path dest) return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(), MOVEFILE_REPLACE_EXISTING) != 0; #else - int rc = std::rename(src.string().c_str(), dest.string().c_str()); + int rc = std::rename(src.c_str(), dest.c_str()); return (rc == 0); #endif /* WIN32 */ } diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 5b21fe4e24..8a2f53ba78 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -61,9 +61,9 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const std::shared_ptr GetBerkeleyEnv(const fs::path& env_directory) { LOCK(cs_db); - auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr()); + auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr()); if (inserted.second) { - auto env = std::make_shared(env_directory.string()); + auto env = std::make_shared(env_directory); inserted.first->second = env; return env; } @@ -101,7 +101,7 @@ void BerkeleyEnvironment::Close() if (error_file) fclose(error_file); - UnlockDirectory(strPath, ".walletlock"); + UnlockDirectory(fs::PathFromString(strPath), ".walletlock"); } void BerkeleyEnvironment::Reset() @@ -111,7 +111,7 @@ void BerkeleyEnvironment::Reset() fMockDb = false; } -BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string()) +BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(fs::PathToString(dir_path)) { Reset(); } @@ -129,24 +129,24 @@ bool BerkeleyEnvironment::Open(bilingual_str& err) return true; } - fs::path pathIn = strPath; + fs::path pathIn = fs::PathFromString(strPath); TryCreateDirectories(pathIn); if (!LockDirectory(pathIn, ".walletlock")) { LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of Dash Core may be using it.\n", strPath); - err = strprintf(_("Error initializing wallet database environment %s!"), Directory()); + err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory()))); return false; } fs::path pathLogDir = pathIn / "database"; TryCreateDirectories(pathLogDir); fs::path pathErrorFile = pathIn / "db.log"; - LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); + LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile)); unsigned int nEnvFlags = 0; if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) nEnvFlags |= DB_PRIVATE; - dbenv->set_lg_dir(pathLogDir.string().c_str()); + dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str()); dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet dbenv->set_lg_bsize(0x10000); dbenv->set_lg_max(1048576); @@ -173,7 +173,7 @@ bool BerkeleyEnvironment::Open(bilingual_str& err) LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2)); } Reset(); - err = strprintf(_("Error initializing wallet database environment %s!"), Directory()); + err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory()))); if (ret == DB_RUNRECOVERY) { err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet"); } @@ -261,7 +261,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr) fs::path file_path = walletDir / strFile; LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion()); - LogPrintf("Using wallet %s\n", file_path.string()); + LogPrintf("Using wallet %s\n", fs::PathToString(file_path)); if (!env->Open(errorStr)) { return false; @@ -274,7 +274,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr) Db db(env->dbenv.get(), 0); int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); if (result != 0) { - errorStr = strprintf(_("%s corrupt. Try using the wallet tool dash-wallet to salvage or restoring a backup."), file_path); + errorStr = strprintf(_("%s corrupt. Try using the wallet tool dash-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path))); return false; } } @@ -566,7 +566,7 @@ void BerkeleyEnvironment::Flush(bool fShutdown) dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); if (!fMockDb) { - fs::remove_all(fs::path(strPath) / "database"); + fs::remove_all(fs::PathFromString(strPath) / "database"); } } } @@ -614,21 +614,21 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const // Copy wallet file fs::path pathSrc = env->Directory() / strFile; - fs::path pathDest(strDest); + fs::path pathDest(fs::PathFromString(strDest)); if (fs::is_directory(pathDest)) - pathDest /= strFile; + pathDest /= fs::PathFromString(strFile); try { if (fs::equivalent(pathSrc, pathDest)) { - LogPrintf("cannot backup to wallet source file %s\n", pathDest.string()); + LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest)); return false; } fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists); - LogPrintf("copied %s to %s\n", strFile, pathDest.string()); + LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest)); return true; } catch (const fs::filesystem_error& e) { - LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e)); + LogPrintf("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), fsbridge::get_filesystem_error_message(e)); return false; } } @@ -828,10 +828,10 @@ std::unique_ptr MakeBerkeleyDatabase(const fs::path& path, con std::unique_ptr db; { LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor - std::string data_filename = data_file.filename().string(); + std::string data_filename = fs::PathToString(data_file.filename()); std::shared_ptr env = GetBerkeleyEnv(data_file.parent_path()); if (env->m_databases.count(data_filename)) { - error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string())); + error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename))); status = DatabaseStatus::FAILED_ALREADY_LOADED; return nullptr; } diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h index 65deef7031..4ea0f460fe 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -62,7 +62,7 @@ class BerkeleyEnvironment bool IsMock() const { return fMockDb; } bool IsInitialized() const { return fDbEnvInit; } - fs::path Directory() const { return strPath; } + fs::path Directory() const { return fs::PathFromString(strPath); } bool Open(bilingual_str& error); void Close(); @@ -140,7 +140,7 @@ class BerkeleyDatabase : public WalletDatabase bool Verify(bilingual_str& error); /** Return path to main database filename */ - std::string Filename() override { return (env->Directory() / strFile).string(); } + std::string Filename() override { return fs::PathToString(env->Directory() / strFile); } std::string Format() override { return "bdb"; } /** diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 8d5316e0af..c74c69ed09 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -12,7 +12,7 @@ std::vector ListDatabases(const fs::path& wallet_dir) { - const size_t offset = wallet_dir.string().size() + (wallet_dir == wallet_dir.root_name() ? 0 : 1); + const size_t offset = wallet_dir.native().size() + (wallet_dir == wallet_dir.root_name() ? 0 : 1); std::vector paths; boost::system::error_code ec; @@ -20,9 +20,9 @@ std::vector ListDatabases(const fs::path& wallet_dir) if (ec) { if (fs::is_directory(*it)) { it.no_push(); - LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), it->path().string()); + LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path())); } else { - LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string()); + LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path())); } continue; } @@ -30,7 +30,8 @@ std::vector ListDatabases(const fs::path& wallet_dir) try { // Get wallet path relative to walletdir by removing walletdir from the wallet path. // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60. - const fs::path path = it->path().string().substr(offset); + const auto path_str = it->path().native().substr(offset); + const fs::path path{path_str.begin(), path_str.end()}; if (it->status().type() == fs::directory_file && (IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) { @@ -50,7 +51,7 @@ std::vector ListDatabases(const fs::path& wallet_dir) } } } catch (const std::exception& e) { - LogPrintf("%s: Error scanning %s: %s\n", __func__, it->path().string(), e.what()); + LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what()); it.no_push(); } } @@ -85,7 +86,7 @@ bool IsBDBFile(const fs::path& path) // This check also prevents opening lock files. boost::system::error_code ec; auto size = fs::file_size(path, ec); - if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string()); + if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path)); if (size < 4096) return false; fsbridge::ifstream file(path, std::ios::binary); @@ -109,7 +110,7 @@ bool IsSQLiteFile(const fs::path& path) // A SQLite Database file is at least 512 bytes. boost::system::error_code ec; auto size = fs::file_size(path, ec); - if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string()); + if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path)); if (size < 512) return false; fsbridge::ifstream file(path, std::ios::binary); diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp index b1793bce38..7459aa8715 100644 --- a/src/wallet/dump.cpp +++ b/src/wallet/dump.cpp @@ -19,16 +19,16 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error) return false; } - fs::path path = dump_filename; + fs::path path = fs::PathFromString(dump_filename); path = fs::absolute(path); if (fs::exists(path)) { - error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), path.string()); + error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path)); return false; } fsbridge::ofstream dump_file; dump_file.open(path); if (dump_file.fail()) { - error = strprintf(_("Unable to open %s for writing"), path.string()); + error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path)); return false; } @@ -114,10 +114,10 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling return false; } - fs::path dump_path = dump_filename; + fs::path dump_path = fs::PathFromString(dump_filename); dump_path = fs::absolute(dump_path); if (!fs::exists(dump_path)) { - error = strprintf(_("Dump file %s does not exist."), dump_path.string()); + error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path)); return false; } fsbridge::ifstream dump_file(dump_path); diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 50f446863e..6b93370bb2 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -615,13 +615,13 @@ class WalletLoaderImpl : public WalletLoader } std::string getWalletDir() override { - return GetWalletDir().string(); + return fs::PathToString(GetWalletDir()); } std::vector listWalletDir() override { std::vector paths; for (auto& path : ListDatabases(GetWalletDir())) { - paths.push_back(path.string()); + paths.push_back(fs::PathToString(path)); } return paths; } diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 3cc8e4a3cc..8dbc18aa27 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -22,25 +22,25 @@ bool VerifyWallets(interfaces::Chain& chain) { if (gArgs.IsArgSet("-walletdir")) { - fs::path wallet_dir = gArgs.GetArg("-walletdir", ""); + fs::path wallet_dir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); boost::system::error_code error; // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); if (error || !fs::exists(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir))); return false; } else if (!fs::is_directory(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir))); return false; // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version } else if (!wallet_dir.is_absolute()) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir))); return false; } - gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string()); + gArgs.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir)); } - LogPrintf("Using wallet directory %s\n", GetWalletDir().string()); + LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir())); chain.initMessage(_("Verifying wallet(s)…").translated); @@ -65,7 +65,7 @@ bool VerifyWallets(interfaces::Chain& chain) std::set wallet_paths; for (const auto& wallet_file : gArgs.GetArgs("-wallet")) { - const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_file); + const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file)); if (!wallet_paths.insert(path).second) { chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file)); @@ -95,7 +95,7 @@ bool LoadWallets(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoi try { std::set wallet_paths; for (const std::string& name : gArgs.GetArgs("-wallet")) { - if (!wallet_paths.insert(name).second) { + if (!wallet_paths.insert(fs::PathFromString(name)).second) { continue; } DatabaseOptions options; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 595da22809..f855f6ae34 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -510,7 +510,7 @@ RPCHelpMan importwallet() EnsureWalletIsUnlocked(*pwallet); fsbridge::ifstream file; - file.open(request.params[0].get_str(), std::ios::in | std::ios::ate); + file.open(fs::u8path(request.params[0].get_str()), std::ios::in | std::ios::ate); if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); } @@ -936,7 +936,7 @@ RPCHelpMan dumpwallet() EnsureWalletIsUnlocked(wallet); - fs::path filepath = request.params[0].get_str(); + fs::path filepath = fs::u8path(request.params[0].get_str()); filepath = fs::absolute(filepath); /* Prevent arbitrary files from being overwritten. There have been reports @@ -945,7 +945,7 @@ RPCHelpMan dumpwallet() * It may also avoid other security issues. */ if (fs::exists(filepath)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first"); + throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.u8string() + " already exists. If you are sure this is what you want, move it out of the way first"); } fsbridge::ofstream file; @@ -1067,7 +1067,7 @@ RPCHelpMan dumpwallet() std::string strWarning = strprintf(_("%s file contains all private keys from this wallet. Do not share it with anyone!").translated, request.params[0].get_str()); obj.pushKV("keys", int(vKeyBirth.size())); - obj.pushKV("filename", filepath.string()); + obj.pushKV("filename", filepath.u8string()); obj.pushKV("warning", strWarning); return obj; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 71d2c2c15c..d6718c64e9 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2675,7 +2675,7 @@ static RPCHelpMan listwalletdir() UniValue wallets(UniValue::VARR); for (const auto& path : ListDatabases(GetWalletDir())) { UniValue wallet(UniValue::VOBJ); - wallet.pushKV("name", path.string()); + wallet.pushKV("name", path.u8string()); wallets.push_back(wallet); } @@ -3074,7 +3074,7 @@ static RPCHelpMan restorewallet() WalletContext& context = EnsureWalletContext(request.context); - std::string backup_file = request.params[1].get_str(); + auto backup_file = fs::u8path(request.params[1].get_str()); std::string wallet_name = request.params[0].get_str(); @@ -3084,7 +3084,7 @@ static RPCHelpMan restorewallet() bilingual_str error; std::vector warnings; - const std::shared_ptr wallet = RestoreWallet(*context.chain, *context.m_coinjoin_loader, backup_file, wallet_name, load_on_start, status, error, warnings); + const std::shared_ptr wallet = RestoreWallet(*context.chain, *context.m_coinjoin_loader, fs::PathToString(backup_file), wallet_name, load_on_start, status, error, warnings); HandleWalletError(wallet, status, error); diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 9c442f5fa4..1437f7ae6f 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -67,7 +67,7 @@ static void SetPragma(sqlite3* db, const std::string& key, const std::string& va } SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock) - : WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string()) + : WalletDatabase(), m_mock(mock), m_dir_path(fs::PathToString(dir_path)), m_file_path(fs::PathToString(file_path)) { { LOCK(g_sqlite_mutex); @@ -206,7 +206,7 @@ void SQLiteDatabase::Open() if (m_db == nullptr) { if (!m_mock) { - TryCreateDirectories(m_dir_path); + TryCreateDirectories(fs::PathFromString(m_dir_path)); } int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr); if (ret != SQLITE_OK) { diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index 16cb7e0baf..dba3f35025 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -16,7 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup) static std::shared_ptr GetWalletEnv(const fs::path& path, std::string& database_filename) { fs::path data_file = BDBDataFile(path); - database_filename = data_file.filename().string(); + database_filename = fs::PathToString(data_file.filename()); return GetBerkeleyEnv(data_file.parent_path()); } @@ -25,11 +25,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file) std::string test_name = "test_name.dat"; const fs::path datadir = gArgs.GetDataDirNet(); fs::path file_path = datadir / test_name; -#if BOOST_VERSION >= 107700 - std::ofstream f(BOOST_FILESYSTEM_C_STR(file_path)); -#else - std::ofstream f(file_path.BOOST_FILESYSTEM_C_STR); -#endif // BOOST_VERSION >= 107700 + fs::ofstream f(file_path); f.close(); std::string filename; diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp index a957cb65d1..ef2219ef64 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -32,11 +32,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam fs::create_directories(m_walletdir_path_cases["default"]); fs::create_directories(m_walletdir_path_cases["custom"]); fs::create_directories(m_walletdir_path_cases["relative"]); -#if BOOST_VERSION >= 107700 - std::ofstream f(BOOST_FILESYSTEM_C_STR(m_walletdir_path_cases["file"])); -#else - std::ofstream f(m_walletdir_path_cases["file"].BOOST_FILESYSTEM_C_STR); -#endif // BOOST_VERSION >= 107700 + fs::ofstream f(m_walletdir_path_cases["file"]); f.close(); } @@ -50,5 +46,5 @@ InitWalletDirTestingSetup::~InitWalletDirTestingSetup() void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path) { - gArgs.ForceSetArg("-walletdir", walletdir_path.string()); + gArgs.ForceSetArg("-walletdir", fs::PathToString(walletdir_path)); } diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp index 2676e575b7..ca27dbdc03 100644 --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } @@ -32,7 +32,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = gArgs.GetArg("-walletdir", ""); + fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index f1be3b3f99..369c0e8048 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -272,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) SetMockTime(KEY_TIME); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); - std::string backup_file = (gArgs.GetDataDirNet() / "wallet.backup").string(); + std::string backup_file = fs::PathToString(gArgs.GetDataDirNet() / "wallet.backup"); // Import key into wallet and call dumpwallet to create backup file. { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c3e9aa36bd..7e9dd44bb1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -370,16 +370,16 @@ std::shared_ptr RestoreWallet(interfaces::Chain& chain, interfaces::Coi DatabaseOptions options; options.require_existing = true; - if (!fs::exists(backup_file)) { + if (!fs::exists(fs::u8path(backup_file))) { error = Untranslated("Backup file does not exist"); status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE; return nullptr; } - const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_name); + const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name)); if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) { - error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.string())); + error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(wallet_path))); status = DatabaseStatus::FAILED_ALREADY_EXISTS; return nullptr; } @@ -4660,16 +4660,16 @@ std::unique_ptr MakeWalletDatabase(const std::string& name, cons // 2. Path to an existing directory. // 3. Path to a symlink to a directory. // 4. For backwards compatibility, the name of a data file in -walletdir. - const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), name); + const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name)); fs::file_type path_type = fs::symlink_status(wallet_path).type(); if (!(path_type == fs::file_not_found || path_type == fs::directory_file || (path_type == fs::symlink_file && fs::is_directory(wallet_path)) || - (path_type == fs::regular_file && fs::path(name).filename() == name))) { + (path_type == fs::regular_file && fs::PathFromString(name).filename() == fs::PathFromString(name)))) { error_string = Untranslated(strprintf( "Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " "database/log.?????????? files can be stored, a location where such a directory could be created, " "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)", - name, GetWalletDir())); + name, fs::quoted(fs::PathToString(GetWalletDir())))); status = DatabaseStatus::FAILED_BAD_PATH; return nullptr; } @@ -4689,7 +4689,7 @@ std::shared_ptr CWallet::Create(interfaces::Chain& chain, interfaces::C // should be possible to use std::allocate_shared. std::shared_ptr walletInstance(new CWallet(&chain, &coinjoin_loader, name, std::move(database)), ReleaseWallet); // TODO: refactor this condition: validation of error looks like workaround - if (!walletInstance->AutoBackupWallet(walletFile, error, warnings) && !error.original.empty()) { + if (!walletInstance->AutoBackupWallet(fs::PathFromString(walletFile), error, warnings) && !error.original.empty()) { return nullptr; } DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); @@ -5168,17 +5168,17 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error if (!fs::exists(backupsDir)) { // Always create backup folder to not confuse the operating system's file browser - WalletLogPrintf("Creating backup folder %s\n", backupsDir.string()); + WalletLogPrintf("Creating backup folder %s\n", fs::PathToString(backupsDir)); if(!fs::create_directories(backupsDir)) { // something is wrong, we shouldn't continue until it's resolved - error_string = strprintf(_("Wasn't able to create wallet backup folder %s!"), backupsDir.string()); + error_string = strprintf(_("Wasn't able to create wallet backup folder %s!"), fs::PathToString(backupsDir)); WalletLogPrintf("%s\n", error_string.translated); nWalletBackups = -1; return false; } } else if (!fs::is_directory(backupsDir)) { // something is wrong, we shouldn't continue until it's resolved - error_string = strprintf(_("%s is not a valid backup folder!"), backupsDir.string()); + error_string = strprintf(_("%s is not a valid backup folder!"), fs::PathToString(backupsDir)); WalletLogPrintf("%s\n", error_string.translated); nWalletBackups = -1; return false; @@ -5200,8 +5200,8 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error LOCK(cs_wallet); fs::path backupFile = backupsDir / (strWalletName + dateTimeStr); backupFile.make_preferred(); - if (!BackupWallet(backupFile.string())) { - warnings.push_back(strprintf(_("Failed to create backup %s!"), backupFile.string())); + if (!BackupWallet(fs::PathToString(backupFile))) { + warnings.push_back(strprintf(_("Failed to create backup %s!"), fs::PathToString(backupFile))); WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original); nWalletBackups = -1; return false; @@ -5220,7 +5220,7 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error // ... strWalletName file fs::path strSourceFile = BDBDataFile(wallet_path); std::shared_ptr env = GetBerkeleyEnv(strSourceFile.parent_path()); - fs::path sourceFile = env->Directory() / strSourceFile.filename().string(); + fs::path sourceFile = env->Directory() / strSourceFile; fs::path backupFile = backupsDir / (strWalletName + dateTimeStr); sourceFile.make_preferred(); backupFile.make_preferred(); @@ -5233,7 +5233,7 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error if(fs::exists(sourceFile)) { try { fs::copy_file(sourceFile, backupFile); - WalletLogPrintf("Creating backup of %s -> %s\n", sourceFile.string(), backupFile.string()); + WalletLogPrintf("Creating backup of %s -> %s\n", fs::PathToString(sourceFile), fs::PathToString(backupFile)); } catch(fs::filesystem_error &error) { warnings.push_back(strprintf(_("Failed to create backup, error: %s"), fsbridge::get_filesystem_error_message(error))); WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original); @@ -5256,7 +5256,7 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error { currentFile = dir_iter->path().filename(); // Only add the backups for the current wallet, e.g. wallet.dat.* - if (dir_iter->path().stem().string() == strWalletName) { + if (fs::PathToString(dir_iter->path().stem()) == strWalletName) { folder_set.insert(folder_set_t::value_type(fs::last_write_time(dir_iter->path()), *dir_iter)); } } @@ -5272,7 +5272,7 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error // More than nWalletBackups backups: delete oldest one(s) try { fs::remove(file.second); - WalletLogPrintf("Old backup deleted: %s\n", file.second); + WalletLogPrintf("Old backup deleted: %s\n", fs::PathToString(file.second)); } catch(fs::filesystem_error &error) { warnings.push_back(strprintf(_("Failed to delete backup, error: %s"), fsbridge::get_filesystem_error_message(error))); WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 86809a5e34..fc0c1ae9a0 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1075,7 +1075,7 @@ std::unique_ptr MakeDatabase(const fs::path& path, const Databas try { exists = fs::symlink_status(path).type() != fs::file_not_found; } catch (const fs::filesystem_error& e) { - error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e))); + error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e))); status = DatabaseStatus::FAILED_BAD_PATH; return nullptr; } @@ -1087,33 +1087,33 @@ std::unique_ptr MakeDatabase(const fs::path& path, const Databas } if (IsSQLiteFile(SQLiteDataFile(path))) { if (format) { - error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", path.string())); + error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", fs::PathToString(path))); status = DatabaseStatus::FAILED_BAD_FORMAT; return nullptr; } format = DatabaseFormat::SQLITE; } } else if (options.require_existing) { - error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string())); + error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path))); status = DatabaseStatus::FAILED_NOT_FOUND; return nullptr; } if (!format && options.require_existing) { - error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string())); + error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path))); status = DatabaseStatus::FAILED_BAD_FORMAT; return nullptr; } if (format && options.require_create) { - error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string())); + error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path))); status = DatabaseStatus::FAILED_ALREADY_EXISTS; return nullptr; } // A db already exists so format is set, but options also specifies the format, so make sure they agree if (format && options.require_format && format != options.require_format) { - error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", path.string())); + error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", fs::PathToString(path))); status = DatabaseStatus::FAILED_BAD_FORMAT; return nullptr; } @@ -1135,7 +1135,7 @@ std::unique_ptr MakeDatabase(const fs::path& path, const Databas #ifdef USE_SQLITE return MakeSQLiteDatabase(path, options, status, error); #endif - error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", path.string())); + error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", fs::PathToString(path))); status = DatabaseStatus::FAILED_BAD_FORMAT; return nullptr; } @@ -1143,7 +1143,7 @@ std::unique_ptr MakeDatabase(const fs::path& path, const Databas #ifdef USE_BDB return MakeBerkeleyDatabase(path, options, status, error); #endif - error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", path.string())); + error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", fs::PathToString(path))); status = DatabaseStatus::FAILED_BAD_FORMAT; return nullptr; } diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 82f417dba9..64952df6f3 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -134,7 +134,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) return false; } const std::string name = args.GetArg("-wallet", ""); - const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name); + const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name)); if (command == "create") { DatabaseOptions options; diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index e0b66b7064..08af6a59ee 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -12,7 +12,7 @@ fs::path GetWalletDir() fs::path path; if (gArgs.IsArgSet("-walletdir")) { - path = gArgs.GetArg("-walletdir", ""); + path = fs::PathFromString(gArgs.GetArg("-walletdir", "")); if (!fs::is_directory(path)) { // If the path specified doesn't exist, we return the deliberately // invalid empty string. From 193f6fde2ee632bbde4e1472272e032a794018e3 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 5 Nov 2021 18:22:43 +0100 Subject: [PATCH 04/18] merge bitcoin#23446: Mention that BerkeleyDB is for legacy wallet in build-unix --- doc/build-unix.md | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/doc/build-unix.md b/doc/build-unix.md index 27f00b5b1e..607f9cf418 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -45,11 +45,11 @@ Optional dependencies: gmp | Optimized math routines | Arbitrary precision arithmetic library miniupnpc | UPnP Support | Firewall-jumping support libnatpmp | NAT-PMP Support | Firewall-jumping support - libdb4.8 | Berkeley DB | Optional, wallet storage (only needed when wallet enabled) + libdb4.8 | Berkeley DB | Optional, wallet storage (only needed when legacy wallet enabled) qt | GUI | GUI toolkit (only needed when GUI enabled) libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled) libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.0.0) - sqlite3 | SQLite DB | Wallet storage (only needed when wallet enabled) + sqlite3 | SQLite DB | Optional, wallet storage (only needed when descriptor wallet enabled) For the versions used, see [dependencies.md](dependencies.md) @@ -84,21 +84,17 @@ Now, you can either build from self-compiled [depends](/depends/README.md) or in sudo apt-get install libevent-dev libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev ``` -Berkeley DB is required for the wallet. - -Ubuntu and Debian have their own `libdb-dev` and `libdb++-dev` packages, but these will install -Berkeley DB 5.1 or later. This will break binary wallet compatibility with the distributed executables, which -are based on BerkeleyDB 4.8. If you do not care about wallet compatibility, -pass `--with-incompatible-bdb` to configure. - -Otherwise, you can build Berkeley DB [yourself](#berkeley-db). - -SQLite is required for the wallet: +SQLite is required for the descriptor wallet: ```sh sudo apt-get install libsqlite3-dev ``` +Berkeley DB is required for the legacy wallet. Ubuntu and Debian have their own `libdb-dev` and `libdb++-dev` packages, +but these will install Berkeley DB 5.1 or later. This will break binary wallet compatibility with the distributed +executables, which are based on BerkeleyDB 4.8. If you do not care about wallet compatibility, pass +`--with-incompatible-bdb` to configure. Otherwise, you can build Berkeley DB [yourself](#berkeley-db). + To build Dash Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode) Optional port mapping libraries (see: `--with-miniupnpc` and `--with-natpmp`): @@ -163,7 +159,13 @@ Now, you can either build from self-compiled [depends](/depends/README.md) or in sudo dnf install libevent-devel boost-devel ``` -Berkeley DB is required for the wallet: +SQLite is required for the descriptor wallet: + +```sh +sudo dnf install sqlite-devel +``` + +Berkeley DB is required for the legacy wallet: ```sh sudo dnf install libdb4-devel libdb4-cxx-devel @@ -172,15 +174,7 @@ sudo dnf install libdb4-devel libdb4-cxx-devel Newer Fedora releases, since Fedora 33, have only `libdb-devel` and `libdb-cxx-devel` packages, but these will install Berkeley DB 5.3 or later. This will break binary wallet compatibility with the distributed executables, which are based on Berkeley DB 4.8. If you do not care about wallet compatibility, -pass `--with-incompatible-bdb` to configure. - -Otherwise, you can build Berkeley DB [yourself](#berkeley-db). - -SQLite is required for the wallet: - -```sh -sudo dnf install sqlite-devel -``` +pass `--with-incompatible-bdb` to configure. Otherwise, you can build Berkeley DB [yourself](#berkeley-db). To build Dash Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode) @@ -251,8 +245,10 @@ turned off by default. Berkeley DB ----------- -It is recommended to use Berkeley DB 4.8. If you have to build it yourself, -you can use [the installation script included in contrib/](contrib/install_db4.sh) + +The legacy wallet uses Berkeley DB. To ensure backwards compatibility it is +recommended to use Berkeley DB 4.8. If you have to build it yourself, you can +use [the installation script included in contrib/](/contrib/install_db4.sh) like so: ```sh From 20d359b5706ca72c2edf27072355b4bb67b78e51 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 20 Jul 2024 10:42:17 +0000 Subject: [PATCH 05/18] partial bitcoin#23469: Remove Boost build note from build-unix.md Excludes changes to `systemtap` as USDT (bitcoin#19866) hasn't been backported yet --- doc/build-unix.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/doc/build-unix.md b/doc/build-unix.md index 607f9cf418..12ee3088a5 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -45,11 +45,11 @@ Optional dependencies: gmp | Optimized math routines | Arbitrary precision arithmetic library miniupnpc | UPnP Support | Firewall-jumping support libnatpmp | NAT-PMP Support | Firewall-jumping support - libdb4.8 | Berkeley DB | Optional, wallet storage (only needed when legacy wallet enabled) + libdb4.8 | Berkeley DB | Wallet storage (only needed when legacy wallet enabled) qt | GUI | GUI toolkit (only needed when GUI enabled) - libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled) - libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.0.0) - sqlite3 | SQLite DB | Optional, wallet storage (only needed when descriptor wallet enabled) + libqrencode | QR codes in GUI | QR code generation (only needed when GUI enabled) + libzmq3 | ZMQ notification | ZMQ notifications (requires ZMQ version >= 4.0.0) + sqlite3 | SQLite DB | Wallet storage (only needed when descriptor wallet enabled) For the versions used, see [dependencies.md](dependencies.md) @@ -261,15 +261,6 @@ Otherwise, you can build Dash Core from self-compiled [depends](/depends/README. **Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](#disable-wallet-mode)). -Boost ------ -If you need to build Boost yourself: - - sudo su - ./bootstrap.sh - ./bjam install - - Security -------- To help make your Dash Core installation more secure by making certain attacks impossible to From b0d2484a0b47e606fe409e7331e516dd26ec4eea Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 20 Jul 2024 10:43:13 +0000 Subject: [PATCH 06/18] merge bitcoin#23522: Improve fs::PathToString documentation --- doc/developer-notes.md | 6 ++++++ src/dbwrapper.cpp | 4 ++++ src/fs.h | 45 ++++++++++++++++++++++-------------------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 6d38831811..ce8652f529 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -1256,6 +1256,12 @@ A few guidelines for introducing and reviewing new RPC interfaces: - *Rationale*: User-facing consistency. +- Use `fs::path::u8string()` and `fs::u8path()` functions when converting path + to JSON strings, not `fs::PathToString` and `fs::PathFromString` + + - *Rationale*: JSON strings are Unicode strings, not byte strings, and + RFC8259 requires JSON to be encoded as UTF-8. + Internal interface guidelines ----------------------------- diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 77ecd87845..1c6efe7595 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -137,6 +137,10 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo TryCreateDirectories(path); LogPrintf("Opening LevelDB in %s\n", fs::PathToString(path)); } + // PathToString() return value is safe to pass to leveldb open function, + // because on POSIX leveldb passes the byte string directly to ::open(), and + // on Windows it converts from UTF-8 to UTF-16 before calling ::CreateFileW + // (see env_posix.cc and env_windows.cc). leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(path), &pdb); dbwrapper_private::HandleError(status); LogPrintf("Opened LevelDB successfully\n"); diff --git a/src/fs.h b/src/fs.h index 4a0bf39e95..3cf4371fb4 100644 --- a/src/fs.h +++ b/src/fs.h @@ -94,31 +94,34 @@ static inline path operator+(path p1, path p2) /** * Convert path object to byte string. On POSIX, paths natively are byte - * strings so this is trivial. On Windows, paths natively are Unicode, so an - * encoding step is necessary. + * strings, so this is trivial. On Windows, paths natively are Unicode, so an + * encoding step is necessary. The inverse of \ref PathToString is \ref + * PathFromString. The strings returned and parsed by these functions can be + * used to call POSIX APIs, and for roundtrip conversion, logging, and + * debugging. * - * The inverse of \ref PathToString is \ref PathFromString. The strings - * returned and parsed by these functions can be used to call POSIX APIs, and - * for roundtrip conversion, logging, and debugging. But they are not - * guaranteed to be valid UTF-8, and are generally meant to be used internally, - * not externally. When communicating with external programs and libraries that - * require UTF-8, fs::path::u8string() and fs::u8path() methods can be used. - * For other applications, if support for non UTF-8 paths is required, or if - * higher-level JSON or XML or URI or C-style escapes are preferred, it may be - * also be appropriate to use different path encoding functions. - * - * Implementation note: On Windows, the std::filesystem::path(string) - * constructor and std::filesystem::path::string() method are not safe to use - * here, because these methods encode the path using C++'s narrow multibyte - * encoding, which on Windows corresponds to the current "code page", which is - * unpredictable and typically not able to represent all valid paths. So - * std::filesystem::path::u8string() and std::filesystem::u8path() functions - * are used instead on Windows. On POSIX, u8string/u8path functions are not - * safe to use because paths are not always valid UTF-8, so plain string - * methods which do not transform the path there are used. + * Because \ref PathToString and \ref PathFromString functions don't specify an + * encoding, they are meant to be used internally, not externally. They are not + * appropriate to use in applications requiring UTF-8, where + * fs::path::u8string() and fs::u8path() methods should be used instead. Other + * applications could require still different encodings. For example, JSON, XML, + * or URI applications might prefer to use higher level escapes (\uXXXX or + * &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications + * may require encoding paths with their respective UTF-8 derivatives WTF-8, + * PEP-383, and CESU-8 (see https://en.wikipedia.org/wiki/UTF-8#Derivatives). */ static inline std::string PathToString(const path& path) { + // Implementation note: On Windows, the std::filesystem::path(string) + // constructor and std::filesystem::path::string() method are not safe to + // use here, because these methods encode the path using C++'s narrow + // multibyte encoding, which on Windows corresponds to the current "code + // page", which is unpredictable and typically not able to represent all + // valid paths. So std::filesystem::path::u8string() and + // std::filesystem::u8path() functions are used instead on Windows. On + // POSIX, u8string/u8path functions are not safe to use because paths are + // not always valid UTF-8, so plain string methods which do not transform + // the path there are used. #ifdef WIN32 return path.u8string(); #else From 7c270e68830562982045239a2b006a0503647a8a Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:40:25 +0000 Subject: [PATCH 07/18] merge bitcoin#24026: Block unsafe std::string fs::path conversion copy_file calls --- src/fs.h | 7 +++++++ src/interfaces/wallet.h | 4 ++-- src/wallet/interfaces.cpp | 2 +- src/wallet/rpcwallet.cpp | 2 +- src/wallet/wallet.cpp | 6 +++--- src/wallet/wallet.h | 3 ++- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/fs.h b/src/fs.h index 3cf4371fb4..80dd2c03d3 100644 --- a/src/fs.h +++ b/src/fs.h @@ -92,6 +92,13 @@ static inline path operator+(path p1, path p2) return p1; } +// Disallow implicit std::string conversion for copy_file +// to avoid locale-dependent encoding on Windows. +static inline void copy_file(const path& from, const path& to, copy_option options) +{ + boost::filesystem::copy_file(from, to, options); +} + /** * Convert path object to byte string. On POSIX, paths natively are byte * strings, so this is trivial. On Windows, paths natively are Unicode, so an diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 32fb6b28e8..3bae75ed6f 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -6,7 +6,7 @@ #define BITCOIN_INTERFACES_WALLET_H #include // For CAmount -#include // For fs::path +#include #include // For ChainClient #include // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation) #include