From 31347537436db09bbe113c2802a7cef11971d0e2 Mon Sep 17 00:00:00 2001 From: Peter John Bushnell Date: Fri, 22 Mar 2024 04:11:27 +0000 Subject: [PATCH] Remove Boost filesystem and system (#2747) * Forbid fs::path(std::string) and fs::path::string(). Add AbsPathJoin. * Remove Boost filesystem and system * Fix symbolic link support * lint: set correct include names * lint: format Python * Fix compilation on Windows * Move AbsPathJoin so Windows can find it * Correctly decode literal string --------- Co-authored-by: Prasanna Loganathar --- build-aux/m4/ax_boost_filesystem.m4 | 119 ---------- build-aux/m4/ax_boost_system.m4 | 121 ---------- configure.ac | 4 +- .../draft/x86_64-alpine-linux-musl.dockerfile | 2 +- depends/packages/boost.mk | 2 +- doc/build-unix.md | 2 +- make.sh | 4 +- src/Makefile.am | 2 + src/addrdb.cpp | 7 +- src/chainparams.cpp | 3 +- src/dbwrapper.cpp | 18 +- src/defi-cli.cpp | 2 +- src/flatfile.cpp | 4 +- src/fs.cpp | 120 ++-------- src/fs.h | 218 +++++++++++++----- src/init.cpp | 60 ++--- src/logging.h | 1 + src/net.cpp | 1 + src/rpc/request.cpp | 17 +- src/rpc/server.cpp | 2 +- src/rpc/stats.cpp | 6 +- src/spv/spv_wrapper.cpp | 2 +- src/test/dbwrapper_tests.cpp | 4 +- src/test/fs_tests.cpp | 82 ++++++- src/test/setup_common.cpp | 2 +- src/test/streams_tests.cpp | 1 + src/test/util_tests.cpp | 25 +- src/torcontrol.cpp | 8 +- src/util/getuniquepath.cpp | 15 ++ src/util/getuniquepath.h | 20 ++ src/util/system.cpp | 64 ++--- src/wallet/db.cpp | 45 ++-- src/wallet/db.h | 2 +- src/wallet/load.cpp | 16 +- src/wallet/rpcdump.cpp | 17 +- src/wallet/rpcwallet.cpp | 4 +- src/wallet/test/db_tests.cpp | 7 +- src/wallet/test/init_test_fixture.cpp | 11 +- src/wallet/test/init_tests.cpp | 11 +- src/wallet/test/wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 10 +- src/wallet/walletdb.cpp | 12 +- src/wallet/wallettool.cpp | 2 +- src/wallet/walletutil.cpp | 34 +-- src/wallet/walletutil.h | 4 - test/functional/wallet_multiwallet.py | 5 +- test/lint/lint-includes.sh | 2 - 47 files changed, 530 insertions(+), 592 deletions(-) delete mode 100644 build-aux/m4/ax_boost_filesystem.m4 delete mode 100644 build-aux/m4/ax_boost_system.m4 create mode 100644 src/util/getuniquepath.cpp create mode 100644 src/util/getuniquepath.h diff --git a/build-aux/m4/ax_boost_filesystem.m4 b/build-aux/m4/ax_boost_filesystem.m4 deleted file mode 100644 index f5c9d56470b..00000000000 --- a/build-aux/m4/ax_boost_filesystem.m4 +++ /dev/null @@ -1,119 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_filesystem.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_FILESYSTEM -# -# DESCRIPTION -# -# Test for Filesystem library from the Boost C++ libraries. The macro -# requires a preceding call to AX_BOOST_BASE. Further documentation is -# available at . -# -# This macro calls: -# -# AC_SUBST(BOOST_FILESYSTEM_LIB) -# -# And sets: -# -# HAVE_BOOST_FILESYSTEM -# -# LICENSE -# -# Copyright (c) 2009 Thomas Porschberg -# Copyright (c) 2009 Michael Tindal -# Copyright (c) 2009 Roman Rybalko -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 26 - -AC_DEFUN([AX_BOOST_FILESYSTEM], -[ - AC_ARG_WITH([boost-filesystem], - AS_HELP_STRING([--with-boost-filesystem@<:@=special-lib@:>@], - [use the Filesystem library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-filesystem=boost_filesystem-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_filesystem_lib="" - else - want_boost="yes" - ax_boost_user_filesystem_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - LIBS_SAVED=$LIBS - LIBS="$LIBS $BOOST_SYSTEM_LIB" - export LIBS - - AC_CACHE_CHECK(whether the Boost::Filesystem library is available, - ax_cv_boost_filesystem, - [AC_LANG_PUSH([C++]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[using namespace boost::filesystem; - path my_path( "foo/bar/data.txt" ); - return 0;]])], - ax_cv_boost_filesystem=yes, ax_cv_boost_filesystem=no) - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_filesystem" = "xyes"; then - AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - ax_lib= - if test "x$ax_boost_user_filesystem_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_filesystem* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], - [link_filesystem="no"]) - done - if test "x$link_filesystem" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_filesystem* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], - [link_filesystem="no"]) - done - fi - else - for ax_lib in $ax_boost_user_filesystem_lib boost_filesystem-$ax_boost_user_filesystem_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], - [link_filesystem="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_filesystem library!) - fi - if test "x$link_filesystem" != "xyes"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - LIBS="$LIBS_SAVED" - fi -]) diff --git a/build-aux/m4/ax_boost_system.m4 b/build-aux/m4/ax_boost_system.m4 deleted file mode 100644 index 207d7be8de4..00000000000 --- a/build-aux/m4/ax_boost_system.m4 +++ /dev/null @@ -1,121 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_boost_system.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_SYSTEM -# -# DESCRIPTION -# -# Test for System library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_SYSTEM_LIB) -# -# And sets: -# -# HAVE_BOOST_SYSTEM -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2008 Michael Tindal -# Copyright (c) 2008 Daniel Casimiro -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 19 - -AC_DEFUN([AX_BOOST_SYSTEM], -[ - AC_ARG_WITH([boost-system], - AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], - [use the System library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-system=boost_system-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_system_lib="" - else - want_boost="yes" - ax_boost_user_system_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::System library is available, - ax_cv_boost_system, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - CXXFLAGS= - - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::system::error_category *a = 0;]])], - ax_cv_boost_system=yes, ax_cv_boost_system=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_system" = "xyes"; then - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - if test "x$ax_boost_user_system_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - if test "x$link_system" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_system" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/configure.ac b/configure.ac index a0640dad647..41fd3f69c2c 100644 --- a/configure.ac +++ b/configure.ac @@ -1012,8 +1012,6 @@ AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST]) if test x$want_boost = xno; then AC_MSG_ERROR([[cannot be built without boost]]) fi -AX_BOOST_SYSTEM -AX_BOOST_FILESYSTEM dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic dnl counter implementations. In 1.63 and later the std::atomic approach is default. @@ -1068,7 +1066,7 @@ if test x$use_boost = xyes; then AX_CHECK_PREPROC_FLAG([-DBOOST_NO_CXX98_FUNCTION_BASE], [BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_CXX98_FUNCTION_BASE"], [], [$CXXFLAG_WERROR], [AC_LANG_PROGRAM([[#include ]])]) - BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB" + BOOST_LIBS="$BOOST_LDFLAGS" LIBS="$BOOST_LIBS $LIBS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" diff --git a/contrib/dockerfiles/draft/x86_64-alpine-linux-musl.dockerfile b/contrib/dockerfiles/draft/x86_64-alpine-linux-musl.dockerfile index b128c3638c6..2d0fa930b76 100644 --- a/contrib/dockerfiles/draft/x86_64-alpine-linux-musl.dockerfile +++ b/contrib/dockerfiles/draft/x86_64-alpine-linux-musl.dockerfile @@ -14,7 +14,7 @@ RUN apk add --update alpine-sdk RUN apk add --no-cache bash gcc git libffi musl-dev libffi-dev autoconf automake RUN apk add --no-cache openssh-client make db-dev openssl openssl-dev RUN apk add --no-cache libtool libevent libevent-dev -RUN apk add --no-cache boost boost-dev boost-system boost-filesystem +RUN apk add --no-cache boost boost-dev COPY . . diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 620241fe795..47d62026e50 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -27,7 +27,7 @@ $(package)_toolset_$(host_os)=gcc $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) -$(package)_config_libraries=filesystem,system,test +$(package)_config_libraries=test $(package)_cxxflags_linux=-fPIC endef diff --git a/doc/build-unix.md b/doc/build-unix.md index 45104e1fff4..c3262abbcd7 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -81,7 +81,7 @@ Build requirements: Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies: - sudo apt-get install libssl-dev libevent-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev + sudo apt-get install libssl-dev libevent-dev libboost-test-dev Rust toolchain is necessary for the build: diff --git a/make.sh b/make.sh index 0934dda50ff..625d5c3ae6f 100755 --- a/make.sh +++ b/make.sh @@ -690,8 +690,8 @@ pkg_install_deps() { # python3-venv for settings up all python deps apt-get install -y \ software-properties-common build-essential git libtool autotools-dev automake \ - pkg-config bsdmainutils python3 python3-pip python3-venv libssl-dev libevent-dev libboost-system-dev \ - libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev \ + pkg-config bsdmainutils python3 python3-pip python3-venv libssl-dev libevent-dev \ + libboost-chrono-dev libboost-test-dev libboost-thread-dev \ libminiupnpc-dev libzmq3-dev libqrencode-dev wget ccache \ libdb-dev libdb++-dev libdb5.3 libdb5.3-dev libdb5.3++ libdb5.3++-dev \ curl cmake zip unzip libc6-dev gcc-multilib locales locales-all diff --git a/src/Makefile.am b/src/Makefile.am index 1d823f97df6..5f767e681ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -278,6 +278,7 @@ DEFI_CORE_H = \ util/bytevectorhash.h \ util/error.h \ util/fees.h \ + util/getuniquepath.h \ util/system.h \ util/moneystr.h \ util/rbf.h \ @@ -699,6 +700,7 @@ libdefi_util_a_SOURCES = \ util/bytevectorhash.cpp \ util/error.cpp \ util/fees.cpp \ + util/getuniquepath.cpp \ util/system.cpp \ util/moneystr.cpp \ util/rbf.cpp \ diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 50e8ab2efa7..9b0750c0aa4 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,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 @@ -59,7 +60,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(); @@ -110,7 +111,7 @@ bool DeserializeFileDB(const fs::path& path, Data& data) FILE *file = fsbridge::fopen(path, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, path.string()); + return error("%s: Failed to open file %s", __func__, fs::PathToString(path)); return DeserializeDB(filein, data); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ea477bf3970..d6d6e3da508 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -1478,7 +1479,7 @@ void ClearCheckpoints(CChainParams ¶ms) { } Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName) { - std::ifstream file(fileName); + std::ifstream file(fs::PathFromString(fileName)); if (!file.good()) { return Res::Err("Could not read %s. Ensure it exists and has read permissions", fileName); } diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index f8bc56a1482..603a664f957 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -125,7 +125,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; @@ -143,21 +143,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. @@ -174,10 +174,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/defi-cli.cpp b/src/defi-cli.cpp index 5b4a0393e34..c0168a11ab0 100644 --- a/src/defi-cli.cpp +++ b/src/defi-cli.cpp @@ -407,7 +407,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", DEFI_CONF_FILENAME)).string().c_str())); + fs::PathToString(GetConfigFile(gArgs.GetArg("-conf", DEFI_CONF_FILENAME))))); } else { throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword"); } diff --git a/src/flatfile.cpp b/src/flatfile.cpp index 887ae9999fb..b320b4ec55f 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 9fa97bc271e..66be89951b2 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,14 +1,18 @@ #include #ifndef WIN32 +#include #include +#include +#include +#include #else #ifndef NOMINMAX #define NOMINMAX #endif #include -#include #include +#include #endif namespace fsbridge { @@ -16,13 +20,19 @@ 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()); #endif } +fs::path AbsPathJoin(const fs::path& base, const fs::path& path) +{ + assert(base.is_absolute()); + return path.empty() ? base : fs::path(base / path); +} + #ifndef WIN32 static std::string GetErrorReason() { @@ -31,7 +41,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(); } @@ -117,108 +127,4 @@ std::string get_filesystem_error_message(const fs::filesystem_error& e) #endif } -#ifdef WIN32 -#ifdef __GLIBCXX__ - -// reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270 - -static std::string openmodeToStr(std::ios_base::openmode mode) -{ - switch (mode & ~std::ios_base::ate) { - case std::ios_base::out: - case std::ios_base::out | std::ios_base::trunc: - return "w"; - case std::ios_base::out | std::ios_base::app: - case std::ios_base::app: - return "a"; - case std::ios_base::in: - return "r"; - case std::ios_base::in | std::ios_base::out: - return "r+"; - case std::ios_base::in | std::ios_base::out | std::ios_base::trunc: - return "w+"; - case std::ios_base::in | std::ios_base::out | std::ios_base::app: - case std::ios_base::in | std::ios_base::app: - return "a+"; - case std::ios_base::out | std::ios_base::binary: - case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary: - return "wb"; - case std::ios_base::out | std::ios_base::app | std::ios_base::binary: - case std::ios_base::app | std::ios_base::binary: - return "ab"; - case std::ios_base::in | std::ios_base::binary: - return "rb"; - case std::ios_base::in | std::ios_base::out | std::ios_base::binary: - return "r+b"; - case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary: - return "w+b"; - case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary: - case std::ios_base::in | std::ios_base::app | std::ios_base::binary: - return "a+b"; - default: - return std::string(); - } -} - -void ifstream::open(const fs::path& p, std::ios_base::openmode mode) -{ - close(); - mode |= std::ios_base::in; - m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str()); - if (m_file == nullptr) { - return; - } - m_filebuf = __gnu_cxx::stdio_filebuf(m_file, mode); - rdbuf(&m_filebuf); - if (mode & std::ios_base::ate) { - seekg(0, std::ios_base::end); - } -} - -void ifstream::close() -{ - if (m_file != nullptr) { - m_filebuf.close(); - fclose(m_file); - } - m_file = nullptr; -} - -void ofstream::open(const fs::path& p, std::ios_base::openmode mode) -{ - close(); - mode |= std::ios_base::out; - m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str()); - if (m_file == nullptr) { - return; - } - m_filebuf = __gnu_cxx::stdio_filebuf(m_file, mode); - rdbuf(&m_filebuf); - if (mode & std::ios_base::ate) { - seekp(0, std::ios_base::end); - } -} - -void ofstream::close() -{ - if (m_file != nullptr) { - m_filebuf.close(); - fclose(m_file); - } - m_file = nullptr; -} -#else // __GLIBCXX__ - -static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t), - "Warning: This build is using boost::filesystem ofstream and ifstream " - "implementations which will fail to open paths containing multibyte " - "characters. You should delete this static_assert to ignore this warning, " - "or switch to a different C++ standard library like the Microsoft C++ " - "Standard Library (where boost uses non-standard extensions to construct " - "stream objects with wide filenames), or the GNU libstdc++ library (where " - "a more complicated workaround has been implemented above)."); - -#endif // __GLIBCXX__ -#endif // WIN32 - } // fsbridge diff --git a/src/fs.h b/src/fs.h index 362f7895468..b6497d8b7c8 100644 --- a/src/fs.h +++ b/src/fs.h @@ -5,22 +5,171 @@ #ifndef DEFI_FS_H #define DEFI_FS_H -#include +#include + +#include +#include +#include +#include +#include #include -#if defined WIN32 && defined __GLIBCXX__ -#include -#endif +#include -#include -#include +/** Filesystem operations and types */ /** Filesystem operations and types */ -namespace fs = boost::filesystem; +namespace fs { + + using namespace std::filesystem; + + /** + * Path class wrapper to block calls to the fs::path(std::string) implicit + * constructor and the fs::path::string() method, which have unsafe and + * unpredictable behavior on Windows (see implementation note in + * \ref PathToString for details) + */ + class path : public std::filesystem::path + { + public: + using std::filesystem::path::path; + + // Allow path objects arguments for compatibility. + path(std::filesystem::path path) : std::filesystem::path::path(std::move(path)) {} + path& operator=(std::filesystem::path path) { std::filesystem::path::operator=(std::move(path)); return *this; } + path& operator/=(std::filesystem::path path) { std::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) : std::filesystem::path(c) {} + path& operator=(const char* c) { std::filesystem::path::operator=(c); return *this; } + path& operator/=(const char* c) { std::filesystem::path::operator/=(c); return *this; } + path& append(const char* c) { std::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; + + // Required for path overloads in . + // See https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=96e0367ead5d8dcac3bec2865582e76e2fbab190 + path& make_preferred() { std::filesystem::path::make_preferred(); return *this; } + path filename() const { return std::filesystem::path::filename(); } + }; + + // Disallow implicit std::string conversion for absolute to avoid + // locale-dependent encoding on windows. + static inline path absolute(const path& p) + { + return std::filesystem::absolute(p); + } + + // Disallow implicit std::string conversion for exists to avoid + // locale-dependent encoding on windows. + static inline bool exists(const path& p) + { + return std::filesystem::exists(p); + } + + // Allow explicit quoted stream I/O. + static inline auto quoted(const std::string& s) + { + return std::quoted(s, '"', '&'); + } + + // Allow safe path append operations. + static inline path operator+(path p1, path p2) + { + p1 += std::move(p2); + return p1; + } + + // Disallow implicit std::string conversion for copy_file + // to avoid locale-dependent encoding on Windows. + static inline bool copy_file(const path& from, const path& to, copy_options options) + { + return std::filesystem::copy_file(from, to, options); + } + + /** + * Convert path object to a 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. + * + * 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) + { +#ifdef WIN32 + return path.u8string(); +#else + static_assert(std::is_same::value, "PathToString not implemented on this platform"); + return path.std::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 std::filesystem::path(string); +#endif + } + + /** + * Create directory (and if necessary its parents), unless the leaf directory + * already exists or is a symlink to an existing directory. + * This is a temporary workaround for an issue in libstdc++ that has been fixed + * upstream [PR101510]. + */ + static inline bool create_directories(const std::filesystem::path& p) + { + if (std::filesystem::is_symlink(p) && std::filesystem::is_directory(p)) { + return false; + } + return std::filesystem::create_directories(p); + } + + /** + * This variant is not used. Delete it to prevent it from accidentally working + * around the workaround. If it is needed, add a workaround in the same pattern + * as above. + */ + bool create_directories(const std::filesystem::path& p, std::error_code& ec) = delete; +} // namespace fs /** Bridge operations to C stdio */ namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode); + /** + * Helper function for joining two paths + * + * @param[in] base Base path + * @param[in] path Path to combine with base + * @returns path unchanged if it is an absolute path, otherwise returns base joined with path. Returns base unchanged if path is empty. + * @pre Base path must be absolute + * @post Returned path will always be absolute + */ + fs::path AbsPathJoin(const fs::path& base, const fs::path& path); + class FileLock { public: @@ -42,54 +191,13 @@ namespace fsbridge { }; std::string get_filesystem_error_message(const fs::filesystem_error& e); - - // GNU libstdc++ specific workaround for opening UTF-8 paths on Windows. - // - // On Windows, it is only possible to reliably access multibyte file paths through - // `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't - // require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't - // provide them (in contrast to the Microsoft C++ library, see - // https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032), - // Boost is forced to fall back to `char` constructors which may not work properly. - // - // Work around this issue by creating stream objects with `_wfopen` in - // combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed - // with an upgrade to C++17, where streams can be constructed directly from - // `std::filesystem::path` objects. - -#if defined WIN32 && defined __GLIBCXX__ - class ifstream : public std::istream - { - public: - ifstream() = default; - explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); } - ~ifstream() { close(); } - void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in); - bool is_open() { return m_filebuf.is_open(); } - void close(); - - private: - __gnu_cxx::stdio_filebuf m_filebuf; - FILE* m_file = nullptr; - }; - class ofstream : public std::ostream - { - public: - ofstream() = default; - explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); } - ~ofstream() { close(); } - void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out); - bool is_open() { return m_filebuf.is_open(); } - void close(); - - private: - __gnu_cxx::stdio_filebuf m_filebuf; - FILE* m_file = nullptr; - }; -#else // !(WIN32 && __GLIBCXX__) - typedef fs::ifstream ifstream; - typedef fs::ofstream ofstream; -#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 std::filesystem::path&) = delete; + template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete; +} // namespace tinyformat + #endif // DEFI_FS_H diff --git a/src/init.cpp b/src/init.cpp index 16346b220ed..a78ee6fcddb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -67,8 +67,11 @@ #include #include -#include -#include +#include +#include +#include +#include +#include #ifndef WIN32 #include @@ -116,12 +119,12 @@ static const char* DEFI_PID_FILENAME = "defid.pid"; static fs::path GetPidFile() { - return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", DEFI_PID_FILENAME))); + return AbsPathForConfigVal(fs::PathFromString(gArgs.GetArg("-pid", DEFI_PID_FILENAME))); } NODISCARD static bool CreatePidFile() { - fsbridge::ofstream file{GetPidFile()}; + std::ofstream file{GetPidFile()}; if (file) { #ifdef WIN32 tfm::format(file, "%d\n", GetCurrentProcessId()); @@ -130,7 +133,7 @@ NODISCARD static bool CreatePidFile() #endif return true; } else { - return InitError(strprintf(_("Unable to create the PID file '%s': %s").translated, GetPidFile().string(), std::strerror(errno))); + return InitError(strprintf(_("Unable to create the PID file '%s': %s").translated, fs::PathToString(GetPidFile()), std::strerror(errno))); } } @@ -266,7 +269,7 @@ void Shutdown(InitInterfaces& interfaces) if (!est_fileout.IsNull()) ::feeEstimator.Write(est_fileout); else - LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string()); + LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, fs::PathToString(est_path)); fFeeEstimatesInitialized = false; } @@ -761,13 +764,14 @@ static void CleanupBlockRevFiles() LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); fs::path blocksdir = GetBlocksDir(); 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()); } } @@ -834,7 +838,7 @@ static void ThreadImport(std::vector vImportFiles) } RenameOver(pathBootstrap, pathBootstrapOld); } else { - LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string()); + LogPrintf("Warning: Could not open bootstrap file %s\n", fs::PathToString(pathBootstrap)); } } @@ -842,14 +846,14 @@ static void ThreadImport(std::vector vImportFiles) 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)); LoadExternalBlockFile(chainparams, 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)); } } @@ -1019,7 +1023,7 @@ void InitParameterInteraction() void InitLogging() { LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile"); - LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + LogInstance().m_file_path = AbsPathForConfigVal(fs::PathFromString(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE))); LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); @@ -1363,10 +1367,10 @@ static bool LockDataDirectory(bool probeOnly) // Make sure only a single DeFi Blockchain process is using the data directory. fs::path datadir = GetDataDir(); if (!DirIsWritable(datadir)) { - return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions.").translated, datadir.string())); + return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions.").translated, fs::PathToString(datadir))); } if (!LockDirectory(datadir, ".lock", probeOnly)) { - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.").translated, datadir.string(), PACKAGE_NAME)); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.").translated, fs::PathToString(datadir), PACKAGE_NAME)); } return true; } @@ -1414,13 +1418,13 @@ bool SetupLogging() { } if (!LogInstance().StartLogging()) { return InitError(strprintf("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", GetDataDir().string()); + LogPrintf("Default data directory %s\n", fs::PathToString(GetDefaultDataDir())); + LogPrintf("Using data directory %s\n", fs::PathToString(GetDataDir())); return true; } @@ -1730,24 +1734,24 @@ bool AppInitMain(InitInterfaces& interfaces) // Only log conf file usage message if conf file actually exists. fs::path config_file_path = GetConfigFile(gArgs.GetArg("-conf", DEFI_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 (gArgs.IsArgSet("-conf")) { // Warn if no conf file exists at path provided by user - InitWarning(strprintf(_("The specified config file %s does not exist\n").translated, config_file_path.string())); + InitWarning(strprintf(_("The specified config file %s does not exist\n").translated, 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)); } LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. - if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) { + if (gArgs.IsArgSet("-datadir") && !fs::PathFromString(gArgs.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 defid 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 defi is started while in a temporary directory.\n", - gArgs.GetArg("-datadir", ""), fs::current_path().string()); + gArgs.GetArg("-datadir", ""), fs::PathToString(fs::current_path())); } InitSignatureCache(); @@ -2222,11 +2226,11 @@ bool AppInitMain(InitInterfaces& interfaces) // ********************************************************* Step 12: import blocks if (!CheckDiskSpace(GetDataDir())) { - InitError(strprintf(_("Error: Disk space is low for %s").translated, GetDataDir())); + InitError(strprintf(_("Error: Disk space is low for %s").translated, fs::quoted(fs::PathToString(GetDataDir())))); return false; } if (!CheckDiskSpace(GetBlocksDir())) { - InitError(strprintf(_("Error: Disk space is low for %s").translated, GetBlocksDir())); + InitError(strprintf(_("Error: Disk space is low for %s").translated, fs::quoted(fs::PathToString(GetBlocksDir())))); return false; } @@ -2246,7 +2250,7 @@ bool AppInitMain(InitInterfaces& interfaces) std::vector vImportFiles; for (const std::string& strFile : gArgs.GetArgs("-loadblock")) { - vImportFiles.push_back(strFile); + vImportFiles.push_back(fs::PathFromString(strFile)); } threadGroup.emplace_back(ThreadImport, vImportFiles); diff --git a/src/logging.h b/src/logging.h index 5c3997fcc91..9365988c00a 100644 --- a/src/logging.h +++ b/src/logging.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/src/net.cpp b/src/net.cpp index e6daa84b144..677d7f6ba71 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp index dc9e1c13a6a..33572926a97 100644 --- a/src/rpc/request.cpp +++ b/src/rpc/request.cpp @@ -12,6 +12,11 @@ #include #include +#include +#include +#include +#include + /** * JSON-RPC protocol. Defi speaks version 1.0 for maximum compatibility, * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were @@ -70,7 +75,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) @@ -83,11 +88,11 @@ bool GenerateAuthCookie(std::string *cookie_out) /** the umask determines what permissions are used to create this file - * these are set to 077 in init.cpp unless overridden with -sysperms. */ - fsbridge::ofstream file; + std::ofstream file; 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 +100,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; @@ -107,7 +112,7 @@ bool GenerateAuthCookie(std::string *cookie_out) bool GetAuthCookie(std::string *cookie_out) { - fsbridge::ifstream file; + std::ifstream file; std::string cookie; fs::path filepath = GetAuthCookieFile(); file.open(filepath); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 3a412bf9a52..57793ba460a 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -227,7 +227,7 @@ static UniValue getrpcinfo(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); result.pushKV("active_commands", active_commands); - const std::string path = LogInstance().m_file_path.string(); + const std::string path = fs::PathToString(LogInstance().m_file_path); UniValue log_path(UniValue::VSTR, path); result.pushKV("logpath", log_path); diff --git a/src/rpc/stats.cpp b/src/rpc/stats.cpp index e9b8770c2ad..161e20937a8 100644 --- a/src/rpc/stats.cpp +++ b/src/rpc/stats.cpp @@ -2,6 +2,8 @@ #include #include +#include + bool CRPCStats::isActive() { return active.load(); } void CRPCStats::setActive(bool isActive) { active.store(isActive); } @@ -22,7 +24,7 @@ std::map CRPCStats::getMap() { void CRPCStats::save() { fs::path statsPath = GetDataDir() / DEFAULT_STATSFILE; - fsbridge::ofstream file(statsPath); + std::ofstream file(statsPath); file << toJSON().write() << '\n'; file.close(); @@ -30,7 +32,7 @@ void CRPCStats::save() { void CRPCStats::load() { fs::path statsPath = GetDataDir() / DEFAULT_STATSFILE; - fsbridge::ifstream file(statsPath); + std::ifstream file(statsPath); if (!file.is_open()) return; std::string line; diff --git a/src/spv/spv_wrapper.cpp b/src/spv/spv_wrapper.cpp index c8be9233e66..6a7b74283a8 100644 --- a/src/spv/spv_wrapper.cpp +++ b/src/spv/spv_wrapper.cpp @@ -206,7 +206,7 @@ CSpvWrapper::CSpvWrapper(bool isMainnet, size_t nCacheSize, bool fMemory, bool f // Configuring spv logs: // (we need intermediate persistent storage for filename here) - spv_internal_logfilename = AbsPathForConfigVal("spv.log").string(); + spv_internal_logfilename = fs::PathToString(AbsPathForConfigVal(fs::PathFromString("spv.log"))); spv_logfilename = spv_internal_logfilename.c_str(); LogPrint(BCLog::SPV, "internal logs set to %s\n", spv_logfilename); spv_log2console = 0; diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 5073f82bdb2..ae27ff4fa13 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) { // We're going to share this fs::path between two wrappers fs::path ph = GetDataDir() / "existing_data_no_obfuscate"; - create_directories(ph); + fs::create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. std::unique_ptr dbw = std::make_unique(ph, (1 << 10), false, false, false); @@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) { // We're going to share this fs::path between two wrappers fs::path ph = GetDataDir() / "existing_data_reindex"; - create_directories(ph); + fs::create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. std::unique_ptr dbw = std::make_unique(ph, (1 << 10), false, false, false); diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp index 09002cac9c6..637e82ea460 100644 --- a/src/test/fs_tests.cpp +++ b/src/test/fs_tests.cpp @@ -5,53 +5,117 @@ #include #include #include +#include #include +#include +#include +#include + 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 = GetDataDir(); // tmpfile1 should be the same as tmpfile2 - fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃"; - fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃"; + fs::path tmpfile1 = tmpfolder / fs::u8path("fs_tests_₿_🏃"); + fs::path tmpfile2 = tmpfolder / fs::u8path("fs_tests_₿_🏃"); { - fsbridge::ofstream file(tmpfile1); + std::ofstream file(tmpfile1); file << "defi"; } { - fsbridge::ifstream file(tmpfile2); + std::ifstream file(tmpfile2); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "defi"); } { - fsbridge::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate); + std::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, ""); } { - fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app); + std::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app); file << "tests"; } { - fsbridge::ifstream file(tmpfile1); + std::ifstream file(tmpfile1); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "defitests"); } { - fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc); + std::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc); file << "defi"; } { - fsbridge::ifstream file(tmpfile1); + std::ifstream file(tmpfile1); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "defi"); } + { + // Join an absolute path and a relative path. + fs::path p = fsbridge::AbsPathJoin(tmpfolder, fs::u8path("fs_tests_₿_🏃")); + BOOST_CHECK(p.is_absolute()); + BOOST_CHECK_EQUAL(tmpfile1, p); + } + { + // Join two absolute paths. + fs::path p = fsbridge::AbsPathJoin(tmpfile1, tmpfile2); + BOOST_CHECK(p.is_absolute()); + BOOST_CHECK_EQUAL(tmpfile2, p); + } + { + // Ensure joining with empty paths does not add trailing path components. + BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, "")); + BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, {})); + } + { + fs::path p1 = GetUniquePath(tmpfolder); + fs::path p2 = GetUniquePath(tmpfolder); + fs::path p3 = GetUniquePath(tmpfolder); + + // Ensure that the parent path is always the same. + BOOST_CHECK_EQUAL(tmpfolder, p1.parent_path()); + BOOST_CHECK_EQUAL(tmpfolder, p2.parent_path()); + BOOST_CHECK_EQUAL(tmpfolder, p3.parent_path()); + + // Ensure that generated paths are actually different. + BOOST_CHECK(p1 != p2); + BOOST_CHECK(p2 != p3); + BOOST_CHECK(p1 != p3); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp index e3e9f895285..32a945ad7ba 100644 --- a/src/test/setup_common.cpp +++ b/src/test/setup_common.cpp @@ -71,7 +71,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) : m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30)))) { fs::create_directories(m_path_root); - gArgs.ForceSetArg("-datadir", m_path_root.string()); + gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root)); ClearDatadirCache(); SelectParams(chainName); gArgs.ForceSetArg("-printtoconsole", "0"); diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index cb42807623f..92e91dd23f7 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. +#include #include #include diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 0383fa185de..2c0f95d12a1 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,28 @@ BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(util_datadir) +{ + ClearDatadirCache(); + const fs::path dd_norm = GetDataDir(); + + gArgs.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/"); + ClearDatadirCache(); + BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + + gArgs.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/."); + ClearDatadirCache(); + BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + + gArgs.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./"); + ClearDatadirCache(); + BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); + + gArgs.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//"); + ClearDatadirCache(); + BOOST_CHECK_EQUAL(dd_norm, GetDataDir()); +} + BOOST_AUTO_TEST_CASE(util_criticalsection) { CCriticalSection cs; @@ -1528,7 +1551,7 @@ BOOST_AUTO_TEST_CASE(test_DirIsWritable) BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true); // Should not be able to write to a non-existent dir. - tmpdirname = tmpdirname / fs::unique_path(); + tmpdirname = GetUniquePath(tmpdirname); BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false); fs::create_directory(tmpdirname); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 494abdaf71d..8b752d34e10 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -466,7 +466,7 @@ TorController::TorController(struct event_base* _base, const std::string& _targe // 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; } } @@ -504,9 +504,9 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe service = LookupNumeric(std::string(service_id+".onion").c_str(), 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 @@ -655,7 +655,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/getuniquepath.cpp b/src/util/getuniquepath.cpp new file mode 100644 index 00000000000..657c9076a87 --- /dev/null +++ b/src/util/getuniquepath.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2021-2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +fs::path GetUniquePath(const fs::path& base) +{ + FastRandomContext rnd; + fs::path tmpFile = base / fs::u8path(HexStr(rnd.randbytes(8))); + return tmpFile; +} + diff --git a/src/util/getuniquepath.h b/src/util/getuniquepath.h new file mode 100644 index 00000000000..bf063c121d8 --- /dev/null +++ b/src/util/getuniquepath.h @@ -0,0 +1,20 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef DEFI_UTIL_GETUNIQUEPATH_H +#define DEFI_UTIL_GETUNIQUEPATH_H + +#include + +/** + * Helper function for getting a unique path + * + * @param[in] base Base path + * @returns base joined with a random 8-character long string. + * @post Returned path is unique with high probability. + */ +fs::path GetUniquePath(const fs::path& base); + +#endif // DEFI_UTIL_GETUNIQUEPATH_H + diff --git a/src/util/system.cpp b/src/util/system.cpp index cd2b45fb329..8cf5840638b 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include @@ -29,6 +31,8 @@ #endif // __linux__ #include +#include +#include #include #include #include @@ -63,6 +67,10 @@ #include #endif +#include +#include +#include +#include #include // Application startup time (used for uptime calculation) @@ -87,7 +95,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; } @@ -96,11 +104,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; } @@ -108,7 +116,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name) { std::lock_guard lock(cs_dir_locks); - dir_locks.erase((directory / lockfile_name).string()); + dir_locks.erase(fs::PathToString(directory / lockfile_name)); } void ReleaseDirectoryLocks() @@ -119,7 +127,7 @@ void ReleaseDirectoryLocks() bool DirIsWritable(const fs::path& directory) { - fs::path tmpFile = directory / fs::unique_path(); + fs::path tmpFile = GetUniquePath(directory); FILE* file = fsbridge::fopen(tmpFile, "a"); if (!file) return false; @@ -314,6 +322,19 @@ NODISCARD static bool InterpretOption(std::string key, std::string val, unsigned return true; } +namespace { + fs::path StripRedundantLastElementsOfPath(const fs::path& path) + { + auto result = path; + while (result.filename().empty() || fs::PathToString(result.filename()) == ".") { + result = result.parent_path(); + } + + assert(fs::equivalent(result, path)); + return result; + } +} // namespace + ArgsManager::ArgsManager() { // nothing to do @@ -740,7 +761,7 @@ const fs::path &GetBlocksDir() if (!path.empty()) return path; if (gArgs.IsArgSet("-blocksdir")) { - path = fs::system_complete(gArgs.GetArg("-blocksdir", "")); + path = fs::absolute(fs::PathFromString(gArgs.GetArg("-blocksdir", ""))); if (!fs::is_directory(path)) { path = ""; return path; @@ -749,7 +770,7 @@ const fs::path &GetBlocksDir() path = GetDataDir(false); } - path /= BaseParams().DataDir(); + path /= fs::PathFromString(BaseParams().DataDir()); path /= "blocks"; fs::create_directories(path); return path; @@ -766,7 +787,7 @@ const fs::path &GetDataDir(bool fNetSpecific) std::string datadir = gArgs.GetArg("-datadir", ""); if (!datadir.empty()) { - path = fs::system_complete(datadir); + path = fs::absolute(StripRedundantLastElementsOfPath(fs::PathFromString(datadir))); if (!fs::is_directory(path)) { path = ""; return path; @@ -775,20 +796,21 @@ const fs::path &GetDataDir(bool fNetSpecific) path = GetDefaultDataDir(); } if (fNetSpecific) - path /= BaseParams().DataDir(); + path /= fs::PathFromString(BaseParams().DataDir()); if (fs::create_directories(path)) { // This is the first run, create wallets subdirectory too fs::create_directories(path / "wallets"); } + path = StripRedundantLastElementsOfPath(path); return path; } 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::absolute(fs::PathFromString(datadir))); } void ClearDatadirCache() @@ -802,7 +824,7 @@ void ClearDatadirCache() fs::path GetConfigFile(const std::string& confPath) { - return AbsPathForConfigVal(fs::path(confPath), false); + return AbsPathForConfigVal(fs::PathFromString(confPath), false); } static std::string TrimString(const std::string& str, const std::string& pattern) @@ -895,7 +917,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } const std::string confPath = GetArg("-conf", DEFI_CONF_FILENAME); - fsbridge::ifstream stream(GetConfigFile(confPath)); + std::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file if (stream.good()) { @@ -928,7 +950,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } for (const std::string& to_include : includeconf) { - fsbridge::ifstream include_config(GetConfigFile(to_include)); + std::ifstream include_config(GetConfigFile(to_include)); if (include_config.good()) { if (!ReadConfigStream(include_config, to_include, error, ignore_invalid_keys)) { return false; @@ -994,13 +1016,13 @@ 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 */ } /** - * Ignores exceptions thrown by Boost's create_directories if the requested directory exists. + * Ignores exceptions thrown by create_directories if the requested directory exists. * Specifically handles case where path p exists, but it wasn't possible for the user to * write to the parent directory. */ @@ -1187,16 +1209,6 @@ void SetupEnvironment() // Set the default input/output charset is utf-8 SetConsoleCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8); -#endif - // The path locale is lazy initialized and to avoid deinitialization errors - // in multithreading environments, it is set explicitly by the main thread. - // A dummy locale is used to extract the internal default locale, used by - // fs::path, which is then used to explicitly imbue the path. - std::locale loc = fs::path::imbue(std::locale::classic()); -#ifndef WIN32 - fs::path::imbue(loc); -#else - fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16())); #endif } @@ -1240,7 +1252,7 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) if (path.is_absolute()) { return path; } - return fs::absolute(path, GetDataDir(net_specific)); + return fsbridge::AbsPathJoin(GetDataDir(net_specific), path); } int ScheduleBatchPriority() diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index c32dae9c663..3f2ebd76832 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. +#include #include #include @@ -58,7 +59,7 @@ static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory // existing file, treat it as the path to a BDB data file in a parent // directory that also contains BDB log files. env_directory = wallet_path.parent_path(); - database_filename = wallet_path.filename().string(); + database_filename = fs::PathToString(wallet_path.filename()); } else { // Normal case: Interpret wallet path as a directory path containing // data and log files. @@ -73,7 +74,7 @@ bool IsWalletLoaded(const fs::path& wallet_path) std::string database_filename; SplitWalletPath(wallet_path, env_directory, database_filename); LOCK(cs_db); - auto env = g_dbenvs.find(env_directory.string()); + auto env = g_dbenvs.find(fs::PathToString(env_directory)); if (env == g_dbenvs.end()) return false; auto database = env->second.lock(); return database && database->IsDatabaseLoaded(database_filename); @@ -99,9 +100,9 @@ std::shared_ptr GetWalletEnv(const fs::path& wallet_path, s fs::path env_directory; SplitWalletPath(wallet_path, env_directory, database_filename); 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; } @@ -140,7 +141,7 @@ void BerkeleyEnvironment::Close() if (error_file) fclose(error_file); - UnlockDirectory(strPath, ".walletlock"); + UnlockDirectory(fs::PathFromString(strPath), ".walletlock"); } void BerkeleyEnvironment::Reset() @@ -150,7 +151,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(); } @@ -167,7 +168,7 @@ bool BerkeleyEnvironment::Open(bool retry) if (fDbEnvInit) 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 defi may be using it.\n", strPath); @@ -177,13 +178,13 @@ bool BerkeleyEnvironment::Open(bool retry) 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); @@ -215,7 +216,7 @@ bool BerkeleyEnvironment::Open(bool retry) fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime()); try { fs::rename(pathLogDir, pathDatabaseBak); - LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string()); + LogPrintf("Moved old %s to %s. Retrying.\n", fs::PathToString(pathLogDir), fs::PathToString(pathDatabaseBak)); } catch (const fs::filesystem_error&) { // failure is ok (well, not really, but it's not worse than what we started with) } @@ -277,7 +278,7 @@ BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string& return VerifyResult::RECOVER_FAIL; // Try to recover: - bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename); + bool fRecovered = (*recoverFunc)(fs::PathFromString(strPath) / strFile, out_backup_filename); return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL); } @@ -396,10 +397,10 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er fs::path walletDir = env->Directory(); LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(nullptr, nullptr, nullptr)); - LogPrintf("Using wallet %s\n", file_path.string()); + LogPrintf("Using wallet %s\n", fs::PathToString(file_path)); if (!env->Open(true /* retry */)) { - errorStr = strprintf(_("Error initializing wallet database environment %s!").translated, walletDir); + errorStr = strprintf(_("Error initializing wallet database environment %s!").translated, fs::quoted(fs::PathToString(walletDir))); return false; } @@ -422,7 +423,7 @@ bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::string& w " Original %s saved as %s in %s; if" " your balance or transactions are incorrect you should" " restore from a backup.").translated, - walletFile, backup_filename, walletDir); + walletFile, backup_filename, fs::PathToString(walletDir)); } if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL) { @@ -792,7 +793,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"); } } } @@ -865,21 +866,21 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) // 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()); + if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) { + LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest)); return false; } fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing); - 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; } } @@ -894,7 +895,7 @@ void BerkeleyDatabase::Flush(bool shutdown) env->Flush(shutdown); if (shutdown) { LOCK(cs_db); - g_dbenvs.erase(env->Directory().string()); + g_dbenvs.erase(fs::PathToString(env->Directory())); env = nullptr; } else { // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the diff --git a/src/wallet/db.h b/src/wallet/db.h index 35584d01138..78e0cb915db 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -57,7 +57,7 @@ class BerkeleyEnvironment bool IsMock() const { return fMockDb; } bool IsInitialized() const { return fDbEnvInit; } bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); } - fs::path Directory() const { return strPath; } + fs::path Directory() const { return fs::PathFromString(strPath); } /** * Verify that database file strFile is OK. If it is not, diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 622bc1de8e0..899828e594b 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -13,28 +13,30 @@ #include #include +#include + bool VerifyWallets(interfaces::Chain& chain, const std::vector& wallet_files) { if (gArgs.IsArgSet("-walletdir")) { - fs::path wallet_dir = gArgs.GetArg("-walletdir", ""); - boost::system::error_code error; + fs::path wallet_dir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + std::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").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist").translated, fs::PathToString(wallet_dir))); return false; } else if (!fs::is_directory(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory").translated, 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").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path").translated, 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); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 17aa88bfc08..f9755763855 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -21,8 +22,10 @@ #include #include -#include +#include +#include #include +#include #include #include @@ -579,8 +582,8 @@ UniValue importwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - fsbridge::ifstream file; - file.open(request.params[0].get_str(), std::ios::in | std::ios::ate); + std::ifstream file; + 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"); } @@ -790,7 +793,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - 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 @@ -799,10 +802,10 @@ UniValue dumpwallet(const JSONRPCRequest& request) * 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; + std::ofstream file; file.open(filepath); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -888,7 +891,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) file.close(); UniValue reply(UniValue::VOBJ); - reply.pushKV("filename", filepath.string()); + reply.pushKV("filename", filepath.u8string()); return reply; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e946579ab22..67f22db4410 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2551,7 +2551,7 @@ static UniValue listwalletdir(const JSONRPCRequest& request) UniValue wallets(UniValue::VARR); for (const auto& path : ListWalletDir()) { UniValue wallet(UniValue::VOBJ); - wallet.pushKV("name", path.string()); + wallet.pushKV("name", path.u8string()); wallets.push_back(wallet); } @@ -2621,7 +2621,7 @@ static UniValue loadwallet(const JSONRPCRequest& request) } else if (fs::is_directory(location.GetPath())) { // The given filename is a directory. Check that there's a wallet.dat file. fs::path wallet_dat_file = location.GetPath() / "wallet.dat"; - if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) { + if (fs::symlink_status(wallet_dat_file).type() == fs::file_type::not_found) { throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file."); } } diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index e998cc93bbb..551fedeb4c8 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -2,14 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. -#include - #include #include #include #include +#include +#include +#include BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup) @@ -18,7 +19,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file) std::string test_name = "test_name.dat"; const fs::path datadir = GetDataDir(); fs::path file_path = datadir / test_name; - fs::ofstream f(file_path); + std::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 1bbb94ce563..396987062c1 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -5,6 +5,9 @@ #include #include +#include +#include + #include InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName) @@ -21,8 +24,8 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam m_walletdir_path_cases["custom"] = m_datadir / "my_wallets"; m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist"; m_walletdir_path_cases["file"] = m_datadir / "not_a_directory.dat"; - m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep; - m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep; + m_walletdir_path_cases["trailing"] = m_datadir / ("wallets" + sep); + m_walletdir_path_cases["trailing2"] = m_datadir / ("wallets" + sep + sep); fs::current_path(m_datadir); m_walletdir_path_cases["relative"] = "wallets"; @@ -30,7 +33,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"]); - fs::ofstream f(m_walletdir_path_cases["file"]); + std::ofstream f(m_walletdir_path_cases["file"]); f.close(); } @@ -41,5 +44,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 9c90a0663dc..6078b836cd2 100644 --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -16,7 +16,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) SetWalletDir(m_walletdir_path_cases["default"]); bool result = m_chain_client->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(walletdir == expected_path); } @@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom) SetWalletDir(m_walletdir_path_cases["custom"]); bool result = m_chain_client->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(walletdir == expected_path); } @@ -63,19 +63,22 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) SetWalletDir(m_walletdir_path_cases["trailing"]); bool result = m_chain_client->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(walletdir == expected_path); } +#ifndef WIN32 +// Windows does not consider "datadir/wallets//" to be a valid directory path. BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) { SetWalletDir(m_walletdir_path_cases["trailing2"]); bool result = m_chain_client->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(walletdir == expected_path); } +#endif BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 307c77c5b56..9d8520e4870 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -212,7 +212,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) auto locked_chain = chain->lock(); LockAssertion lock(::cs_main); - std::string backup_file = (GetDataDir() / "wallet.backup").string(); + std::string backup_file = fs::PathToString(GetDataDir() / "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 fce511e1372..db9b0bde5a0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4325,14 +4325,14 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b LOCK(cs_wallets); const fs::path& wallet_path = location.GetPath(); 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(location.GetName()).filename() == location.GetName()))) { + if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory || + (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) || + (path_type == fs::file_type::regular && fs::PathFromString(location.GetName()).filename() == fs::PathFromString(location.GetName())))) { error_string = 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)", - location.GetName(), GetWalletDir()); + location.GetName(), fs::quoted(fs::PathToString(GetWalletDir()))); return false; } @@ -4368,7 +4368,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags) { - const std::string& walletFile = WalletDataFilePath(location.GetPath()).string(); + const std::string& walletFile = fs::PathToString(WalletDataFilePath(location.GetPath())); // needed to restore wallet transaction meta data after -zapwallettxes std::vector vWtx; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index b5abdf0fdcd..6719cab91d5 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -691,25 +691,25 @@ void AutoBackupWallet() { fs::path prevBackup = env->Directory() / strprintf("auto.backup.%s.bak1", walletName); fs::path currentBackup = env->Directory() / strprintf("auto.backup.%s.bak2", walletName); if (fs::exists(prevBackup) && !fs::exists(currentBackup)) { - pwallet->BackupWallet(currentBackup.string()); + pwallet->BackupWallet(fs::PathToString(currentBackup)); } else if (fs::exists(prevBackup) && fs::exists(currentBackup)) { fs::remove(prevBackup); try { fs::rename(currentBackup, prevBackup); } catch (const fs::filesystem_error &) { - LogPrintf("failed rename %s to %s\n", prevBackup.string(), currentBackup.string()); + LogPrintf("failed rename %s to %s\n", fs::PathToString(prevBackup), fs::PathToString(currentBackup)); } - pwallet->BackupWallet(currentBackup.string()); + pwallet->BackupWallet(fs::PathToString(currentBackup)); } else if (!fs::exists(prevBackup) && fs::exists(currentBackup)) { try { fs::rename(currentBackup, prevBackup); } catch (const fs::filesystem_error &) { - LogPrintf("failed rename %s to %s\n", prevBackup.string(), currentBackup.string()); + LogPrintf("failed rename %s to %s\n", fs::PathToString(prevBackup), fs::PathToString(currentBackup)); } - pwallet->BackupWallet(currentBackup.string()); + pwallet->BackupWallet(fs::PathToString(currentBackup)); } else { - pwallet->BackupWallet(prevBackup.string()); + pwallet->BackupWallet(fs::PathToString(prevBackup)); } } diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index c6bee2b9e58..e69522a0f80 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -102,7 +102,7 @@ static void WalletShowInfo(CWallet* wallet_instance) bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) { - fs::path path = fs::absolute(name, GetWalletDir()); + fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name)); if (command == "create") { std::shared_ptr wallet_instance = CreateWallet(name, path); diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index ba76b330716..5038ece4df5 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -7,12 +7,17 @@ #include #include +#include +#include +#include +#include + 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. @@ -35,12 +40,12 @@ static bool IsBerkeleyBtree(const fs::path& path) // A Berkeley DB Btree file has at least 4K. // This check also prevents opening lock files. - boost::system::error_code ec; + std::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); + std::ifstream file(path, std::ios::binary); if (!file.is_open()) return false; file.seekg(12, std::ios::beg); // Magic bytes start at offset 12 @@ -57,24 +62,25 @@ static bool IsBerkeleyBtree(const fs::path& path) std::vector ListWalletDir() { const fs::path wallet_dir = GetWalletDir(); - const size_t offset = wallet_dir.string().size() + 1; + const size_t offset = wallet_dir.native().size() + 1; std::vector paths; - boost::system::error_code ec; + std::error_code ec; for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) { if (ec) { - LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string()); + LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path())); continue; } // 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 && IsBerkeleyBtree(it->path() / "wallet.dat")) { + if (it->status().type() == fs::file_type::directory && IsBerkeleyBtree(it->path() / "wallet.dat")) { // Found a directory which contains wallet.dat btree file, add it as a wallet. paths.emplace_back(path); - } else if (it.depth() == 0 && it->symlink_status().type() == fs::regular_file && IsBerkeleyBtree(it->path())) { + } else if (it.depth() == 0 && it->symlink_status().type() == fs::file_type::regular && IsBerkeleyBtree(it->path())) { if (it->path().filename() == "wallet.dat") { // Found top-level wallet.dat btree file, add top level directory "" // as a wallet. @@ -94,12 +100,10 @@ std::vector ListWalletDir() WalletLocation::WalletLocation(const std::string& name) : m_name(name) - , m_path(fs::absolute(name, GetWalletDir())) -{ - m_file_path = (m_path / m_name).string(); -} + , m_path(fs::absolute(name.empty() ? GetWalletDir() : fs::path(GetWalletDir() / fs::PathFromString(name)))) +{} bool WalletLocation::Exists() const { - return fs::symlink_status(m_path).type() != fs::file_not_found; + return fs::symlink_status(m_path).type() != fs::file_type::not_found; } diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index d69eb840df3..6354ec4e1d0 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -19,7 +19,6 @@ std::vector ListWalletDir(); //! The WalletLocation class provides wallet information. class WalletLocation final { - std::string m_file_path; std::string m_name; fs::path m_path; @@ -33,9 +32,6 @@ class WalletLocation final //! Get wallet absolute path. const fs::path& GetPath() const { return m_path; } - //! Get wallet absolute file path. - const std::string& GetFilePath() const { return m_file_path; } - //! Return whether the wallet exists. bool Exists() const; }; diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 5bdf56827c5..0b7cac6c803 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -8,6 +8,7 @@ """ import os import shutil +import sys import time from test_framework.test_framework import DefiTestFramework @@ -104,7 +105,9 @@ def wallet_file(name): assert_equal(os.path.isfile(wallet_file(wallet_name)), True) # should not initialize if wallet path can't be created - exp_stderr = "boost::filesystem::create_directories:" + exp_stderr = ( + "filesystem error:" if sys.platform != "win32" else "create_directories:" + ) self.nodes[0].assert_start_raises_init_error( ["-wallet=wallet.dat/bad"], exp_stderr, match=ErrorMatch.PARTIAL_REGEX ) diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 2535dfeb318..710ae4b5deb 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -56,8 +56,6 @@ EXPECTED_BOOST_INCLUDES=( boost/asio.hpp boost/circular_buffer.hpp boost/date_time/posix_time/posix_time.hpp - boost/filesystem.hpp - boost/filesystem/fstream.hpp boost/multiprecision/cpp_int.hpp boost/multi_index/hashed_index.hpp boost/multi_index/ordered_index.hpp