merge bitcoin#20744: Use std::filesystem. Remove Boost Filesystem & System

This commit is contained in:
Kittywhiskers Van Gogh 2024-08-06 17:40:56 +00:00
parent be7ac493d0
commit a3b79267e0
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
46 changed files with 264 additions and 437 deletions

View File

@ -1,118 +0,0 @@
# ===========================================================================
# https://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 <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_FILESYSTEM_LIB)
#
# And sets:
#
# HAVE_BOOST_FILESYSTEM
#
# LICENSE
#
# Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
# Copyright (c) 2009 Michael Tindal
# Copyright (c) 2009 Roman Rybalko <libtorrent@romanr.info>
#
# 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 28
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 <boost/filesystem/path.hpp>]],
[[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/@<:@^\/@:>@*//'`
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
])

View File

@ -0,0 +1,47 @@
dnl Copyright (c) 2022 The Bitcoin Core developers
dnl Distributed under the MIT software license, see the accompanying
dnl file COPYING or http://www.opensource.org/licenses/mit-license.php.
# GCC 8.1 and earlier requires -lstdc++fs
# Clang 8.0.0 (libc++) and earlier requires -lc++fs
m4_define([_CHECK_FILESYSTEM_testbody], [[
#include <filesystem>
namespace fs = std::filesystem;
int main() {
(void)fs::current_path().root_name();
return 0;
}
]])
AC_DEFUN([CHECK_FILESYSTEM], [
AC_LANG_PUSH(C++)
AC_MSG_CHECKING([whether std::filesystem can be used without link library])
AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_FILESYSTEM_testbody])],[
AC_MSG_RESULT([yes])
],[
AC_MSG_RESULT([no])
SAVED_LIBS="$LIBS"
LIBS="$SAVED_LIBS -lstdc++fs"
AC_MSG_CHECKING([whether std::filesystem needs -lstdc++fs])
AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_FILESYSTEM_testbody])],[
AC_MSG_RESULT([yes])
],[
AC_MSG_RESULT([no])
AC_MSG_CHECKING([whether std::filesystem needs -lc++fs])
LIBS="$SAVED_LIBS -lc++fs"
AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_FILESYSTEM_testbody])],[
AC_MSG_RESULT([yes])
],[
AC_MSG_FAILURE([cannot figure out how to use std::filesystem])
])
])
])
AC_LANG_POP
])

View File

@ -6,7 +6,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-dev libboost-filesystem-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev" export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
export NO_DEPENDS=1 export NO_DEPENDS=1
export TEST_RUNNER_EXTRA="--timeout-factor=4" # Increase timeout because sanitizers slow down export TEST_RUNNER_EXTRA="--timeout-factor=4" # Increase timeout because sanitizers slow down
export FUNCTIONAL_TESTS_CONFIG="--exclude wallet_multiwallet.py" # Temporarily suppress ASan heap-use-after-free (see issue #14163) export FUNCTIONAL_TESTS_CONFIG="--exclude wallet_multiwallet.py" # Temporarily suppress ASan heap-use-after-free (see issue #14163)

View File

@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_fuzz export CONTAINER_NAME=ci_native_fuzz
export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libboost-filesystem-dev libboost-test-dev" export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libboost-test-dev"
export DEP_OPTS="NO_UPNP=1 DEBUG=1" export DEP_OPTS="NO_UPNP=1 DEBUG=1"
export CPPFLAGS="-DDEBUG_LOCKORDER -DARENA_DEBUG" export CPPFLAGS="-DDEBUG_LOCKORDER -DARENA_DEBUG"
export CXXFLAGS="-Werror -Wno-unused-command-line-argument -Wno-unused-value -Wno-deprecated-builtins" export CXXFLAGS="-Werror -Wno-unused-command-line-argument -Wno-unused-value -Wno-deprecated-builtins"

View File

@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_fuzz_valgrind export CONTAINER_NAME=ci_native_fuzz_valgrind
export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev valgrind" export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libboost-test-dev valgrind"
export NO_DEPENDS=1 export NO_DEPENDS=1
export RUN_UNIT_TESTS=false export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false export RUN_FUNCTIONAL_TESTS=false

View File

@ -8,7 +8,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_ubsan export CONTAINER_NAME=ci_native_ubsan
export PACKAGES="clang-16 llvm-16 python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev" export PACKAGES="clang-16 llvm-16 python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
export DEP_OPTS="NO_UPNP=1 DEBUG=1" export DEP_OPTS="NO_UPNP=1 DEBUG=1"
export GOAL="install" export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports --enable-crash-hooks --with-sanitizers=undefined CC=clang-16 CXX=clang++-16" export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports --enable-crash-hooks --with-sanitizers=undefined CC=clang-16 CXX=clang++-16"

View File

@ -6,7 +6,7 @@
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev" export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev"
export USE_VALGRIND=1 export USE_VALGRIND=1
export NO_DEPENDS=1 export NO_DEPENDS=1
export TEST_RUNNER_EXTRA="--exclude rpc_bind --timeout-factor=4" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 export TEST_RUNNER_EXTRA="--exclude rpc_bind --timeout-factor=4" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547

View File

