mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
refactor: Extract util/fs_helpers from util/system
This is an extraction of filesystem related functions from util/system into their own utility file. The background of this commit is an ongoing effort to decouple the libbitcoinkernel library from the ArgsManager defined in system.h. Moving these functions out of system.h allows including them from a separate source file without including the ArgsManager definitions from system.h.
This commit is contained in:
parent
40e1c4d402
commit
18fb36367a
22 changed files with 382 additions and 318 deletions
|
@ -285,6 +285,7 @@ BITCOIN_CORE_H = \
|
|||
util/exception.h \
|
||||
util/fastrange.h \
|
||||
util/fees.h \
|
||||
util/fs_helpers.h \
|
||||
util/getuniquepath.h \
|
||||
util/golombrice.h \
|
||||
util/hash_type.h \
|
||||
|
@ -707,6 +708,7 @@ libbitcoin_util_a_SOURCES = \
|
|||
util/error.cpp \
|
||||
util/exception.cpp \
|
||||
util/fees.cpp \
|
||||
util/fs_helpers.cpp \
|
||||
util/getuniquepath.cpp \
|
||||
util/hasher.cpp \
|
||||
util/sock.cpp \
|
||||
|
@ -951,6 +953,7 @@ libbitcoinkernel_la_SOURCES = \
|
|||
uint256.cpp \
|
||||
util/check.cpp \
|
||||
util/exception.cpp \
|
||||
util/fs_helpers.cpp \
|
||||
util/getuniquepath.cpp \
|
||||
util/hasher.cpp \
|
||||
util/moneystr.cpp \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <streams.h>
|
||||
#include <tinyformat.h>
|
||||
#include <univalue.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/settings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/translation.h>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include <logging.h>
|
||||
#include <random.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <flatfile.h>
|
||||
#include <logging.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/system.h>
|
||||
#include <util/fs_helpers.h>
|
||||
|
||||
FlatFileSeq::FlatFileSeq(fs::path dir, const char* prefix, size_t chunk_size) :
|
||||
m_dir(std::move(dir)),
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <hash.h>
|
||||
#include <index/blockfilterindex.h>
|
||||
#include <node/blockstorage.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
#include <txmempool.h>
|
||||
#include <util/asmap.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <logging.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/string.h>
|
||||
#include <util/system.h>
|
||||
#include <util/time.h>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <sync.h>
|
||||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <util/system.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/time.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <script/script.h>
|
||||
#include <script/standard.h>
|
||||
#include <util/exception.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/system.h>
|
||||
#include <util/time.h>
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <qt/optionsmodel.h>
|
||||
|
||||
#include <interfaces/node.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <qt/psbtoperationsdialog.h>
|
||||
#include <qt/walletmodel.h>
|
||||
#include <qt/walletview.h>
|
||||
#include <util/system.h>
|
||||
#include <util/fs_helpers.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
#include <random.h>
|
||||
#include <rpc/protocol.h>
|
||||
#include <util/system.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
#include <fs.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/system.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/getuniquepath.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/system.h>
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <fs.h>
|
||||
#include <hash.h> // For Hash()
|
||||
|
@ -12,6 +10,8 @@
|
|||
#include <test/util/random.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <uint256.h>
|
||||
#include <util/bitdeque.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/getuniquepath.h>
|
||||
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
|
||||
#include <util/moneystr.h>
|
||||
|
@ -22,7 +22,6 @@
|
|||
#include <util/string.h>
|
||||
#include <util/time.h>
|
||||
#include <util/vector.h>
|
||||
#include <util/bitdeque.h>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
|
295
src/util/fs_helpers.cpp
Normal file
295
src/util/fs_helpers.cpp
Normal file
|
@ -0,0 +1,295 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2023 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 <util/fs_helpers.h>
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config/bitcoin-config.h>
|
||||
#endif
|
||||
|
||||
#include <fs.h>
|
||||
#include <logging.h>
|
||||
#include <sync.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/getuniquepath.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#ifndef WIN32
|
||||
// for posix_fallocate, in configure.ac we check if it is present after this
|
||||
#ifdef __linux__
|
||||
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h> /* For _get_osfhandle, _chsize */
|
||||
#include <shlobj.h> /* For SHGetSpecialFolderPathW */
|
||||
#endif // WIN32
|
||||
|
||||
/** Mutex to protect dir_locks. */
|
||||
static GlobalMutex cs_dir_locks;
|
||||
/** A map that contains all the currently held directory locks. After
|
||||
* successful locking, these will be held here until the global destructor
|
||||
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Create empty lock file if it doesn't exist.
|
||||
FILE* file = fsbridge::fopen(pathLockFile, "a");
|
||||
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", fs::PathToString(directory), lock->GetReason());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
dir_locks.erase(fs::PathToString(directory / lockfile_name));
|
||||
}
|
||||
|
||||
void ReleaseDirectoryLocks()
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
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
|
||||
|
||||
uint64_t free_bytes_available = fs::space(dir).available;
|
||||
return free_bytes_available >= min_disk_space + additional_bytes;
|
||||
}
|
||||
|
||||
std::streampos GetFileSize(const char* path, std::streamsize max)
|
||||
{
|
||||
std::ifstream file{path, std::ios::binary};
|
||||
file.ignore(max);
|
||||
return file.gcount();
|
||||
}
|
||||
|
||||
bool FileCommit(FILE* file)
|
||||
{
|
||||
if (fflush(file) != 0) { // harmless if redundantly called
|
||||
LogPrintf("%s: fflush failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#ifdef WIN32
|
||||
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
||||
if (FlushFileBuffers(hFile) == 0) {
|
||||
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
|
||||
return false;
|
||||
}
|
||||
#elif defined(MAC_OSX) && defined(F_FULLFSYNC)
|
||||
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
|
||||
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#elif HAVE_FDATASYNC
|
||||
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
|
||||
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
|
||||
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void DirectoryCommit(const fs::path& dirname)
|
||||
{
|
||||
#ifndef WIN32
|
||||
FILE* file = fsbridge::fopen(dirname, "r");
|
||||
if (file) {
|
||||
fsync(fileno(file));
|
||||
fclose(file);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TruncateFile(FILE* file, unsigned int length)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
return _chsize(_fileno(file), length) == 0;
|
||||
#else
|
||||
return ftruncate(fileno(file), length) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* this function tries to raise the file descriptor limit to the requested number.
|
||||
* It returns the actual file descriptor limit (which may be more or less than nMinFD)
|
||||
*/
|
||||
int RaiseFileDescriptorLimit(int nMinFD)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
return 2048;
|
||||
#else
|
||||
struct rlimit limitFD;
|
||||
if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
|
||||
if (limitFD.rlim_cur < (rlim_t)nMinFD) {
|
||||
limitFD.rlim_cur = nMinFD;
|
||||
if (limitFD.rlim_cur > limitFD.rlim_max)
|
||||
limitFD.rlim_cur = limitFD.rlim_max;
|
||||
setrlimit(RLIMIT_NOFILE, &limitFD);
|
||||
getrlimit(RLIMIT_NOFILE, &limitFD);
|
||||
}
|
||||
return limitFD.rlim_cur;
|
||||
}
|
||||
return nMinFD; // getrlimit failed, assume it's fine
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* this function tries to make a particular range of a file allocated (corresponding to disk space)
|
||||
* it is advisory, and the range specified in the arguments will never contain live data
|
||||
*/
|
||||
void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
// Windows-specific version
|
||||
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
||||
LARGE_INTEGER nFileSize;
|
||||
int64_t nEndPos = (int64_t)offset + length;
|
||||
nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
|
||||
nFileSize.u.HighPart = nEndPos >> 32;
|
||||
SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
|
||||
SetEndOfFile(hFile);
|
||||
#elif defined(MAC_OSX)
|
||||
// OSX specific version
|
||||
// NOTE: Contrary to other OS versions, the OSX version assumes that
|
||||
// NOTE: offset is the size of the file.
|
||||
fstore_t fst;
|
||||
fst.fst_flags = F_ALLOCATECONTIG;
|
||||
fst.fst_posmode = F_PEOFPOSMODE;
|
||||
fst.fst_offset = 0;
|
||||
fst.fst_length = length; // mac os fst_length takes the # of free bytes to allocate, not desired file size
|
||||
fst.fst_bytesalloc = 0;
|
||||
if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
|
||||
fst.fst_flags = F_ALLOCATEALL;
|
||||
fcntl(fileno(file), F_PREALLOCATE, &fst);
|
||||
}
|
||||
ftruncate(fileno(file), static_cast<off_t>(offset) + length);
|
||||
#else
|
||||
#if defined(HAVE_POSIX_FALLOCATE)
|
||||
// Version using posix_fallocate
|
||||
off_t nEndPos = (off_t)offset + length;
|
||||
if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
|
||||
#endif
|
||||
// Fallback version
|
||||
// TODO: just write one byte per block
|
||||
static const char buf[65536] = {};
|
||||
if (fseek(file, offset, SEEK_SET)) {
|
||||
return;
|
||||
}
|
||||
while (length > 0) {
|
||||
unsigned int now = 65536;
|
||||
if (length < now)
|
||||
now = length;
|
||||
fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
|
||||
length -= now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
||||
{
|
||||
WCHAR pszPath[MAX_PATH] = L"";
|
||||
|
||||
if (SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) {
|
||||
return fs::path(pszPath);
|
||||
}
|
||||
|
||||
LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
|
||||
return fs::path("");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool RenameOver(fs::path src, fs::path dest)
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
// This is a workaround for a bug in libstdc++ which
|
||||
// implements std::filesystem::rename with _wrename function.
|
||||
// This bug has been fixed in upstream:
|
||||
// - GCC 10.3: 8dd1c1085587c9f8a21bb5e588dfe1e8cdbba79e
|
||||
// - GCC 11.1: 1dfd95f0a0ca1d9e6cbc00e6cbfd1fa20a98f312
|
||||
// For more details see the commits mentioned above.
|
||||
return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING) != 0;
|
||||
#else
|
||||
std::error_code error;
|
||||
fs::rename(src, dest, error);
|
||||
return !error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores exceptions thrown by create_directories if the requested directory exists.
|
||||
* Specifically handles case where path p exists, but it wasn't possible for the user to
|
||||
* write to the parent directory.
|
||||
*/
|
||||
bool TryCreateDirectories(const fs::path& p)
|
||||
{
|
||||
try {
|
||||
return fs::create_directories(p);
|
||||
} catch (const fs::filesystem_error&) {
|
||||
if (!fs::exists(p) || !fs::is_directory(p))
|
||||
throw;
|
||||
}
|
||||
|
||||
// create_directories didn't create the directory, it had to have existed already
|
||||
return false;
|
||||
}
|
63
src/util/fs_helpers.h
Normal file
63
src/util/fs_helpers.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2023 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_FS_HELPERS_H
|
||||
#define BITCOIN_UTIL_FS_HELPERS_H
|
||||
|
||||
#include <fs.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
|
||||
/**
|
||||
* Ensure file contents are fully committed to disk, using a platform-specific
|
||||
* feature analogous to fsync().
|
||||
*/
|
||||
bool FileCommit(FILE* file);
|
||||
|
||||
/**
|
||||
* Sync directory contents. This is required on some environments to ensure that
|
||||
* newly created files are committed to disk.
|
||||
*/
|
||||
void DirectoryCommit(const fs::path& dirname);
|
||||
|
||||
bool TruncateFile(FILE* file, unsigned int length);
|
||||
int RaiseFileDescriptorLimit(int nMinFD);
|
||||
void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length);
|
||||
|
||||
/**
|
||||
* Rename src to dest.
|
||||
* @return true if the rename was successful.
|
||||
*/
|
||||
[[nodiscard]] bool RenameOver(fs::path src, fs::path dest);
|
||||
|
||||
bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only = false);
|
||||
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.
|
||||
*
|
||||
* @param[in] path The file path
|
||||
* @param[in] max Stop seeking beyond this limit
|
||||
* @return The file size or max
|
||||
*/
|
||||
std::streampos GetFileSize(const char* path, std::streamsize max = std::numeric_limits<std::streamsize>::max());
|
||||
|
||||
/** Release all directory locks. This is used for unit testing only, at runtime
|
||||
* the global destructor will take care of the locks.
|
||||
*/
|
||||
void ReleaseDirectoryLocks();
|
||||
|
||||
bool TryCreateDirectories(const fs::path& p);
|
||||
fs::path GetDefaultDataDir();
|
||||
|
||||
#ifdef WIN32
|
||||
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
|
||||
#endif
|
||||
|
||||
#endif // BITCOIN_UTIL_FS_HELPERS_H
|
|
@ -9,6 +9,7 @@
|
|||
#include <fs.h>
|
||||
#include <sync.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/getuniquepath.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
|
@ -22,17 +23,6 @@
|
|||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
// for posix_fallocate, in configure.ac we check if it is present after this
|
||||
#ifdef __linux__
|
||||
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <fcntl.h>
|
||||
|
@ -44,7 +34,6 @@
|
|||
|
||||
#include <codecvt>
|
||||
|
||||
#include <io.h> /* for _commit */
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
@ -72,78 +61,6 @@ const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
|||
|
||||
ArgsManager gArgs;
|
||||
|
||||
/** Mutex to protect dir_locks. */
|
||||
static GlobalMutex cs_dir_locks;
|
||||
/** A map that contains all the currently held directory locks. After
|
||||
* successful locking, these will be held here until the global destructor
|
||||
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Create empty lock file if it doesn't exist.
|
||||
FILE* file = fsbridge::fopen(pathLockFile, "a");
|
||||
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", fs::PathToString(directory), lock->GetReason());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
dir_locks.erase(fs::PathToString(directory / lockfile_name));
|
||||
}
|
||||
|
||||
void ReleaseDirectoryLocks()
|
||||
{
|
||||
LOCK(cs_dir_locks);
|
||||
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
|
||||
|
||||
uint64_t free_bytes_available = fs::space(dir).available;
|
||||
return free_bytes_available >= min_disk_space + additional_bytes;
|
||||
}
|
||||
|
||||
std::streampos GetFileSize(const char* path, std::streamsize max) {
|
||||
std::ifstream file{path, std::ios::binary};
|
||||
file.ignore(max);
|
||||
return file.gcount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a string argument as a boolean.
|
||||
*
|
||||
|
@ -1091,182 +1008,6 @@ void ArgsManager::LogArgs() const
|
|||
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
|
||||
}
|
||||
|
||||
bool RenameOver(fs::path src, fs::path dest)
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
// This is a workaround for a bug in libstdc++ which
|
||||
// implements std::filesystem::rename with _wrename function.
|
||||
// This bug has been fixed in upstream:
|
||||
// - GCC 10.3: 8dd1c1085587c9f8a21bb5e588dfe1e8cdbba79e
|
||||
// - GCC 11.1: 1dfd95f0a0ca1d9e6cbc00e6cbfd1fa20a98f312
|
||||
// For more details see the commits mentioned above.
|
||||
return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING) != 0;
|
||||
#else
|
||||
std::error_code error;
|
||||
fs::rename(src, dest, error);
|
||||
return !error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores exceptions thrown by create_directories if the requested directory exists.
|
||||
* Specifically handles case where path p exists, but it wasn't possible for the user to
|
||||
* write to the parent directory.
|
||||
*/
|
||||
bool TryCreateDirectories(const fs::path& p)
|
||||
{
|
||||
try
|
||||
{
|
||||
return fs::create_directories(p);
|
||||
} catch (const fs::filesystem_error&) {
|
||||
if (!fs::exists(p) || !fs::is_directory(p))
|
||||
throw;
|
||||
}
|
||||
|
||||
// create_directories didn't create the directory, it had to have existed already
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileCommit(FILE *file)
|
||||
{
|
||||
if (fflush(file) != 0) { // harmless if redundantly called
|
||||
LogPrintf("%s: fflush failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#ifdef WIN32
|
||||
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
||||
if (FlushFileBuffers(hFile) == 0) {
|
||||
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
|
||||
return false;
|
||||
}
|
||||
#elif defined(MAC_OSX) && defined(F_FULLFSYNC)
|
||||
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
|
||||
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#elif HAVE_FDATASYNC
|
||||
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
|
||||
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
|
||||
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void DirectoryCommit(const fs::path &dirname)
|
||||
{
|
||||
#ifndef WIN32
|
||||
FILE* file = fsbridge::fopen(dirname, "r");
|
||||
if (file) {
|
||||
fsync(fileno(file));
|
||||
fclose(file);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TruncateFile(FILE *file, unsigned int length) {
|
||||
#if defined(WIN32)
|
||||
return _chsize(_fileno(file), length) == 0;
|
||||
#else
|
||||
return ftruncate(fileno(file), length) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* this function tries to raise the file descriptor limit to the requested number.
|
||||
* It returns the actual file descriptor limit (which may be more or less than nMinFD)
|
||||
*/
|
||||
int RaiseFileDescriptorLimit(int nMinFD) {
|
||||
#if defined(WIN32)
|
||||
return 2048;
|
||||
#else
|
||||
struct rlimit limitFD;
|
||||
if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
|
||||
if (limitFD.rlim_cur < (rlim_t)nMinFD) {
|
||||
limitFD.rlim_cur = nMinFD;
|
||||
if (limitFD.rlim_cur > limitFD.rlim_max)
|
||||
limitFD.rlim_cur = limitFD.rlim_max;
|
||||
setrlimit(RLIMIT_NOFILE, &limitFD);
|
||||
getrlimit(RLIMIT_NOFILE, &limitFD);
|
||||
}
|
||||
return limitFD.rlim_cur;
|
||||
}
|
||||
return nMinFD; // getrlimit failed, assume it's fine
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* this function tries to make a particular range of a file allocated (corresponding to disk space)
|
||||
* it is advisory, and the range specified in the arguments will never contain live data
|
||||
*/
|
||||
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
|
||||
#if defined(WIN32)
|
||||
// Windows-specific version
|
||||
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
||||
LARGE_INTEGER nFileSize;
|
||||
int64_t nEndPos = (int64_t)offset + length;
|
||||
nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
|
||||
nFileSize.u.HighPart = nEndPos >> 32;
|
||||
SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
|
||||
SetEndOfFile(hFile);
|
||||
#elif defined(MAC_OSX)
|
||||
// OSX specific version
|
||||
// NOTE: Contrary to other OS versions, the OSX version assumes that
|
||||
// NOTE: offset is the size of the file.
|
||||
fstore_t fst;
|
||||
fst.fst_flags = F_ALLOCATECONTIG;
|
||||
fst.fst_posmode = F_PEOFPOSMODE;
|
||||
fst.fst_offset = 0;
|
||||
fst.fst_length = length; // mac os fst_length takes the # of free bytes to allocate, not desired file size
|
||||
fst.fst_bytesalloc = 0;
|
||||
if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
|
||||
fst.fst_flags = F_ALLOCATEALL;
|
||||
fcntl(fileno(file), F_PREALLOCATE, &fst);
|
||||
}
|
||||
ftruncate(fileno(file), static_cast<off_t>(offset) + length);
|
||||
#else
|
||||
#if defined(HAVE_POSIX_FALLOCATE)
|
||||
// Version using posix_fallocate
|
||||
off_t nEndPos = (off_t)offset + length;
|
||||
if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
|
||||
#endif
|
||||
// Fallback version
|
||||
// TODO: just write one byte per block
|
||||
static const char buf[65536] = {};
|
||||
if (fseek(file, offset, SEEK_SET)) {
|
||||
return;
|
||||
}
|
||||
while (length > 0) {
|
||||
unsigned int now = 65536;
|
||||
if (length < now)
|
||||
now = length;
|
||||
fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
|
||||
length -= now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
||||
{
|
||||
WCHAR pszPath[MAX_PATH] = L"";
|
||||
|
||||
if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate))
|
||||
{
|
||||
return fs::path(pszPath);
|
||||
}
|
||||
|
||||
LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
|
||||
return fs::path("");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
std::string ShellEscape(const std::string& arg)
|
||||
{
|
||||
|
@ -1290,7 +1031,6 @@ void runCommand(const std::string& strCommand)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
void SetupEnvironment()
|
||||
{
|
||||
#ifdef HAVE_MALLOPT_ARENA_MAX
|
||||
|
|
|
@ -42,55 +42,9 @@ extern const char * const BITCOIN_SETTINGS_FILENAME;
|
|||
|
||||
void SetupEnvironment();
|
||||
bool SetupNetworking();
|
||||
|
||||
/**
|
||||
* Ensure file contents are fully committed to disk, using a platform-specific
|
||||
* feature analogous to fsync().
|
||||
*/
|
||||
bool FileCommit(FILE *file);
|
||||
|
||||
/**
|
||||
* Sync directory contents. This is required on some environments to ensure that
|
||||
* newly created files are committed to disk.
|
||||
*/
|
||||
void DirectoryCommit(const fs::path &dirname);
|
||||
|
||||
bool TruncateFile(FILE *file, unsigned int length);
|
||||
int RaiseFileDescriptorLimit(int nMinFD);
|
||||
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
|
||||
|
||||
/**
|
||||
* Rename src to dest.
|
||||
* @return true if the rename was successful.
|
||||
*/
|
||||
[[nodiscard]] bool RenameOver(fs::path src, fs::path dest);
|
||||
|
||||
bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only=false);
|
||||
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.
|
||||
*
|
||||
* @param[in] path The file path
|
||||
* @param[in] max Stop seeking beyond this limit
|
||||
* @return The file size or max
|
||||
*/
|
||||
std::streampos GetFileSize(const char* path, std::streamsize max = std::numeric_limits<std::streamsize>::max());
|
||||
|
||||
/** Release all directory locks. This is used for unit testing only, at runtime
|
||||
* the global destructor will take care of the locks.
|
||||
*/
|
||||
void ReleaseDirectoryLocks();
|
||||
|
||||
bool TryCreateDirectories(const fs::path& p);
|
||||
fs::path GetDefaultDataDir();
|
||||
// Return true if -datadir option points to a valid directory or is not specified.
|
||||
bool CheckDataDirOption(const ArgsManager& args);
|
||||
fs::path GetConfigFile(const ArgsManager& args, const fs::path& configuration_file_path);
|
||||
#ifdef WIN32
|
||||
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
|
||||
#endif
|
||||
#ifndef WIN32
|
||||
std::string ShellEscape(const std::string& arg);
|
||||
#endif
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <uint256.h>
|
||||
#include <undo.h>
|
||||
#include <util/check.h> // For NDEBUG compile time check
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/hasher.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/rbf.h>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <wallet/db.h>
|
||||
|
||||
#include <util/check.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include <crypto/common.h>
|
||||
#include <logging.h>
|
||||
#include <sync.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/translation.h>
|
||||
#include <wallet/db.h>
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <util/check.h>
|
||||
#include <util/error.h>
|
||||
#include <util/fees.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/string.h>
|
||||
|
@ -39,8 +40,8 @@
|
|||
#include <util/translation.h>
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/context.h>
|
||||
#include <wallet/fees.h>
|
||||
#include <wallet/external_signer_scriptpubkeyman.h>
|
||||
#include <wallet/fees.h>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue