refactor: Block unsafe fs::path std::string conversion calls

There is no change in behavior. This just helps prepare for the
transition from boost::filesystem to std::filesystem by avoiding calls
to methods which will be unsafe after the transaction to std::filesystem
to due lack of a boost::filesystem::path::imbue equivalent and inability
to set a predictable locale.

Co-authored-by: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com>
Co-authored-by: Kiminuo <kiminuo@protonmail.com>
Co-authored-by: MarcoFalke <falke.marco@gmail.com>
This commit is contained in:
Russell Yanofsky
2021-09-10 00:17:20 -04:00
parent b39a477ec6
commit 6544ea5035
43 changed files with 244 additions and 196 deletions

View File

@@ -13,6 +13,7 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <tinyformat.h>
/** Filesystem operations and types */
namespace fs {
@@ -23,8 +24,8 @@ using namespace boost::filesystem;
* Path class wrapper to prepare application code for transition from
* boost::filesystem library to std::filesystem implementation. The main
* purpose of the class is to define fs::path::u8string() and fs::u8path()
* functions not present in boost. In the next git commit, it also blocks calls
* to the fs::path(std::string) implicit constructor and the fs::path::string()
* functions not present in boost. It also blocks calls to the
* fs::path(std::string) implicit constructor and the fs::path::string()
* method, which worked well in the boost::filesystem implementation, but have
* unsafe and unpredictable behavior on Windows in the std::filesystem
* implementation (see implementation note in \ref PathToString for details).
@@ -33,7 +34,26 @@ class path : public boost::filesystem::path
{
public:
using boost::filesystem::path::path;
// Allow path objects arguments for compatibility.
path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {}
path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; }
path& operator/=(boost::filesystem::path path) { boost::filesystem::path::operator/=(std::move(path)); return *this; }
// Allow literal string arguments, which are safe as long as the literals are ASCII.
path(const char* c) : boost::filesystem::path(c) {}
path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; }
path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; }
path& append(const char* c) { boost::filesystem::path::append(c); return *this; }
// Disallow std::string arguments to avoid locale-dependent decoding on windows.
path(std::string) = delete;
path& operator=(std::string) = delete;
path& operator/=(std::string) = delete;
path& append(std::string) = delete;
// Disallow std::string conversion method to avoid locale-dependent encoding on windows.
std::string string() const = delete;
// Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem.
std::string u8string() const { return boost::filesystem::path::string(); }
@@ -45,6 +65,33 @@ static inline path u8path(const std::string& string)
return boost::filesystem::path(string);
}
// Disallow implicit std::string conversion for system_complete to avoid
// locale-dependent encoding on windows.
static inline path system_complete(const path& p)
{
return boost::filesystem::system_complete(p);
}
// Disallow implicit std::string conversion for exists to avoid
// locale-dependent encoding on windows.
static inline bool exists(const path& p)
{
return boost::filesystem::exists(p);
}
// Allow explicit quoted stream I/O.
static inline auto quoted(const std::string& s)
{
return boost::io::quoted(s, '&');
}
// Allow safe path append operations.
static inline path operator+(path p1, path p2)
{
p1 += std::move(p2);
return p1;
}
/**
* Convert path object to byte string. On POSIX, paths natively are byte
* strings so this is trivial. On Windows, paths natively are Unicode, so an
@@ -179,4 +226,11 @@ namespace fsbridge {
#endif // WIN32 && __GLIBCXX__
};
// Disallow path operator<< formatting in tinyformat to avoid locale-dependent
// encoding on windows.
namespace tinyformat {
template<> inline void formatValue(std::ostream&, const char*, const char*, int, const boost::filesystem::path&) = delete;
template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
} // namespace tinyformat
#endif // BITCOIN_FS_H