@ -82,6 +82,9 @@ fi
dnl Check if -latomic is required for <std::atomic> dnl Check if -latomic is required for <std::atomic>
CHECK_ATOMIC CHECK_ATOMIC
dnl check if additional link flags are required for std::filesystem
CHECK_FILESYSTEM
dnl Unless the user specified OBJCXX, force it to be the same as CXX. This ensures dnl Unless the user specified OBJCXX, force it to be the same as CXX. This ensures
dnl that we get the same -std flags for both. dnl that we get the same -std flags for both.
m4_ifdef([AC_PROG_OBJCXX],[ m4_ifdef([AC_PROG_OBJCXX],[
@ -1472,8 +1475,6 @@ dnl and will generate warnings with newer compilers.
dnl See: https://github.com/boostorg/container_hash/issues/22. dnl See: https://github.com/boostorg/container_hash/issues/22.
BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_CXX98_FUNCTION_BASE" BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_CXX98_FUNCTION_BASE"
AX_BOOST_FILESYSTEM
dnl Opt-in to Boost Process dnl Opt-in to Boost Process
if test "x$boost_process" != xno; then if test "x$boost_process" != xno; then
AC_MSG_CHECKING(for Boost Process) AC_MSG_CHECKING(for Boost Process)
@ -1488,7 +1489,7 @@ if test x$suppress_external_warnings != xno; then
BOOST_CPPFLAGS=SUPPRESS_WARNINGS($BOOST_CPPFLAGS) BOOST_CPPFLAGS=SUPPRESS_WARNINGS($BOOST_CPPFLAGS)
fi fi
BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB" BOOST_LIBS="$BOOST_LDFLAGS"
fi fi
dnl Check for reduced exports dnl Check for reduced exports

View File

@ -118,12 +118,6 @@
fun:__wcsnlen_sse4_1 fun:__wcsnlen_sse4_1
fun:wcsnrtombs fun:wcsnrtombs
} }
{
Suppress wcsnrtombs warning (remove after removing boost::fs)
Memcheck:Cond
...
fun:_ZN5boost10filesystem6detail11unique_pathERKNS0_4pathEPNS_6system10error_codeE
}
{ {
Suppress boost warning Suppress boost warning
Memcheck:Leak Memcheck:Leak
@ -134,21 +128,6 @@
fun:_ZN5boost9unit_test14unit_test_mainEPFbvEiPPc fun:_ZN5boost9unit_test14unit_test_mainEPFbvEiPPc
fun:main fun:main
} }
{
Suppress boost::filesystem warning (fixed in boost 1.70: https://github.com/boostorg/filesystem/commit/bbe9d1771e5d679b3f10c42a58fc81f7e8c024a9)
Memcheck:Cond
fun:_ZN5boost10filesystem6detail28directory_iterator_incrementERNS0_18directory_iteratorEPNS_6system10error_codeE
...
obj:*/libboost_filesystem.so.*
}
{
Suppress boost::filesystem warning (could be related: https://stackoverflow.com/questions/9830182/function-boostfilesystemcomplete-being-reported-as-possible-memory-leak-by-v)
Memcheck:Leak
match-leak-kinds: reachable
fun:_Znwm
...
fun:_ZN5boost10filesystem8absoluteERKNS0_4pathES3_
}
{ {
Suppress boost still reachable memory warning Suppress boost still reachable memory warning
Memcheck:Leak Memcheck:Leak

View File

@ -26,7 +26,7 @@ $(package)_toolset_$(host_os)=clang
else else
$(package)_toolset_$(host_os)=gcc $(package)_toolset_$(host_os)=gcc
endif endif
$(package)_config_libraries=filesystem,test $(package)_config_libraries=test
$(package)_cxxflags+=-std=c++11 $(package)_cxxflags+=-std=c++11
$(package)_cxxflags_linux=-fPIC $(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_freebsd=-fPIC $(package)_cxxflags_freebsd=-fPIC

View File

@ -81,7 +81,7 @@ sudo apt-get install build-essential libtool autotools-dev automake pkg-config b
Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies: Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies:
```sh ```sh
sudo apt-get install libevent-dev libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev sudo apt-get install libevent-dev libboost-dev libboost-test-dev
``` ```
SQLite is required for the descriptor wallet: SQLite is required for the descriptor wallet:

View File

@ -8,6 +8,7 @@
#include <addrman.h> #include <addrman.h>
#include <chainparams.h> #include <chainparams.h>
#include <clientversion.h> #include <clientversion.h>
#include <fs.h>
#include <hash.h> #include <hash.h>
#include <logging/timer.h> #include <logging/timer.h>
#include <netbase.h> #include <netbase.h>

View File

@ -10,6 +10,7 @@
#include <validation.h> #include <validation.h>
#include <algorithm> #include <algorithm>
#include <fstream>
#include <iostream> #include <iostream>
#include <regex> #include <regex>
@ -23,7 +24,7 @@ void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& bench
// nothing to write, bail out // nothing to write, bail out
return; return;
} }
fsbridge::ofstream fout{fs::PathFromString(filename)}; std::ofstream fout{fs::PathFromString(filename)};
if (fout.is_open()) { if (fout.is_open()) {
ankerl::nanobench::render(tpl, benchmarkResults, fout); ankerl::nanobench::render(tpl, benchmarkResults, fout);
} else { } else {

View File

@ -37,7 +37,7 @@ FILE *fopen(const fs::path& p, const char *mode)
fs::path AbsPathJoin(const fs::path& base, const fs::path& path) fs::path AbsPathJoin(const fs::path& base, const fs::path& path)
{ {
assert(base.is_absolute()); assert(base.is_absolute());
return fs::absolute(path, base); return path.empty() ? base : fs::path(base / path);
} }
#ifndef WIN32 #ifndef WIN32
@ -153,112 +153,4 @@ std::string get_filesystem_error_message(const fs::filesystem_error& e)
#endif #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<char>(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<char>(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__
#if BOOST_VERSION >= 107700
static_assert(sizeof(*BOOST_FILESYSTEM_C_STR(boost::filesystem::path())) == sizeof(wchar_t),
#else
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 "
"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 } // fsbridge

134
src/fs.h
View File

@ -5,46 +5,42 @@
#ifndef BITCOIN_FS_H #ifndef BITCOIN_FS_H
#define BITCOIN_FS_H #define BITCOIN_FS_H
#include <stdio.h>
#include <string>
#if defined WIN32 && defined __GLIBCXX__
#include <ext/stdio_filebuf.h>
#endif
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <tinyformat.h> #include <tinyformat.h>
#include <cstdio>
#include <filesystem>
#include <iomanip>
#include <ios>
#include <ostream>
#include <string>
#include <utility>
/** Filesystem operations and types */ /** Filesystem operations and types */
namespace fs { namespace fs {
using namespace boost::filesystem; using namespace std::filesystem;
/** /**
* Path class wrapper to prepare application code for transition from * Path class wrapper to block calls to the fs::path(std::string) implicit
* boost::filesystem library to std::filesystem implementation. The main * constructor and the fs::path::string() method, which have unsafe and
* purpose of the class is to define fs::path::u8string() and fs::u8path() * unpredictable behavior on Windows (see implementation note in
* functions not present in boost. It also blocks calls to the * \ref PathToString for details)
* 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 class path : public std::filesystem::path
{ {
public: public:
using boost::filesystem::path::path; using std::filesystem::path::path;
// Allow path objects arguments for compatibility. // Allow path objects arguments for compatibility.
path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {} path(std::filesystem::path path) : std::filesystem::path::path(std::move(path)) {}
path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; } path& operator=(std::filesystem::path path) { std::filesystem::path::operator=(std::move(path)); return *this; }
path& operator/=(boost::filesystem::path path) { boost::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. // Allow literal string arguments, which are safe as long as the literals are ASCII.
path(const char* c) : boost::filesystem::path(c) {} path(const char* c) : std::filesystem::path(c) {}
path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; } path& operator=(const char* c) { std::filesystem::path::operator=(c); return *this; }
path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; } path& operator/=(const char* c) { std::filesystem::path::operator/=(c); return *this; }
path& append(const char* c) { boost::filesystem::path::append(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. // Disallow std::string arguments to avoid locale-dependent decoding on windows.
path(std::string) = delete; path(std::string) = delete;
@ -55,52 +51,48 @@ public:
// Disallow std::string conversion method to avoid locale-dependent encoding on windows. // Disallow std::string conversion method to avoid locale-dependent encoding on windows.
std::string string() const = delete; std::string string() const = delete;
// Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem. // Required for path overloads in <fstream>.
std::string u8string() const { return boost::filesystem::path::string(); } // 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(); }
}; };
// Define UTF-8 string conversion function not present in boost::filesystem but present in std::filesystem. // Disallow implicit std::string conversion for absolute to avoid
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. // locale-dependent encoding on windows.
static inline path system_complete(const path& p) static inline path absolute(const path& p)
{ {
return boost::filesystem::system_complete(p); return std::filesystem::absolute(p);
} }
// Disallow implicit std::string conversion for exists to avoid // Disallow implicit std::string conversion for exists to avoid
// locale-dependent encoding on windows. // locale-dependent encoding on windows.
static inline bool exists(const path& p) static inline bool exists(const path& p)
{ {
return boost::filesystem::exists(p); return std::filesystem::exists(p);
} }
// Allow explicit quoted stream I/O. // Allow explicit quoted stream I/O.
static inline auto quoted(const std::string& s) static inline auto quoted(const std::string& s)
{ {
return boost::io::quoted(s, '&'); return std::quoted(s, '"', '&');
} }
// Allow safe path append operations. // Allow safe path append operations.
static inline path operator+(path p1, path p2) static inline path operator+(path p1, path p2)
{ {
p1 += static_cast<boost::filesystem::path&&>(p2); p1 += std::move(p2);
return p1; return p1;
} }
// Disallow implicit std::string conversion for copy_file // Disallow implicit std::string conversion for copy_file
// to avoid locale-dependent encoding on Windows. // to avoid locale-dependent encoding on Windows.
static inline void copy_file(const path& from, const path& to, copy_option options) static inline bool copy_file(const path& from, const path& to, copy_options options)
{ {
boost::filesystem::copy_file(from, to, options); return std::filesystem::copy_file(from, to, options);
} }
/** /**
* Convert path object to byte string. On POSIX, paths natively are byte * 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 * strings, so this is trivial. On Windows, paths natively are Unicode, so an
* encoding step is necessary. The inverse of \ref PathToString is \ref * encoding step is necessary. The inverse of \ref PathToString is \ref
* PathFromString. The strings returned and parsed by these functions can be * PathFromString. The strings returned and parsed by these functions can be
@ -112,7 +104,7 @@ static inline void copy_file(const path& from, const path& to, copy_option optio
* appropriate to use in applications requiring UTF-8, where * appropriate to use in applications requiring UTF-8, where
* fs::path::u8string() and fs::u8path() methods should be used instead. Other * fs::path::u8string() and fs::u8path() methods should be used instead. Other
* applications could require still different encodings. For example, JSON, XML, * applications could require still different encodings. For example, JSON, XML,
* or URI applications might prefer to use higher level escapes (\uXXXX or * or URI applications might prefer to use higher-level escapes (\uXXXX or
* &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications * &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications
* may require encoding paths with their respective UTF-8 derivatives WTF-8, * 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). * PEP-383, and CESU-8 (see https://en.wikipedia.org/wiki/UTF-8#Derivatives).
@ -133,7 +125,7 @@ static inline std::string PathToString(const path& path)
return path.u8string(); return path.u8string();
#else #else
static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform"); static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform");
return path.boost::filesystem::path::string(); return path.std::filesystem::path::string();
#endif #endif
} }
@ -145,7 +137,7 @@ static inline path PathFromString(const std::string& string)
#ifdef WIN32 #ifdef WIN32
return u8path(string); return u8path(string);
#else #else
return boost::filesystem::path(string); return std::filesystem::path(string);
#endif #endif
} }
} // namespace fs } // namespace fs
@ -186,60 +178,12 @@ namespace fsbridge {
}; };
std::string get_filesystem_error_message(const fs::filesystem_error& e); 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<char> 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<char> 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 // Disallow path operator<< formatting in tinyformat to avoid locale-dependent
// encoding on windows. // encoding on windows.
namespace tinyformat { 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 std::filesystem::path&) = delete;
template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete; template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
} // namespace tinyformat } // namespace tinyformat

View File

@ -105,12 +105,15 @@
#include <statsd_client.h> #include <statsd_client.h>
#include <condition_variable>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <functional> #include <functional>
#include <set> #include <set>
#include <stdint.h>
#include <stdio.h>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -159,7 +162,7 @@ static fs::path GetPidFile(const ArgsManager& args)
[[nodiscard]] static bool CreatePidFile(const ArgsManager& args) [[nodiscard]] static bool CreatePidFile(const ArgsManager& args)
{ {
fsbridge::ofstream file{GetPidFile(args)}; std::ofstream file{GetPidFile(args)};
if (file) { if (file) {
#ifdef WIN32 #ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId()); tfm::format(file, "%d\n", GetCurrentProcessId());

View File

@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
#include <logging.h> #include <logging.h>
#include <util/system.h> #include <util/system.h>
#include <util/threadnames.h> #include <util/threadnames.h>

View File

@ -13,6 +13,7 @@
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
#include <functional>
#include <list> #include <list>
#include <mutex> #include <mutex>
#include <string> #include <string>

View File

@ -17,6 +17,7 @@
#include <compat.h> #include <compat.h>
#include <consensus/consensus.h> #include <consensus/consensus.h>
#include <crypto/sha256.h> #include <crypto/sha256.h>
#include <fs.h>
#include <i2p.h> #include <i2p.h>
#include <net_permissions.h> #include <net_permissions.h>
#include <netaddress.h> #include <netaddress.h>

View File

@ -15,6 +15,7 @@
#include <base58.h> #include <base58.h>
#include <chainparams.h> #include <chainparams.h>
#include <fs.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <key_io.h> #include <key_io.h>
#include <policy/policy.h> #include <policy/policy.h>
@ -77,6 +78,10 @@
#include <QtGlobal> #include <QtGlobal>
#include <chrono> #include <chrono>
#include <exception>
#include <fstream>
#include <string>
#include <vector>
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
@ -810,7 +815,7 @@ fs::path static GetAutostartFilePath()
bool GetStartOnSystemStartup() bool GetStartOnSystemStartup()
{ {
fsbridge::ifstream optionFile(GetAutostartFilePath()); std::ifstream optionFile{GetAutostartFilePath()};
if (!optionFile.good()) if (!optionFile.good())
return false; return false;
// Scan through file for "Hidden=true": // Scan through file for "Hidden=true":
@ -842,7 +847,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
fs::create_directories(GetAutostartDir()); fs::create_directories(GetAutostartDir());
fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc); std::ofstream optionFile{GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc};
if (!optionFile.good()) if (!optionFile.good())
return false; return false;
std::string chain = gArgs.GetChainName(); std::string chain = gArgs.GetChainName();

View File

@ -5,6 +5,7 @@
#include <qt/psbtoperationsdialog.h> #include <qt/psbtoperationsdialog.h>
#include <core_io.h> #include <core_io.h>
#include <fs.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <key_io.h> #include <key_io.h>
#include <node/psbt.h> #include <node/psbt.h>
@ -15,7 +16,9 @@
#include <qt/optionsmodel.h> #include <qt/optionsmodel.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <fstream>
#include <iostream> #include <iostream>
#include <string>
PSBTOperationsDialog::PSBTOperationsDialog( PSBTOperationsDialog::PSBTOperationsDialog(
@ -150,7 +153,7 @@ void PSBTOperationsDialog::saveTransaction() {
if (filename.isEmpty()) { if (filename.isEmpty()) {
return; return;
} }
fsbridge::ofstream out{filename.toLocal8Bit().data(), fsbridge::ofstream::out | fsbridge::ofstream::binary}; std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
out << ssTx.str(); out << ssTx.str();
out.close(); out.close();
showStatus(tr("PSBT saved to disk."), StatusLevel::INFO); showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);

View File

@ -27,9 +27,12 @@
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/fees.h> #include <wallet/fees.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <validation.h> #include <validation.h>
#include <array>
#include <fstream>
#include <memory>
#include <QFontMetrics> #include <QFontMetrics>
#include <QScrollBar> #include <QScrollBar>
#include <QSettings> #include <QSettings>
@ -521,7 +524,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (filename.isEmpty()) { if (filename.isEmpty()) {
return; return;
} }
fsbridge::ofstream out{filename.toLocal8Bit().data(), fsbridge::ofstream::out | fsbridge::ofstream::binary}; std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
out << ssTx.str(); out << ssTx.str();
out.close(); out.close();
Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION); Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);

View File

@ -4,6 +4,7 @@
#include <qt/walletframe.h> #include <qt/walletframe.h>
#include <fs.h>
#include <node/ui_interface.h> #include <node/ui_interface.h>
#include <psbt.h> #include <psbt.h>
#include <qt/bitcoingui.h> #include <qt/bitcoingui.h>
@ -19,6 +20,8 @@
#include <util/system.h> #include <util/system.h>
#include <cassert> #include <cassert>
#include <fstream>
#include <string>
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
@ -285,7 +288,7 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR); Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
return; return;
} }
fsbridge::ifstream in{filename.toLocal8Bit().data(), std::ios::binary}; std::ifstream in{filename.toLocal8Bit().data(), std::ios::binary};
data = std::string(std::istreambuf_iterator<char>{in}, {}); data = std::string(std::istreambuf_iterator<char>{in}, {});
} }

View File

@ -12,6 +12,11 @@
#include <util/system.h> #include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
/** /**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, * JSON-RPC protocol. Bitcoin 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 * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
@ -83,7 +88,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
/** the umask determines what permissions are used to create this file - /** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms. * these are set to 077 in init.cpp unless overridden with -sysperms.
*/ */
fsbridge::ofstream file; std::ofstream file;
fs::path filepath_tmp = GetAuthCookieFile(true); fs::path filepath_tmp = GetAuthCookieFile(true);
file.open(filepath_tmp); file.open(filepath_tmp);
if (!file.is_open()) { if (!file.is_open()) {
@ -107,7 +112,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
bool GetAuthCookie(std::string *cookie_out) bool GetAuthCookie(std::string *cookie_out)
{ {
fsbridge::ifstream file; std::ifstream file;
std::string cookie; std::string cookie;
fs::path filepath = GetAuthCookieFile(); fs::path filepath = GetAuthCookieFile();
file.open(filepath); file.open(filepath);

View File

@ -9,6 +9,10 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <fstream>
#include <ios>
#include <string>
BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(fsbridge_pathtostring) BOOST_AUTO_TEST_CASE(fsbridge_pathtostring)
@ -45,37 +49,37 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
fs::path tmpfile1 = tmpfolder / "fs_tests_∋_🏃"; fs::path tmpfile1 = tmpfolder / "fs_tests_∋_🏃";
fs::path tmpfile2 = tmpfolder / "fs_tests_∋_🏃"; fs::path tmpfile2 = tmpfolder / "fs_tests_∋_🏃";
{ {
fsbridge::ofstream file(tmpfile1); std::ofstream file{tmpfile1};
file << "bitcoin"; file << "bitcoin";
} }
{ {
fsbridge::ifstream file(tmpfile2); std::ifstream file{tmpfile2};
std::string input_buffer; std::string input_buffer;
file >> input_buffer; file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
} }
{ {
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; std::string input_buffer;
file >> input_buffer; file >> input_buffer;
BOOST_CHECK_EQUAL(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"; file << "tests";
} }
{ {
fsbridge::ifstream file(tmpfile1); std::ifstream file{tmpfile1};
std::string input_buffer; std::string input_buffer;
file >> input_buffer; file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "bitcointests"); BOOST_CHECK_EQUAL(input_buffer, "bitcointests");
} }
{ {
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 << "bitcoin"; file << "bitcoin";
} }
{ {
fsbridge::ifstream file(tmpfile1); std::ifstream file{tmpfile1};
std::string input_buffer; std::string input_buffer;
file >> input_buffer; file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); BOOST_CHECK_EQUAL(input_buffer, "bitcoin");

View File

@ -4,6 +4,7 @@
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
#include <fs.h>
#include <netaddress.h> #include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
@ -13,9 +14,10 @@
#include <csignal> #include <csignal>
#include <cstdint> #include <cstdint>
#include <string>
#include <exception> #include <exception>
#include <fstream>
#include <memory> #include <memory>
#include <string>
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
@ -59,7 +61,7 @@ void initialize()
} }
if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) { if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl; std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
fsbridge::ofstream out_stream{out_path, std::ios::binary}; std::ofstream out_stream{out_path, std::ios::binary};
for (const auto& t : FuzzTargets()) { for (const auto& t : FuzzTargets()) {
if (std::get<2>(t.second)) continue; if (std::get<2>(t.second)) continue;
out_stream << t.first << std::endl; out_stream << t.first << std::endl;

View File

@ -20,7 +20,8 @@
#include <script/bitcoinconsensus.h> #include <script/bitcoinconsensus.h>
#endif #endif
#include <stdint.h> #include <cstdint>
#include <fstream>
#include <string> #include <string>
#include <vector> #include <vector>

View File

@ -4,6 +4,7 @@
#include <util/settings.h> #include <util/settings.h>
#include <fs.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <test/util/str.h> #include <test/util/str.h>
@ -13,6 +14,11 @@
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/string.h> #include <util/string.h>
#include <util/system.h> #include <util/system.h>
#include <fstream>
#include <map>
#include <string>
#include <system_error>
#include <vector> #include <vector>
inline bool operator==(const util::SettingsValue& a, const util::SettingsValue& b) inline bool operator==(const util::SettingsValue& a, const util::SettingsValue& b)
@ -36,7 +42,7 @@ inline std::ostream& operator<<(std::ostream& os, const std::pair<std::string, u
inline void WriteText(const fs::path& path, const std::string& text) inline void WriteText(const fs::path& path, const std::string& text)
{ {
fsbridge::ofstream file; std::ofstream file;
file.open(path); file.open(path);
file << text; file << text;
} }

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
#include <streams.h> #include <streams.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>

View File

@ -5,6 +5,7 @@
#include <util/system.h> #include <util/system.h>
#include <clientversion.h> #include <clientversion.h>
#include <fs.h>
#include <hash.h> // For Hash() #include <hash.h> // For Hash()
#include <key.h> // For CKey #include <key.h> // For CKey
#include <sync.h> #include <sync.h>
@ -72,9 +73,12 @@ BOOST_AUTO_TEST_CASE(util_datadir)
args.ClearPathCache(); args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
#ifndef WIN32
// Windows does not consider "datadir/.//" to be a valid directory path.
args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//"); args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//");
args.ClearPathCache(); args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
#endif
} }
namespace { namespace {

View File

@ -6,6 +6,7 @@
#include <clientversion.h> #include <clientversion.h>
#include <crypto/common.h> #include <crypto/common.h>
#include <fs.h>
#include <logging.h> #include <logging.h>
#include <streams.h> #include <streams.h>

View File

@ -2,11 +2,17 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
#include <util/settings.h> #include <util/settings.h>
#include <tinyformat.h> #include <tinyformat.h>
#include <univalue.h> #include <univalue.h>
#include <fstream>
#include <map>
#include <string>
#include <vector>
namespace util { namespace util {
namespace { namespace {
@ -63,7 +69,7 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
// Ok for file to not exist // Ok for file to not exist
if (!fs::exists(path)) return true; if (!fs::exists(path)) return true;
fsbridge::ifstream file; std::ifstream file;
file.open(path); file.open(path);
if (!file.is_open()) { if (!file.is_open()) {
errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path))); errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));
@ -118,7 +124,7 @@ bool WriteSettings(const fs::path& path,
for (const auto& value : values) { for (const auto& value : values) {
out.__pushKV(value.first, value.second); out.__pushKV(value.first, value.second);
} }
fsbridge::ofstream file; std::ofstream file;
file.open(path); file.open(path);
if (file.fail()) { if (file.fail()) {
errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path))); errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));

View File

@ -74,9 +74,14 @@
#include <malloc.h> #include <malloc.h>
#endif #endif
#include <univalue.h>
#include <fstream>
#include <map>
#include <memory>
#include <string>
#include <thread> #include <thread>
#include <typeinfo> #include <typeinfo>
#include <univalue.h>
// Application startup time (used for uptime calculation) // Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime(); const int64_t nStartupTime = GetTime();
@ -167,7 +172,7 @@ bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
} }
std::streampos GetFileSize(const char* path, std::streamsize max) { std::streampos GetFileSize(const char* path, std::streamsize max) {
fsbridge::ifstream file{path, std::ios::binary}; std::ifstream file{path, std::ios::binary};
file.ignore(max); file.ignore(max);
return file.gcount(); return file.gcount();
} }
@ -259,7 +264,7 @@ namespace {
fs::path StripRedundantLastElementsOfPath(const fs::path& path) fs::path StripRedundantLastElementsOfPath(const fs::path& path)
{ {
auto result = path; auto result = path;
while (fs::PathToString(result.filename()) == ".") { while (result.filename().empty() || fs::PathToString(result.filename()) == ".") {
result = result.parent_path(); result = result.parent_path();
} }
@ -424,7 +429,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
if (!path.empty()) return path; if (!path.empty()) return path;
if (IsArgSet("-blocksdir")) { if (IsArgSet("-blocksdir")) {
path = fs::system_complete(fs::PathFromString(GetArg("-blocksdir", ""))); path = fs::absolute(fs::PathFromString(GetArg("-blocksdir", "")));
if (!fs::is_directory(path)) { if (!fs::is_directory(path)) {
path = ""; path = "";
return path; return path;
@ -436,7 +441,6 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
path /= fs::PathFromString(BaseParams().DataDir()); path /= fs::PathFromString(BaseParams().DataDir());
path /= "blocks"; path /= "blocks";
fs::create_directories(path); fs::create_directories(path);
path = StripRedundantLastElementsOfPath(path);
return path; return path;
} }
@ -451,7 +455,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
std::string datadir = GetArg("-datadir", ""); std::string datadir = GetArg("-datadir", "");
if (!datadir.empty()) { if (!datadir.empty()) {
path = fs::system_complete(fs::PathFromString(datadir)); path = fs::absolute(StripRedundantLastElementsOfPath(fs::PathFromString(datadir)));
if (!fs::is_directory(path)) { if (!fs::is_directory(path)) {
path = ""; path = "";
return path; return path;
@ -863,7 +867,7 @@ fs::path GetBackupsDir()
bool CheckDataDirOption() bool CheckDataDirOption()
{ {
std::string datadir = gArgs.GetArg("-datadir", ""); std::string datadir = gArgs.GetArg("-datadir", "");
return datadir.empty() || fs::is_directory(fs::system_complete(fs::PathFromString(datadir))); return datadir.empty() || fs::is_directory(fs::absolute(fs::PathFromString(datadir)));
} }
fs::path GetConfigFile(const std::string& confPath) fs::path GetConfigFile(const std::string& confPath)
@ -954,7 +958,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
} }
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
fsbridge::ifstream stream(GetConfigFile(confPath)); std::ifstream stream{GetConfigFile(confPath)};
// not ok to have a config file specified that cannot be opened // not ok to have a config file specified that cannot be opened
if (IsArgSet("-conf") && !stream.good()) { if (IsArgSet("-conf") && !stream.good()) {
@ -1001,7 +1005,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
const size_t default_includes = add_includes({}); const size_t default_includes = add_includes({});
for (const std::string& conf_file_name : conf_file_names) { for (const std::string& conf_file_name : conf_file_names) {
fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name)); std::ifstream conf_file_stream{GetConfigFile(conf_file_name)};
if (conf_file_stream.good()) { if (conf_file_stream.good()) {
if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) { if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
return false; return false;
@ -1028,9 +1032,10 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
} }
} else { } else {
// Create an empty dash.conf if it does not exist // Create an empty dash.conf if it does not exist
FILE* configFile = fsbridge::fopen(GetConfigFile(confPath), "a"); std::ofstream configFile{GetConfigFile(confPath), std::ios_base::app};
if (configFile != nullptr) if (!configFile.good())
fclose(configFile); return false;
configFile.close();
return true; // Nothing to read, so just return return true; // Nothing to read, so just return
} }
@ -1138,7 +1143,7 @@ bool RenameOver(fs::path src, fs::path dest)
} }
/** /**
* 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 * Specifically handles case where path p exists, but it wasn't possible for the user to
* write to the parent directory. * write to the parent directory.
*/ */
@ -1417,16 +1422,6 @@ void SetupEnvironment()
// Set the default input/output charset is utf-8 // Set the default input/output charset is utf-8
SetConsoleCP(CP_UTF8); SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(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<wchar_t>()));
#endif #endif
} }

View File

@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
#include <wallet/bdb.h> #include <wallet/bdb.h>
#include <wallet/db.h> #include <wallet/db.h>
@ -619,12 +620,12 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const
pathDest /= fs::PathFromString(strFile); pathDest /= fs::PathFromString(strFile);
try { try {
if (fs::equivalent(pathSrc, pathDest)) { if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest)); LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
return false; return false;
} }
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists); fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest)); LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
return true; return true;
} catch (const fs::filesystem_error& e) { } catch (const fs::filesystem_error& e) {

View File

@ -8,18 +8,22 @@
#include <logging.h> #include <logging.h>
#include <wallet/db.h> #include <wallet/db.h>
#include <exception>
#include <fstream>
#include <string> #include <string>
#include <system_error>
#include <vector>
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir) std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
{ {
const size_t offset = wallet_dir.native().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<fs::path> paths; std::vector<fs::path> 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)) { for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
if (ec) { if (ec) {
if (fs::is_directory(*it)) { if (fs::is_directory(*it)) {
it.no_push(); it.disable_recursion_pending();
LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path())); LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path()));
} else { } else {
LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path())); LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path()));
@ -33,11 +37,11 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
const auto path_str = it->path().native().substr(offset); const auto path_str = it->path().native().substr(offset);
const fs::path path{path_str.begin(), path_str.end()}; const fs::path path{path_str.begin(), path_str.end()};
if (it->status().type() == fs::directory_file && if (it->status().type() == fs::file_type::directory &&
(IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) { (IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) {
// Found a directory which contains wallet.dat btree file, add it as a wallet. // Found a directory which contains wallet.dat btree file, add it as a wallet.
paths.emplace_back(path); paths.emplace_back(path);
} else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBDBFile(it->path())) { } else if (it.depth() == 0 && it->symlink_status().type() == fs::file_type::regular && IsBDBFile(it->path())) {
if (it->path().filename() == "wallet.dat") { if (it->path().filename() == "wallet.dat") {
// Found top-level wallet.dat btree file, add top level directory "" // Found top-level wallet.dat btree file, add top level directory ""
// as a wallet. // as a wallet.
@ -52,7 +56,7 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what()); LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what());
it.no_push(); it.disable_recursion_pending();
} }
} }
@ -84,12 +88,12 @@ bool IsBDBFile(const fs::path& path)
// A Berkeley DB Btree file has at least 4K. // A Berkeley DB Btree file has at least 4K.
// This check also prevents opening lock files. // This check also prevents opening lock files.
boost::system::error_code ec; std::error_code ec;
auto size = fs::file_size(path, ec); auto size = fs::file_size(path, ec);
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path)); if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
if (size < 4096) return false; 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; if (!file.is_open()) return false;
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12 file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
@ -108,12 +112,12 @@ bool IsSQLiteFile(const fs::path& path)
if (!fs::exists(path)) return false; if (!fs::exists(path)) return false;
// A SQLite Database file is at least 512 bytes. // A SQLite Database file is at least 512 bytes.
boost::system::error_code ec; std::error_code ec;
auto size = fs::file_size(path, ec); auto size = fs::file_size(path, ec);
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path)); if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
if (size < 512) return false; if (size < 512) return false;
fsbridge::ifstream file(path, std::ios::binary); std::ifstream file{path, std::ios::binary};
if (!file.is_open()) return false; if (!file.is_open()) return false;
// Magic is at beginning and is 16 bytes long // Magic is at beginning and is 16 bytes long

View File

@ -4,9 +4,17 @@
#include <wallet/dump.h> #include <wallet/dump.h>
#include <fs.h>
#include <util/translation.h> #include <util/translation.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <algorithm>
#include <fstream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP"; static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
uint32_t DUMP_VERSION = 1; uint32_t DUMP_VERSION = 1;
@ -25,7 +33,7 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
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)); 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; return false;
} }
fsbridge::ofstream dump_file; std::ofstream dump_file;
dump_file.open(path); dump_file.open(path);
if (dump_file.fail()) { if (dump_file.fail()) {
error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path)); error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
@ -120,7 +128,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path)); error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
return false; return false;
} }
fsbridge::ifstream dump_file(dump_path); std::ifstream dump_file{dump_path};
// Compute the checksum // Compute the checksum
CHashWriter hasher(0, 0); CHashWriter hasher(0, 0);

