mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
Merge bitcoin/bitcoin#28075: util: Remove DirIsWritable, GetUniquePath
fa3da629a1
Remove DirIsWritable, GetUniquePath (MarcoFalke)fad3a9793b
Return LockResult::ErrorWrite in LockDirectory (MarcoFalke)fa0afe7408
refactor: Return enum in LockDirectory (MarcoFalke) Pull request description: `GetUniquePath` is only used in tests and in `DirIsWritable`. The check by `DirIsWritable` is redundant with the check done in `LockDirectory`. Fix the redundancy by removing everything, except `LockDirectory`. ACKs for top commit: TheCharlatan: Re-ACKfa3da629a1
hebasto: ACKfa3da629a1
, I have reviewed the code and it looks OK. Tree-SHA512: e95f18cd586de7582e9c08ac7ddb860bfcfcbc8963804f45c5784c5e4c0598dc59ae7e45dd4daf30a5020dbf8433f5db2ad06e46a8676371982003790043c6c9
This commit is contained in:
commit
f48a789385
9 changed files with 67 additions and 126 deletions
|
@ -304,7 +304,6 @@ BITCOIN_CORE_H = \
|
|||
util/fees.h \
|
||||
util/fs.h \
|
||||
util/fs_helpers.h \
|
||||
util/getuniquepath.h \
|
||||
util/golombrice.h \
|
||||
util/hash_type.h \
|
||||
util/hasher.h \
|
||||
|
@ -741,7 +740,6 @@ libbitcoin_util_a_SOURCES = \
|
|||
util/fees.cpp \
|
||||
util/fs.cpp \
|
||||
util/fs_helpers.cpp \
|
||||
util/getuniquepath.cpp \
|
||||
util/hasher.cpp \
|
||||
util/sock.cpp \
|
||||
util/syserror.cpp \
|
||||
|
@ -984,7 +982,6 @@ libbitcoinkernel_la_SOURCES = \
|
|||
util/exception.cpp \
|
||||
util/fs.cpp \
|
||||
util/fs_helpers.cpp \
|
||||
util/getuniquepath.cpp \
|
||||
util/hasher.cpp \
|
||||
util/moneystr.cpp \
|
||||
util/rbf.cpp \
|
||||
|
|
11
src/init.cpp
11
src/init.cpp
|
@ -1047,13 +1047,14 @@ static bool LockDataDirectory(bool probeOnly)
|
|||
{
|
||||
// Make sure only a single Bitcoin process is using the data directory.
|
||||
const fs::path& datadir = gArgs.GetDataDirNet();
|
||||
if (!DirIsWritable(datadir)) {
|
||||
switch (util::LockDirectory(datadir, ".lock", probeOnly)) {
|
||||
case util::LockResult::ErrorWrite:
|
||||
return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), fs::PathToString(datadir)));
|
||||
}
|
||||
if (!LockDirectory(datadir, ".lock", probeOnly)) {
|
||||
case util::LockResult::ErrorLock:
|
||||
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), fs::PathToString(datadir), PACKAGE_NAME));
|
||||
}
|
||||
return true;
|
||||
case util::LockResult::Success: return true;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool AppInitSanityChecks(const kernel::Context& kernel)
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <test/util/setup_common.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/getuniquepath.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
|
@ -101,29 +100,14 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
|
|||
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_CASE(rename)
|
||||
{
|
||||
const fs::path tmpfolder{m_args.GetDataDirBase()};
|
||||
|
||||
const fs::path path1{GetUniquePath(tmpfolder)};
|
||||
const fs::path path2{GetUniquePath(tmpfolder)};
|
||||
const fs::path path1{tmpfolder / "a"};
|
||||
const fs::path path2{tmpfolder / "b"};
|
||||
|
||||
const std::string path1_contents{"1111"};
|
||||
const std::string path2_contents{"2222"};
|
||||
|
@ -158,13 +142,13 @@ BOOST_AUTO_TEST_CASE(create_directories)
|
|||
// Test fs::create_directories workaround.
|
||||
const fs::path tmpfolder{m_args.GetDataDirBase()};
|
||||
|
||||
const fs::path dir{GetUniquePath(tmpfolder)};
|
||||
const fs::path dir{tmpfolder / "a"};
|
||||
fs::create_directory(dir);
|
||||
BOOST_CHECK(fs::exists(dir));
|
||||
BOOST_CHECK(fs::is_directory(dir));
|
||||
BOOST_CHECK(!fs::create_directories(dir));
|
||||
|
||||
const fs::path symlink{GetUniquePath(tmpfolder)};
|
||||
const fs::path symlink{tmpfolder / "b"};
|
||||
fs::create_directory_symlink(dir, symlink);
|
||||
BOOST_CHECK(fs::exists(symlink));
|
||||
BOOST_CHECK(fs::is_symlink(symlink));
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <util/bitdeque.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/getuniquepath.h>
|
||||
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
|
||||
#include <util/moneystr.h>
|
||||
#include <util/overflow.h>
|
||||
|
@ -1106,15 +1105,16 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
|
|||
BOOST_CHECK(!ParseFixedPoint("31.999999999999999999999", 3, &amount));
|
||||
}
|
||||
|
||||
static void TestOtherThread(fs::path dirname, fs::path lockname, bool *result)
|
||||
{
|
||||
*result = LockDirectory(dirname, lockname);
|
||||
}
|
||||
|
||||
#ifndef WIN32 // Cannot do this test on WIN32 due to lack of fork()
|
||||
static constexpr char LockCommand = 'L';
|
||||
static constexpr char UnlockCommand = 'U';
|
||||
static constexpr char ExitCommand = 'X';
|
||||
enum : char {
|
||||
ResSuccess = 2, // Start with 2 to avoid accidental collision with common values 0 and 1
|
||||
ResErrorWrite,
|
||||
ResErrorLock,
|
||||
ResUnlockSuccess,
|
||||
};
|
||||
|
||||
[[noreturn]] static void TestOtherProcess(fs::path dirname, fs::path lockname, int fd)
|
||||
{
|
||||
|
@ -1122,15 +1122,22 @@ static constexpr char ExitCommand = 'X';
|
|||
while (true) {
|
||||
int rv = read(fd, &ch, 1); // Wait for command
|
||||
assert(rv == 1);
|
||||
switch(ch) {
|
||||
switch (ch) {
|
||||
case LockCommand:
|
||||
ch = LockDirectory(dirname, lockname);
|
||||
ch = [&] {
|
||||
switch (util::LockDirectory(dirname, lockname)) {
|
||||
case util::LockResult::Success: return ResSuccess;
|
||||
case util::LockResult::ErrorWrite: return ResErrorWrite;
|
||||
case util::LockResult::ErrorLock: return ResErrorLock;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
assert(false);
|
||||
}();
|
||||
rv = write(fd, &ch, 1);
|
||||
assert(rv == 1);
|
||||
break;
|
||||
case UnlockCommand:
|
||||
ReleaseDirectoryLocks();
|
||||
ch = true; // Always succeeds
|
||||
ch = ResUnlockSuccess; // Always succeeds
|
||||
rv = write(fd, &ch, 1);
|
||||
assert(rv == 1);
|
||||
break;
|
||||
|
@ -1165,53 +1172,58 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
|
|||
TestOtherProcess(dirname, lockname, fd[0]);
|
||||
}
|
||||
BOOST_CHECK_EQUAL(close(fd[0]), 0); // Parent: close child end
|
||||
|
||||
char ch;
|
||||
// Lock on non-existent directory should fail
|
||||
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
|
||||
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
|
||||
BOOST_CHECK_EQUAL(ch, ResErrorWrite);
|
||||
#endif
|
||||
// Lock on non-existent directory should fail
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), false);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname), util::LockResult::ErrorWrite);
|
||||
|
||||
fs::create_directories(dirname);
|
||||
|
||||
// Probing lock on new directory should succeed
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname, true), util::LockResult::Success);
|
||||
|
||||
// Persistent lock on new directory should succeed
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname), util::LockResult::Success);
|
||||
|
||||
// Another lock on the directory from the same thread should succeed
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname), util::LockResult::Success);
|
||||
|
||||
// Another lock on the directory from a different thread within the same process should succeed
|
||||
bool threadresult;
|
||||
std::thread thr(TestOtherThread, dirname, lockname, &threadresult);
|
||||
util::LockResult threadresult;
|
||||
std::thread thr([&] { threadresult = util::LockDirectory(dirname, lockname); });
|
||||
thr.join();
|
||||
BOOST_CHECK_EQUAL(threadresult, true);
|
||||
BOOST_CHECK_EQUAL(threadresult, util::LockResult::Success);
|
||||
#ifndef WIN32
|
||||
// Try to acquire lock in child process while we're holding it, this should fail.
|
||||
char ch;
|
||||
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
|
||||
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
|
||||
BOOST_CHECK_EQUAL((bool)ch, false);
|
||||
BOOST_CHECK_EQUAL(ch, ResErrorLock);
|
||||
|
||||
// Give up our lock
|
||||
ReleaseDirectoryLocks();
|
||||
// Probing lock from our side now should succeed, but not hold on to the lock.
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname, true), util::LockResult::Success);
|
||||
|
||||
// Try to acquire the lock in the child process, this should be successful.
|
||||
BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
|
||||
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
|
||||
BOOST_CHECK_EQUAL((bool)ch, true);
|
||||
BOOST_CHECK_EQUAL(ch, ResSuccess);
|
||||
|
||||
// When we try to probe the lock now, it should fail.
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), false);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname, true), util::LockResult::ErrorLock);
|
||||
|
||||
// Unlock the lock in the child process
|
||||
BOOST_CHECK_EQUAL(write(fd[1], &UnlockCommand, 1), 1);
|
||||
BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
|
||||
BOOST_CHECK_EQUAL((bool)ch, true);
|
||||
BOOST_CHECK_EQUAL(ch, ResUnlockSuccess);
|
||||
|
||||
// When we try to probe the lock now, it should succeed.
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname, true), util::LockResult::Success);
|
||||
|
||||
// Re-lock the lock in the child process, then wait for it to exit, check
|
||||
// successful return. After that, we check that exiting the process
|
||||
|
@ -1224,7 +1236,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
|
|||
BOOST_CHECK_EQUAL(write(fd[1], &ExitCommand, 1), 1);
|
||||
BOOST_CHECK_EQUAL(waitpid(pid, &processstatus, 0), pid);
|
||||
BOOST_CHECK_EQUAL(processstatus, 0);
|
||||
BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
|
||||
BOOST_CHECK_EQUAL(util::LockDirectory(dirname, lockname, true), util::LockResult::Success);
|
||||
|
||||
// Restore SIGCHLD
|
||||
signal(SIGCHLD, old_handler);
|
||||
|
@ -1235,22 +1247,6 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
|
|||
fs::remove_all(dirname);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_DirIsWritable)
|
||||
{
|
||||
// Should be able to write to the data dir.
|
||||
fs::path tmpdirname = m_args.GetDataDirBase();
|
||||
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
|
||||
|
||||
// Should not be able to write to a non-existent dir.
|
||||
tmpdirname = GetUniquePath(tmpdirname);
|
||||
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
|
||||
|
||||
fs::create_directory(tmpdirname);
|
||||
// Should be able to write to it now.
|
||||
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
|
||||
fs::remove(tmpdirname);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_ToLower)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(ToLower('@'), '@');
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <logging.h>
|
||||
#include <sync.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/getuniquepath.h>
|
||||
#include <util/syserror.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
@ -51,31 +50,35 @@ static GlobalMutex cs_dir_locks;
|
|||
* is called.
|
||||
*/
|
||||
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
|
||||
|
||||
bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
|
||||
namespace util {
|
||||
LockResult LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
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(fs::PathToString(pathLockFile))) {
|
||||
return true;
|
||||
return LockResult::Success;
|
||||
}
|
||||
|
||||
// Create empty lock file if it doesn't exist.
|
||||
FILE* file = fsbridge::fopen(pathLockFile, "a");
|
||||
if (file) fclose(file);
|
||||
if (auto created{fsbridge::fopen(pathLockFile, "a")}) {
|
||||
std::fclose(created);
|
||||
} else {
|
||||
return LockResult::ErrorWrite;
|
||||
}
|
||||
auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
|
||||
if (!lock->TryLock()) {
|
||||
return error("Error while attempting to lock directory %s: %s", fs::PathToString(directory), lock->GetReason());
|
||||
error("Error while attempting to lock directory %s: %s", fs::PathToString(directory), lock->GetReason());
|
||||
return LockResult::ErrorLock;
|
||||
}
|
||||
if (!probe_only) {
|
||||
// Lock successful and we're not just probing, put it into the map
|
||||
dir_locks.emplace(fs::PathToString(pathLockFile), std::move(lock));
|
||||
}
|
||||
return true;
|
||||
return LockResult::Success;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
|
@ -88,19 +91,6 @@ void ReleaseDirectoryLocks()
|
|||
dir_locks.clear();
|
||||
}
|
||||
|
||||
bool DirIsWritable(const fs::path& directory)
|
||||
{
|
||||
fs::path tmpFile = GetUniquePath(directory);
|
||||
|
||||
FILE* file = fsbridge::fopen(tmpFile, "a");
|
||||
if (!file) return false;
|
||||
|
||||
fclose(file);
|
||||
remove(tmpFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
|
||||
{
|
||||
constexpr uint64_t min_disk_space = 52428800; // 50 MiB
|
||||
|
|
|
@ -35,9 +35,15 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length);
|
|||
*/
|
||||
[[nodiscard]] bool RenameOver(fs::path src, fs::path dest);
|
||||
|
||||
bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only = false);
|
||||
namespace util {
|
||||
enum class LockResult {
|
||||
Success,
|
||||
ErrorWrite,
|
||||
ErrorLock,
|
||||
};
|
||||
[[nodiscard]] LockResult LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only = false);
|
||||
} // namespace util
|
||||
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name);
|
||||
bool DirIsWritable(const fs::path& directory);
|
||||
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
|
||||
|
||||
/** Get the size of a file by scanning it.
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
// 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 <random.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
fs::path GetUniquePath(const fs::path& base)
|
||||
{
|
||||
FastRandomContext rnd;
|
||||
fs::path tmpFile = base / fs::u8path(HexStr(rnd.randbytes(8)));
|
||||
return tmpFile;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// 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 BITCOIN_UTIL_GETUNIQUEPATH_H
|
||||
#define BITCOIN_UTIL_GETUNIQUEPATH_H
|
||||
|
||||
#include <util/fs.h>
|
||||
|
||||
/**
|
||||
* 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 // BITCOIN_UTIL_GETUNIQUEPATH_H
|
|
@ -149,7 +149,7 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
|
|||
|
||||
fs::path pathIn = fs::PathFromString(strPath);
|
||||
TryCreateDirectories(pathIn);
|
||||
if (!LockDirectory(pathIn, ".walletlock")) {
|
||||
if (util::LockDirectory(pathIn, ".walletlock") != util::LockResult::Success) {
|
||||
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
|
||||
err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
|
||||
return false;
|
||||
|
|
Loading…
Add table
Reference in a new issue