mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 15:09:59 +01:00
Merge bitcoin/bitcoin#22937: refactor: Forbid calling unsafe fs::path(std::string) constructor and fs::path::string() method
6544ea5035refactor: Block unsafe fs::path std::string conversion calls (Russell Yanofsky)b39a477ec6refactor: Add fs::PathToString, fs::PathFromString, u8string, u8path functions (Russell Yanofsky) Pull request description: The `fs::path` class has a `std::string` constructor which will implicitly convert from strings. Implicit conversions like this are not great in general because they can hide complexity and inefficiencies in the code, but this case is especially bad, because after the transition from `boost::filesystem` to `std::filesystem` in #20744 the behavior of this constructor on windows will be more complicated and can mangle path strings. The `fs::path` class also has a `.string()` method which is inverse of the constructor and has the same problems. Fix this by replacing the unsafe method calls with `PathToString` and `PathFromString` function calls, and by forbidding unsafe method calls in the future. ACKs for top commit: kiminuo: ACK6544ea5035laanwj: Code review ACK6544ea5035hebasto: re-ACK6544ea5035, only added `fsbridge_stem` test case, updated comment, and rebased since my [previous](https://github.com/bitcoin/bitcoin/pull/22937#pullrequestreview-765503126) review. Verified with the following command: Tree-SHA512: c36324740eb4ee55151146626166c00d5ccc4b6f3df777e75c112bcb4d1db436c1d9cc8c29a1e7fb96051457d317961ab42e6c380c3be2771d135771b2b49fa0
This commit is contained in:
@@ -201,7 +201,7 @@ std::vector<bool> DecodeAsmap(fs::path path)
|
||||
}
|
||||
fseek(filestr, 0, SEEK_END);
|
||||
int length = ftell(filestr);
|
||||
LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
|
||||
LogPrintf("Opened asmap file %s (%d bytes) from disk\n", fs::quoted(fs::PathToString(path)), length);
|
||||
fseek(filestr, 0, SEEK_SET);
|
||||
uint8_t cur_byte;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
@@ -211,7 +211,7 @@ std::vector<bool> DecodeAsmap(fs::path path)
|
||||
}
|
||||
}
|
||||
if (!SanityCheckASMap(bits, 128)) {
|
||||
LogPrintf("Sanity check of asmap file %s failed\n", path);
|
||||
LogPrintf("Sanity check of asmap file %s failed\n", fs::quoted(fs::PathToString(path)));
|
||||
return {};
|
||||
}
|
||||
return bits;
|
||||
|
||||
@@ -66,24 +66,24 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
|
||||
fsbridge::ifstream file;
|
||||
file.open(path);
|
||||
if (!file.is_open()) {
|
||||
errors.emplace_back(strprintf("%s. Please check permissions.", path.string()));
|
||||
errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));
|
||||
return false;
|
||||
}
|
||||
|
||||
SettingsValue in;
|
||||
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
|
||||
errors.emplace_back(strprintf("Unable to parse settings file %s", path.string()));
|
||||
errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.fail()) {
|
||||
errors.emplace_back(strprintf("Failed reading settings file %s", path.string()));
|
||||
errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));
|
||||
return false;
|
||||
}
|
||||
file.close(); // Done with file descriptor. Release while copying data.
|
||||
|
||||
if (!in.isObject()) {
|
||||
errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string()));
|
||||
errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
|
||||
for (size_t i = 0; i < in_keys.size(); ++i) {
|
||||
auto inserted = values.emplace(in_keys[i], in_values[i]);
|
||||
if (!inserted.second) {
|
||||
errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string()));
|
||||
errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));
|
||||
}
|
||||
}
|
||||
return errors.empty();
|
||||
@@ -109,7 +109,7 @@ bool WriteSettings(const fs::path& path,
|
||||
fsbridge::ofstream file;
|
||||
file.open(path);
|
||||
if (file.fail()) {
|
||||
errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string()));
|
||||
errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));
|
||||
return false;
|
||||
}
|
||||
file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
|
||||
|
||||
@@ -98,7 +98,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;
|
||||
}
|
||||
|
||||
@@ -107,11 +107,11 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
|
||||
if (file) fclose(file);
|
||||
auto lock = std::make_unique<fsbridge::FileLock>(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;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
|
||||
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
dir_locks.erase((directory / lockfile_name).string());
|
||||
dir_locks.erase(fs::PathToString(directory / lockfile_name));
|
||||
}
|
||||
|
||||
void ReleaseDirectoryLocks()
|
||||
@@ -242,7 +242,7 @@ namespace {
|
||||
fs::path StripRedundantLastElementsOfPath(const fs::path& path)
|
||||
{
|
||||
auto result = path;
|
||||
while (result.filename().string() == ".") {
|
||||
while (fs::PathToString(result.filename()) == ".") {
|
||||
result = result.parent_path();
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
|
||||
if (!path.empty()) return path;
|
||||
|
||||
if (IsArgSet("-blocksdir")) {
|
||||
path = fs::system_complete(GetArg("-blocksdir", ""));
|
||||
path = fs::system_complete(fs::PathFromString(GetArg("-blocksdir", "")));
|
||||
if (!fs::is_directory(path)) {
|
||||
path = "";
|
||||
return path;
|
||||
@@ -411,7 +411,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
|
||||
path = GetDataDirBase();
|
||||
}
|
||||
|
||||
path /= BaseParams().DataDir();
|
||||
path /= fs::PathFromString(BaseParams().DataDir());
|
||||
path /= "blocks";
|
||||
fs::create_directories(path);
|
||||
path = StripRedundantLastElementsOfPath(path);
|
||||
@@ -429,7 +429,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
|
||||
|
||||
std::string datadir = GetArg("-datadir", "");
|
||||
if (!datadir.empty()) {
|
||||
path = fs::system_complete(datadir);
|
||||
path = fs::system_complete(fs::PathFromString(datadir));
|
||||
if (!fs::is_directory(path)) {
|
||||
path = "";
|
||||
return path;
|
||||
@@ -438,7 +438,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
|
||||
path = GetDefaultDataDir();
|
||||
}
|
||||
if (net_specific)
|
||||
path /= BaseParams().DataDir();
|
||||
path /= fs::PathFromString(BaseParams().DataDir());
|
||||
|
||||
if (fs::create_directories(path)) {
|
||||
// This is the first run, create wallets subdirectory too
|
||||
@@ -517,7 +517,7 @@ bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const
|
||||
}
|
||||
if (filepath) {
|
||||
std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME);
|
||||
*filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
|
||||
*filepath = fsbridge::AbsPathJoin(GetDataDirNet(), fs::PathFromString(temp ? settings + ".tmp" : settings));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -572,7 +572,7 @@ bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors) const
|
||||
return false;
|
||||
}
|
||||
if (!RenameOver(path_tmp, path)) {
|
||||
SaveErrors({strprintf("Failed renaming settings file %s to %s\n", path_tmp.string(), path.string())}, errors);
|
||||
SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -809,12 +809,12 @@ fs::path GetDefaultDataDir()
|
||||
bool CheckDataDirOption()
|
||||
{
|
||||
std::string datadir = gArgs.GetArg("-datadir", "");
|
||||
return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
|
||||
return datadir.empty() || fs::is_directory(fs::system_complete(fs::PathFromString(datadir)));
|
||||
}
|
||||
|
||||
fs::path GetConfigFile(const std::string& confPath)
|
||||
{
|
||||
return AbsPathForConfigVal(fs::path(confPath), false);
|
||||
return AbsPathForConfigVal(fs::PathFromString(confPath), false);
|
||||
}
|
||||
|
||||
static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
|
||||
@@ -1065,7 +1065,7 @@ bool RenameOver(fs::path src, fs::path dest)
|
||||
return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING) != 0;
|
||||
#else
|
||||
int rc = std::rename(src.string().c_str(), dest.string().c_str());
|
||||
int rc = std::rename(src.c_str(), dest.c_str());
|
||||
return (rc == 0);
|
||||
#endif /* WIN32 */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user