mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-26 21:03:28 +01:00
Merge pull request #3994
1d17647
epee.string_tools: add conversion between UTF-8 and UTF-16 (stoffu)59de6f8
util: add file_locker class (stoffu)3d623a8
wallet: prevent the same wallet file from being opened by multiple processes (stoffu)
This commit is contained in:
commit
4c6de54ee2
@ -33,6 +33,7 @@
|
|||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include "string_tools.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// On Windows there is a problem with non-ASCII characters in path and file names
|
// On Windows there is a problem with non-ASCII characters in path and file names
|
||||||
@ -72,11 +73,9 @@ namespace file_io_utils
|
|||||||
bool save_string_to_file(const std::string& path_to_file, const std::string& str)
|
bool save_string_to_file(const std::string& path_to_file, const std::string& str)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
WCHAR wide_path[1000];
|
std::wstring wide_path;
|
||||||
int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000);
|
try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
|
||||||
if (chars == 0)
|
HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
return false;
|
|
||||||
HANDLE file_handle = CreateFileW(wide_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
if (file_handle == INVALID_HANDLE_VALUE)
|
if (file_handle == INVALID_HANDLE_VALUE)
|
||||||
return false;
|
return false;
|
||||||
DWORD bytes_written;
|
DWORD bytes_written;
|
||||||
@ -131,11 +130,9 @@ namespace file_io_utils
|
|||||||
bool load_file_to_string(const std::string& path_to_file, std::string& target_str, size_t max_size = 1000000000)
|
bool load_file_to_string(const std::string& path_to_file, std::string& target_str, size_t max_size = 1000000000)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
WCHAR wide_path[1000];
|
std::wstring wide_path;
|
||||||
int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000);
|
try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
|
||||||
if (chars == 0)
|
HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
return false;
|
|
||||||
HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
if (file_handle == INVALID_HANDLE_VALUE)
|
if (file_handle == INVALID_HANDLE_VALUE)
|
||||||
return false;
|
return false;
|
||||||
DWORD file_size = GetFileSize(file_handle, NULL);
|
DWORD file_size = GetFileSize(file_handle, NULL);
|
||||||
@ -202,11 +199,9 @@ namespace file_io_utils
|
|||||||
bool get_file_size(const std::string& path_to_file, uint64_t &size)
|
bool get_file_size(const std::string& path_to_file, uint64_t &size)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
WCHAR wide_path[1000];
|
std::wstring wide_path;
|
||||||
int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000);
|
try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
|
||||||
if (chars == 0)
|
HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
return false;
|
|
||||||
HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
if (file_handle == INVALID_HANDLE_VALUE)
|
if (file_handle == INVALID_HANDLE_VALUE)
|
||||||
return false;
|
return false;
|
||||||
LARGE_INTEGER file_size;
|
LARGE_INTEGER file_size;
|
||||||
|
@ -381,6 +381,41 @@ POP_WARNINGS
|
|||||||
res = str.substr(0, pos);
|
res = str.substr(0, pos);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline std::wstring utf8_to_utf16(const std::string& str)
|
||||||
|
{
|
||||||
|
if (str.empty())
|
||||||
|
return {};
|
||||||
|
int wstr_size = MultiByteToWideChar(CP_UTF8, 0, &str[0], str.size(), NULL, 0);
|
||||||
|
if (wstr_size == 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message());
|
||||||
|
}
|
||||||
|
std::wstring wstr(wstr_size, wchar_t{});
|
||||||
|
if (!MultiByteToWideChar(CP_UTF8, 0, &str[0], str.size(), &wstr[0], wstr_size))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message());
|
||||||
|
}
|
||||||
|
return wstr;
|
||||||
|
}
|
||||||
|
inline std::string utf16_to_utf8(const std::wstring& wstr)
|
||||||
|
{
|
||||||
|
if (wstr.empty())
|
||||||
|
return {};
|
||||||
|
int str_size = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], wstr.size(), NULL, 0, NULL, NULL);
|
||||||
|
if (str_size == 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message());
|
||||||
|
}
|
||||||
|
std::string str(str_size, char{});
|
||||||
|
if (!WideCharToMultiByte(CP_UTF8, 0, &wstr[0], wstr.size(), &str[0], str_size, NULL, NULL))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message());
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif //_STRING_TOOLS_H_
|
#endif //_STRING_TOOLS_H_
|
||||||
|
@ -195,6 +195,73 @@ namespace tools
|
|||||||
catch (...) {}
|
catch (...) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_locker::file_locker(const std::string &filename)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
m_fd = INVALID_HANDLE_VALUE;
|
||||||
|
std::wstring filename_wide;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
filename_wide = string_tools::utf8_to_utf16(filename);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
MERROR("Failed to convert path \"" << filename << "\" to UTF-16: " << e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_fd = CreateFileW(filename_wide.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (m_fd != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
OVERLAPPED ov;
|
||||||
|
memset(&ov, 0, sizeof(ov));
|
||||||
|
if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov))
|
||||||
|
{
|
||||||
|
MERROR("Failed to lock " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
|
||||||
|
CloseHandle(m_fd);
|
||||||
|
m_fd = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
m_fd = open(filename, O_RDONLY | O_CREAT, 0666);
|
||||||
|
if (m_fd != -1)
|
||||||
|
{
|
||||||
|
if (flock(m_fd, LOCK_EX | LOCK_NB) == -1)
|
||||||
|
{
|
||||||
|
MERROR("Failed to lock " << filename << ": " << std::strerr(errno));
|
||||||
|
close(m_fd);
|
||||||
|
m_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MERROR("Failed to open " << filename << ": " << std::strerr(errno));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
file_locker::~file_locker()
|
||||||
|
{
|
||||||
|
if (locked())
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
CloseHandle(m_fd);
|
||||||
|
#else
|
||||||
|
close(m_fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool file_locker::locked() const
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return m_fd != INVALID_HANDLE_VALUE;
|
||||||
|
#else
|
||||||
|
return m_fd != -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
std::string get_windows_version_display_string()
|
std::string get_windows_version_display_string()
|
||||||
{
|
{
|
||||||
@ -451,10 +518,15 @@ std::string get_nix_version_display_string()
|
|||||||
|
|
||||||
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
|
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
|
||||||
{
|
{
|
||||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, psz_path, wcslen(psz_path), NULL, 0, NULL, NULL);
|
try
|
||||||
std::string folder_name(size_needed, 0);
|
{
|
||||||
WideCharToMultiByte(CP_UTF8, 0, psz_path, wcslen(psz_path), &folder_name[0], size_needed, NULL, NULL);
|
return string_tools::utf16_to_utf8(psz_path);
|
||||||
return folder_name;
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
MERROR("utf16_to_utf8 failed: " << e.what());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
|
LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
|
||||||
@ -515,18 +587,20 @@ std::string get_nix_version_display_string()
|
|||||||
int code;
|
int code;
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
// Maximizing chances for success
|
// Maximizing chances for success
|
||||||
WCHAR wide_replacement_name[1000];
|
std::wstring wide_replacement_name;
|
||||||
MultiByteToWideChar(CP_UTF8, 0, replacement_name.c_str(), replacement_name.size() + 1, wide_replacement_name, 1000);
|
try { wide_replacement_name = string_tools::utf8_to_utf16(replacement_name); }
|
||||||
WCHAR wide_replaced_name[1000];
|
catch (...) { return std::error_code(GetLastError(), std::system_category()); }
|
||||||
MultiByteToWideChar(CP_UTF8, 0, replaced_name.c_str(), replaced_name.size() + 1, wide_replaced_name, 1000);
|
std::wstring wide_replaced_name;
|
||||||
|
try { wide_replaced_name = string_tools::utf8_to_utf16(replaced_name); }
|
||||||
|
catch (...) { return std::error_code(GetLastError(), std::system_category()); }
|
||||||
|
|
||||||
DWORD attributes = ::GetFileAttributesW(wide_replaced_name);
|
DWORD attributes = ::GetFileAttributesW(wide_replaced_name.c_str());
|
||||||
if (INVALID_FILE_ATTRIBUTES != attributes)
|
if (INVALID_FILE_ATTRIBUTES != attributes)
|
||||||
{
|
{
|
||||||
::SetFileAttributesW(wide_replaced_name, attributes & (~FILE_ATTRIBUTE_READONLY));
|
::SetFileAttributesW(wide_replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = 0 != ::MoveFileExW(wide_replacement_name, wide_replaced_name, MOVEFILE_REPLACE_EXISTING);
|
bool ok = 0 != ::MoveFileExW(wide_replacement_name.c_str(), wide_replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING);
|
||||||
code = ok ? 0 : static_cast<int>(::GetLastError());
|
code = ok ? 0 : static_cast<int>(::GetLastError());
|
||||||
#else
|
#else
|
||||||
bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str());
|
bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str());
|
||||||
|
@ -91,6 +91,20 @@ namespace tools
|
|||||||
const std::string& filename() const noexcept { return m_filename; }
|
const std::string& filename() const noexcept { return m_filename; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class file_locker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
file_locker(const std::string &filename);
|
||||||
|
~file_locker();
|
||||||
|
bool locked() const;
|
||||||
|
private:
|
||||||
|
#ifdef WIN32
|
||||||
|
HANDLE m_fd;
|
||||||
|
#else
|
||||||
|
int m_fd;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
/*! \brief Returns the default data directory.
|
/*! \brief Returns the default data directory.
|
||||||
*
|
*
|
||||||
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
||||||
|
@ -721,6 +721,7 @@ bool WalletImpl::close(bool store)
|
|||||||
LOG_PRINT_L1("Calling wallet::stop...");
|
LOG_PRINT_L1("Calling wallet::stop...");
|
||||||
m_wallet->stop();
|
m_wallet->stop();
|
||||||
LOG_PRINT_L1("wallet::stop done");
|
LOG_PRINT_L1("wallet::stop done");
|
||||||
|
m_wallet->deinit();
|
||||||
result = true;
|
result = true;
|
||||||
clearStatus();
|
clearStatus();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
|
@ -2636,6 +2636,7 @@ void wallet2::detach_blockchain(uint64_t height)
|
|||||||
bool wallet2::deinit()
|
bool wallet2::deinit()
|
||||||
{
|
{
|
||||||
m_is_initialized=false;
|
m_is_initialized=false;
|
||||||
|
m_keys_file_locker.reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
@ -2802,10 +2803,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||||||
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
|
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
|
||||||
keys_file_data.account_data = cipher;
|
keys_file_data.account_data = cipher;
|
||||||
|
|
||||||
|
m_keys_file_locker.reset();
|
||||||
std::string buf;
|
std::string buf;
|
||||||
r = ::serialization::dump_binary(keys_file_data, buf);
|
r = ::serialization::dump_binary(keys_file_data, buf);
|
||||||
r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read
|
r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read
|
||||||
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name);
|
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name);
|
||||||
|
m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3935,12 +3938,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||||||
boost::system::error_code e;
|
boost::system::error_code e;
|
||||||
bool exists = boost::filesystem::exists(m_keys_file, e);
|
bool exists = boost::filesystem::exists(m_keys_file, e);
|
||||||
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
|
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
|
||||||
|
m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!m_keys_file_locker->locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
|
||||||
|
|
||||||
|
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
|
||||||
|
m_keys_file_locker.reset();
|
||||||
if (!load_keys(m_keys_file, password))
|
if (!load_keys(m_keys_file, password))
|
||||||
{
|
{
|
||||||
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
|
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
|
||||||
}
|
}
|
||||||
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
|
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
|
||||||
|
m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
|
||||||
|
|
||||||
//keys loaded ok!
|
//keys loaded ok!
|
||||||
//try to load wallet file. but even if we failed, it is not big problem
|
//try to load wallet file. but even if we failed, it is not big problem
|
||||||
|
@ -1308,6 +1308,7 @@ namespace tools
|
|||||||
boost::optional<crypto::chacha_key> m_ringdb_key;
|
boost::optional<crypto::chacha_key> m_ringdb_key;
|
||||||
|
|
||||||
uint64_t m_last_block_reward;
|
uint64_t m_last_block_reward;
|
||||||
|
std::unique_ptr<tools::file_locker> m_keys_file_locker;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BOOST_CLASS_VERSION(tools::wallet2, 25)
|
BOOST_CLASS_VERSION(tools::wallet2, 25)
|
||||||
|
Loading…
Reference in New Issue
Block a user