Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error if relative -walletdir is specified #12220

Merged
merged 1 commit into from
Jan 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions doc/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@ bitcoin data directory. The behavior is now:
already exists in the data directory root, then wallets will be stored in the
`wallets/` subdirectory by default.
- The location of the wallets directory can be overridden by specifying a
`-walletdir=<path>` option where `<path>` can be an absolute path or a
relative path (relative to the current working directory). `<path>` can
also be a path to symlink to a directory.
`-walletdir=<path>` option where `<path>` can be an absolute path to a
directory or directory symlink.

Care should be taken when choosing the wallets directory location, as if it
becomes unavailable during operation, funds may be lost.
Expand Down
9 changes: 9 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,15 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);

// Warn about relative -datadir path.
if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the "
"current working directory '%s'. This is fragile, because if bitcoin is started in the future "
"from a different location, it will be unable to locate the current data files. There could "
"also be data loss if bitcoin is started while in a temporary directory.\n",
gArgs.GetArg("-datadir", ""), fs::current_path().string());
}

InitSignatureCache();
InitScriptExecutionCache();

Expand Down
12 changes: 8 additions & 4 deletions src/wallet/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,15 @@ bool VerifyWallets()
return true;
}

if (gArgs.IsArgSet("-walletdir") && !fs::is_directory(GetWalletDir())) {
if (fs::exists(fs::system_complete(gArgs.GetArg("-walletdir", "")))) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), gArgs.GetArg("-walletdir", "").c_str()));
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
if (!fs::exists(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
} else if (!fs::is_directory(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
} else if (!wallet_dir.is_absolute()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes slightly more sense to me to have this as the first check, ie we should error out immediately if the user has provided a relative path, rather than first checking whether it exists and is a directory. Is there any particular reason that you've placed this as the last check?

return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
}
return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), gArgs.GetArg("-walletdir", "").c_str()));
}

LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Could remove call to fs::system_complete in GetWalletDir?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Could remove call to fs::system_complete in GetWalletDir?

I'm not sure it was necessary to begin with, but now removed in 46c40bf.

Expand Down
2 changes: 1 addition & 1 deletion src/wallet/walletutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fs::path GetWalletDir()
fs::path path;

if (gArgs.IsArgSet("-walletdir")) {
path = fs::system_complete(gArgs.GetArg("-walletdir", ""));
path = gArgs.GetArg("-walletdir", "");
if (!fs::is_directory(path)) {
// If the path specified doesn't exist, we return the deliberately
// invalid empty string.
Expand Down
4 changes: 4 additions & 0 deletions test/functional/multiwallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def run_test(self):

self.stop_nodes()

self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir())
self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir())

# should not initialize if there are duplicate wallets
self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.')

Expand Down
12 changes: 6 additions & 6 deletions test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,26 +220,26 @@ def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, bin
for i in range(num_nodes):
self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, use_cli=self.options.usecli))

def start_node(self, i, extra_args=None, stderr=None):
def start_node(self, i, *args, **kwargs):
"""Start a bitcoind"""

node = self.nodes[i]

node.start(extra_args, stderr)
node.start(*args, **kwargs)
node.wait_for_rpc_connection()

if self.options.coveragedir is not None:
coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc)

def start_nodes(self, extra_args=None):
def start_nodes(self, extra_args=None, *args, **kwargs):
"""Start multiple bitcoinds"""

if extra_args is None:
extra_args = [None] * self.num_nodes
assert_equal(len(extra_args), self.num_nodes)
try:
for i, node in enumerate(self.nodes):
node.start(extra_args[i])
node.start(extra_args[i], *args, **kwargs)
for node in self.nodes:
node.wait_for_rpc_connection()
except:
Expand Down Expand Up @@ -271,10 +271,10 @@ def restart_node(self, i, extra_args=None):
self.stop_node(i)
self.start_node(i, extra_args)

def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None):
def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None, *args, **kwargs):
with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
try:
self.start_node(i, extra_args, stderr=log_stderr)
self.start_node(i, extra_args, stderr=log_stderr, *args, **kwargs)
self.stop_node(i)
except Exception as e:
assert 'bitcoind exited' in str(e) # node must have shutdown
Expand Down
4 changes: 2 additions & 2 deletions test/functional/test_framework/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ def __getattr__(self, name):
assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
return getattr(self.rpc, name)

def start(self, extra_args=None, stderr=None):
def start(self, extra_args=None, stderr=None, *args, **kwargs):
"""Start the node."""
if extra_args is None:
extra_args = self.extra_args
if stderr is None:
stderr = self.stderr
self.process = subprocess.Popen(self.args + extra_args, stderr=stderr)
self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs)
self.running = True
self.log.debug("bitcoind started, waiting for RPC to come up")

Expand Down