-
Notifications
You must be signed in to change notification settings - Fork 38.8k
fix WIN32 boost::filesystem::path issues when using special chars for datadir path #6093
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
fix WIN32 boost::filesystem::path issues when using special chars for datadir path #6093
Conversation
|
Good catch. I already fixed this once before, people really shouldn't be touching this 'magic' code. Maybe add a comment referring to the issue. |
|
Right. This is not the right approach. I could also guess that it might be connected to https://github.com/bitcoin/bitcoin/blob/master/src/util.cpp#L672 |
|
In windows the file system locale depends on the user locale. I think it only works if boost uses the same locale as windows. But I'm not sure anymore how this was determined. I got it to work once, hence it worked in 0.10... |
|
@jonasschnelli: using the "C" locale to imbue There is more than one problem tackled by
The current situation:
In practise, the current 0.10.1 can result in a failure on Russian (and other) Windows, and earlier versions are affected by the deinitialization issue, resulting in faulty Boost test executions, as well as other failures during the shutdown, such as the crash reported in the context of rebuilding the block database. |
|
Arguably the new problem is worse, as it prevents people from running the client at all with some characters in their data directories, instead of a rare shutdown race. What about: std::locale loc("C");
// On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
// may be invalid, in which case the "C" locale is used as fallback.
try {
loc = std::locale(""); // Raises a runtime error if current locale is invalid
} catch (const std::runtime_error&) {
#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
setenv("LC_ALL", "C", 1);
#endif
}
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
boost::filesystem::path::imbue(loc);This would imbue the system locale on all platforms and should avoid the race condition, as well as the data directory problems. Needs to be tested though.. |
|
@laanwj i tried you proposed code and it does not fix the problem. Maybe it would be worth to revert #5877 and find a proper solution. What really surprises me: At least since 0.9.2 (didn't try further down), bitcoind and Bitcoin-Qt does not run with a datadir-path containing special Chinese or other Asian chars (Windows only). |
|
boost::filesystem::path::imbue() returns the "previously used locale", which should be the "right one", if called in a pristine environment, or to put it another way: if imbue() wouldn't be called at all, then this appears to be the one fs::path would use. In the following patch, a dummy locale is used to extract the internal locale, to use it explicitly, which seems to be a preferable choice, because the goal is not to modify the locale at all, but to prevent deinitialization issues, and the bad-environment-fallback also kicks in earlier. diff --git a/src/util.cpp b/src/util.cpp
index 4fea18b..68fb326 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -713,18 +713,20 @@ void RenameThread(const char* name)
void SetupEnvironment()
{
- std::locale loc("C");
// On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
// may be invalid, in which case the "C" locale is used as fallback.
#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
try {
- loc = std::locale(""); // Raises a runtime error if current locale is invalid
+ std::locale(""); // Raises a runtime error if current locale is invalid
} catch (const std::runtime_error&) {
setenv("LC_ALL", "C", 1);
}
#endif
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
+ // A dummy locale is used to extract the internal default locale, used by
+ // boost::filesystem::path, which is then used to explicitly imbue the path.
+ std::locale loc = boost::filesystem::path::imbue(std::locale::classic());
boost::filesystem::path::imbue(loc);
}I tested 0.9.3, 0.10.1, another patch and this one, and it's behavior appears to be similar to 0.9.3 (i.e. it can handle paths, which 0.10.1 can't handle). Example paths and test results:
Neither the newly patched version, 0.9.3, or 0.10.0, are able to use When choosing |
|
I think that's a bit circuitous. It works, but not for obvious reasons. The returned loc from boost is the classic locale plus custom boost facet for windows. Otherwise, you could just replace the first call with std::locale(). The below is greatly simplified and I think it does what we want. Note that std::locale::global("") does not work as intended on windows, presumably because we're statically linking libstdc++. The basic C setlocale() does though. All we're really trying to accomplish here is to teach boost how to handle locale-specific char conversions for paths, right? Afaik we don't mess with c++ locales elsewhere. The above accomplishes that, won't throw exceptions, and I believe it's portable. Tested and verified with a Chinese profile/username/locale on win7, and on Linux with a busted locale via |
|
@theuni: when building on Ubuntu 14.04, with Back on Windows 8.1 x64, when using bitcoin-qt.exe, the tricky paths, such as I was not able to use that path with any version or patch, though 0.9.3, and the build with the dummy-workaround, respond with The behavior of bitcoind.exe 0.9.3, the dummy-workaround patch, and your simple patch appears to be similar. |
|
Updated this PR with @theunis solution (fixed: added a Won't run on win7 with a username containing Chinese chars (as it was with 0.9.3, 0.10.0, probably it was always like this). Short term we need a solution to fix #6078 without reintroducing #3136. |
|
Ok, I've done more debugging here. @jonasschnelli It works fine for me with a Chinese username. Turns out there's another variable: selected system "Format" in system location settings. Looks like that's what influences the default locale at runtime rather than the system locale. Some testing using So.. makes sense. Locale is per-user. When the Format is set to Chinese, a Chinese username works. This is the case because when we use the user's locale (Format), paths for that language should just work. However. Using @dexX7's trick, Chinese username with English Format does work. This is because of boost's usage of its own codecvt, which queries the Win API for the codepage type: https://github.com/boostorg/filesystem/blob/boost-1.55.0/src/windows_file_codecvt.cpp#L39 So it looks like that's actually the way to go. |
|
@theuni: my idea was basically to get access and capture the whole following block, which uses different facets, depending on the OS and build: boost-1.55.0/src/path.cpp#L792-L873, and then use it to properly "initialize" Boost path by the main thread with the correct locale, after taking care of bad environment settings. Nevertheless, it would probably make sense to catch the exception, or handle failures related to the datadir. In your simpler solution I can trigger a runtime error, if I use the path FWIW: dexX7@c9a3784 @laanwj's words are still ringing in my ear.. :/
|
|
Can't we all just agree to go back to EBCDIC? |
|
I preferred PETSCII :) Question: at least for the 0.10 branch, can you code this so that it doesn't affect behavior on non-Windows? I'd hate to release a 'fix' then break the Linux issue again, where an invalid locale prevents the application from starting.
Phew -- that's suble. Thanks for shedding some light on this dark matter. I agree that @dexX7's swap-the-locale trick, though circuitous. seems to be the best way forward. |
src/util.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please correct instead of remove the comments. Esoteric knowledge about locales is needed to understand this on reading, heck, we will have forgotten about the rationale for this in a few months.
430e459 to
fda4da8
Compare
|
Updated to make use of @dexX7 solution (#6093 (comment)). If there are no complains, i'll open a PR for the 0.10 branch with the same solution but only touching Win32 behavior (some ifdefs). |
|
@dexX7 Thanks for sticking with this and explaining the issue well. It looks like it took me a little while to figure out exactly what you already had, I just had to work through it myself for it to make sense. Nice detective work! @laanwj Agreed on making it windows only for 0.10, and on adding comments. Additionally, we're depending on internal boost workings here that may change in the future, I wonder if it'd be worthwhile to add a quick test to use './bitcoind.exe -datadir=c:\something\non-latin'. Sadly, I don't think we could trust wine to faithfully reproduce the issue, but at least we'd have a set of tests that could be run manually. If that's possible, we'd probably want to bake it into the test_bitcoin.exe binary to rule out python/shell middle-man issues. Edit: thinking on it a little more, using -datadir for a test is overkill. We could just add a quick unit-test to add a subdir inside the current profile and touch a file in there. |
Is this the case? Travis is happy with the change applied on top of 0.10.1, and the bad-environment test passes as well. It does not bring back #3136, and at least to my understanding, it should restore the behavior of pre-0.10, but without the multithreading-deinitialization issue. But please don't give my understanding much weight here. All this is based on the assumption that path::codecvt() is called via
It's the least I can do, given that my fix for #3136 (deinitialization issue), and the follow up #5950 (bad environment handling), ultimately led in this situation.
Having more tests for this would be incredibly useful. There might be a twist though: the unit tests (via TestingSetup) appear to use Just brainstorming on this: what I tested manually might be wrapped into a Python test, which starts and stops nodes, using a bunch of datadirs with "exotic" characters. Unfortunally I'm not sure, which test data could be used for it, given that, appearingly, the system's localization plays a significant role here (e.g. Tackling this from another perspective: ideally there would also be some form of mitigation in the context of Edit: where I'm getting: there are probably other related issues in the context of the filesystem, which are not related to localization. For example, using a datadir without permission, results in a segfault. |
|
@dexX7 If the test throws during initialization, then it will fail anyway, no? I don't see how that's a problem as it still indicates an error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated comment :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
fda4da8 to
3da7849
Compare
|
I lost track of this PR. My tests still tell me that there are problems with special char datadir on windows (as it always was the case). Maybe this PR should concentrate on fixing #6078 without reintroducing #3136 (solved by #5877). |
It's the best solution I've found, though this is still very unfortunate. As far as I can see, it restores the behavior of 0.9.3, without reintroducing #3136. |
|
Cherry-picked to 0.10 as 424ae66 (the change is now subtle enough that no special path for WIN32 is necessary) |
|
post-merge ACK. |
|
@laanwj: the current 0.10 tip doesn't include the changes of this PR? Edit: now I see it. :) |
|
post-merge comments. Kudos to you guys for sticking with a win32 issue, but... Why? 'They' say that coding for *nix is too human resource hungry due to intra-distribution incompatibilities... hmmmmm, Oooookay, if 'they' say so. @sipa @laanwj EBCDIC, PETSCII You must have been rich! "When I were a lad" (olde Englysh comedy) we programmed our 4004 directly in binary using mechanical switches. Our output device was four LED's. Encoding? Who needs it? ;-) |
fixes bitcoin#6078 Github-Pull: bitcoin#6093 Rebased-From: b3ffcdf 3da7849 (cherry picked from commit 424ae66)


fixes #6078
Meant for 0.11 branch.