View File

@ -7,6 +7,9 @@
#include <fs.h> #include <fs.h>
#include <string>
#include <vector>
class CWallet; class CWallet;
struct bilingual_str; struct bilingual_str;

View File

@ -19,13 +19,15 @@
#include <univalue.h> #include <univalue.h>
#include <system_error>
bool VerifyWallets(interfaces::Chain& chain) bool VerifyWallets(interfaces::Chain& chain)
{ {
if (gArgs.IsArgSet("-walletdir")) { if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path wallet_dir = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
boost::system::error_code error; std::error_code error;
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory // 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).remove_trailing_separator(); fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
if (error || !fs::exists(wallet_dir)) { if (error || !fs::exists(wallet_dir)) {
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir))); chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
return false; return false;

View File

@ -6,6 +6,7 @@
#include <chain.h> #include <chain.h>
#include <clientversion.h> #include <clientversion.h>
#include <core_io.h> #include <core_io.h>
#include <fs.h>
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <key_io.h> #include <key_io.h>
#include <merkleblock.h> #include <merkleblock.h>
@ -21,11 +22,12 @@
#include <util/translation.h> #include <util/translation.h>
#include <validation.h> #include <validation.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
#include <stdint.h> #include <cstdint>
#include <fstream>
#include <tuple> #include <tuple>
#include <string>
#include <univalue.h> #include <univalue.h>
@ -509,7 +511,7 @@ RPCHelpMan importwallet()
EnsureWalletIsUnlocked(*pwallet); EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file; std::ifstream file;
file.open(fs::u8path(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()) { if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@ -662,7 +664,7 @@ RPCHelpMan importelectrumwallet()
EnsureWalletIsUnlocked(*pwallet); EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file; std::ifstream file;
std::string strFileName = request.params[0].get_str(); std::string strFileName = request.params[0].get_str();
size_t nDotPos = strFileName.find_last_of("."); size_t nDotPos = strFileName.find_last_of(".");
if(nDotPos == std::string::npos) if(nDotPos == std::string::npos)
@ -948,7 +950,7 @@ RPCHelpMan dumpwallet()
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"); 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); file.open(filepath);
if (!file.is_open()) if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

View File

@ -2,14 +2,15 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <memory>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <fs.h> #include <fs.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <wallet/bdb.h> #include <wallet/bdb.h>
#include <fstream>
#include <memory>
#include <string>
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
@ -25,7 +26,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
std::string test_name = "test_name.dat"; std::string test_name = "test_name.dat";
const fs::path datadir = gArgs.GetDataDirNet(); const fs::path datadir = gArgs.GetDataDirNet();
fs::path file_path = datadir / test_name; fs::path file_path = datadir / test_name;
fs::ofstream f(file_path); std::ofstream f{file_path};
f.close(); f.close();
std::string filename; std::string filename;

View File

@ -7,6 +7,9 @@
#include <util/check.h> #include <util/check.h>
#include <util/system.h> #include <util/system.h>
#include <fstream>
#include <string>
#include <wallet/test/init_test_fixture.h> #include <wallet/test/init_test_fixture.h>
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
@ -23,8 +26,8 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
m_walletdir_path_cases["custom"] = m_datadir / "my_wallets"; m_walletdir_path_cases["custom"] = m_datadir / "my_wallets";
m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist"; 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["file"] = m_datadir / "not_a_directory.dat";
m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep; m_walletdir_path_cases["trailing"] = m_datadir / ("wallets" + sep);
m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep; m_walletdir_path_cases["trailing2"] = m_datadir / ("wallets" + sep + sep);
fs::current_path(m_datadir); fs::current_path(m_datadir);
m_walletdir_path_cases["relative"] = "wallets"; m_walletdir_path_cases["relative"] = "wallets";
@ -32,7 +35,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
fs::create_directories(m_walletdir_path_cases["default"]); fs::create_directories(m_walletdir_path_cases["default"]);
fs::create_directories(m_walletdir_path_cases["custom"]); fs::create_directories(m_walletdir_path_cases["custom"]);
fs::create_directories(m_walletdir_path_cases["relative"]); 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(); f.close();
} }

View File

@ -80,6 +80,8 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_CHECK_EQUAL(walletdir, expected_path); BOOST_CHECK_EQUAL(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) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
{ {
SetWalletDir(m_walletdir_path_cases["trailing2"]); SetWalletDir(m_walletdir_path_cases["trailing2"]);
@ -92,5 +94,6 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path); BOOST_CHECK_EQUAL(walletdir, expected_path);
} }
#endif
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -385,7 +385,7 @@ std::shared_ptr<CWallet> RestoreWallet(interfaces::Chain& chain, interfaces::Coi
} }
auto wallet_file = wallet_path / "wallet.dat"; auto wallet_file = wallet_path / "wallet.dat";
fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists); fs::copy_file(backup_file, wallet_file, fs::copy_options::none);
auto wallet = LoadWallet(chain, coinjoin_loader, wallet_name, load_on_start, options, status, error, warnings); auto wallet = LoadWallet(chain, coinjoin_loader, wallet_name, load_on_start, options, status, error, warnings);
@ -4662,9 +4662,9 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
// 4. For backwards compatibility, the name of a data file in -walletdir. // 4. For backwards compatibility, the name of a data file in -walletdir.
const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name)); const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
fs::file_type path_type = fs::symlink_status(wallet_path).type(); fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file || if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) || (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
(path_type == fs::regular_file && fs::PathFromString(name).filename() == fs::PathFromString(name)))) { (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
error_string = Untranslated(strprintf( error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " "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, " "database/log.?????????? files can be stored, a location where such a directory could be created, "
@ -5232,7 +5232,7 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error
} }
if(fs::exists(sourceFile)) { if(fs::exists(sourceFile)) {
try { try {
fs::copy_file(sourceFile, backupFile, fs::copy_option::fail_if_exists); fs::copy_file(sourceFile, backupFile, fs::copy_options::none);
WalletLogPrintf("Creating backup of %s -> %s\n", fs::PathToString(sourceFile), fs::PathToString(backupFile)); WalletLogPrintf("Creating backup of %s -> %s\n", fs::PathToString(sourceFile), fs::PathToString(backupFile));
} catch(fs::filesystem_error &error) { } catch(fs::filesystem_error &error) {
warnings.push_back(strprintf(_("Failed to create backup, error: %s"), fsbridge::get_filesystem_error_message(error))); warnings.push_back(strprintf(_("Failed to create backup, error: %s"), fsbridge::get_filesystem_error_message(error)));
@ -5257,7 +5257,16 @@ bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error
currentFile = dir_iter->path().filename(); currentFile = dir_iter->path().filename();
// Only add the backups for the current wallet, e.g. wallet.dat.* // Only add the backups for the current wallet, e.g. wallet.dat.*
if (fs::PathToString(dir_iter->path().stem()) == 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)); folder_set.insert(folder_set_t::value_type(
// TODO: C++17 compliant time conversion code is abominable, switch to C++20
// compliant code when C++17 support is dropped
std::chrono::system_clock::to_time_t(
std::chrono::time_point_cast<std::chrono::system_clock::duration>(
fs::last_write_time(dir_iter->path()) - fs::file_time_type::clock::now() + std::chrono::system_clock::now()
)
),
*dir_iter
));
} }
} }
} }

View File

@ -1073,7 +1073,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
{ {
bool exists; bool exists;
try { try {
exists = fs::symlink_status(path).type() != fs::file_not_found; exists = fs::symlink_status(path).type() != fs::file_type::not_found;
} catch (const fs::filesystem_error& e) { } catch (const fs::filesystem_error& e) {
error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), 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; status = DatabaseStatus::FAILED_BAD_PATH;

View File

@ -11,6 +11,7 @@ from decimal import Decimal
import os import os
import shutil import shutil
import stat import stat
import sys
import time import time
from test_framework.authproxy import JSONRPCException from test_framework.authproxy import JSONRPCException
@ -141,7 +142,7 @@ class MultiWalletTest(BitcoinTestFramework):
# should raise rpc error if wallet path can't be created # should raise rpc error if wallet path can't be created
err_code = -4 if self.options.descriptors else -1 err_code = -4 if self.options.descriptors else -1
assert_raises_rpc_error(err_code, "boost::filesystem::create_director", self.nodes[0].createwallet, "w8/bad") assert_raises_rpc_error(err_code, "filesystem error:" if sys.platform != 'win32' else "create_directories:", self.nodes[0].createwallet, "w8/bad")
# check that all requested wallets were created # check that all requested wallets were created
self.stop_node(0) self.stop_node(0)

View File

@ -50,8 +50,6 @@ fi
EXPECTED_BOOST_INCLUDES=( EXPECTED_BOOST_INCLUDES=(
boost/date_time/posix_time/posix_time.hpp boost/date_time/posix_time/posix_time.hpp
boost/filesystem.hpp
boost/filesystem/fstream.hpp
boost/multi_index/hashed_index.hpp boost/multi_index/hashed_index.hpp
boost/multi_index/ordered_index.hpp boost/multi_index/ordered_index.hpp
boost/multi_index/sequenced_index.hpp boost/multi_index/sequenced_index.hpp