mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 11:16:39 +00:00
fix(elevator): make use of wide characters everywhere (#1559)
Switching the Windows language to a language encoded using UTF-16 (like Japanese) revealed that our native elevator module garbles language specific strings, making the child writer process unable to find an image inside a language specific directory, and thus resulting in a "File is not accessible" error. As a solution, we make use of `std::wstring` and wide character Windows API functions in the elevator module. Change-Type: patch Changelog-Entry: Fix "file is not accessible" error when flashing an image that lives inside a directory whose name is UTF-16 encoded on Windows. Fixes: https://github.com/resin-io/etcher/issues/1459 Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
This commit is contained in:
parent
80b588683e
commit
fd109b5770
@ -29,13 +29,13 @@ NAN_METHOD(Elevate) {
|
||||
return Nan::ThrowError("Callback must be a function");
|
||||
}
|
||||
|
||||
std::vector<std::string> arguments =
|
||||
std::vector<std::wstring> arguments =
|
||||
etcher::v8utils::GetArguments(info[0].As<v8::Array>());
|
||||
v8::Local<v8::Function> callback = info[1].As<v8::Function>();
|
||||
|
||||
etcher::ELEVATE_RESULT result = etcher::Elevate(
|
||||
arguments.front(),
|
||||
std::vector<std::string>(arguments.begin() + 1, arguments.end()));
|
||||
std::vector<std::wstring>(arguments.begin() + 1, arguments.end()));
|
||||
|
||||
// Create results object
|
||||
v8::Isolate *isolate = v8::Isolate::GetCurrent();
|
||||
@ -51,8 +51,7 @@ NAN_METHOD(Elevate) {
|
||||
YIELD_OBJECT(callback, results);
|
||||
break;
|
||||
default:
|
||||
std::string details = etcher::ElevateResultToString(result);
|
||||
YIELD_ERROR(callback, details.c_str());
|
||||
YIELD_ERROR(callback, etcher::ElevateResultToString(result));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,10 +53,10 @@ enum class ELEVATE_RESULT {
|
||||
ELEVATE_UNKNOWN_ERROR
|
||||
};
|
||||
|
||||
ELEVATE_RESULT Elevate(const std::string &command,
|
||||
std::vector<std::string> arguments);
|
||||
ELEVATE_RESULT Elevate(const std::wstring &command,
|
||||
std::vector<std::wstring> arguments);
|
||||
|
||||
std::string ElevateResultToString(const ELEVATE_RESULT &result);
|
||||
std::wstring ElevateResultToString(const ELEVATE_RESULT &result);
|
||||
|
||||
} // namespace etcher
|
||||
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
#include "os/elevate.h"
|
||||
|
||||
static std::string JoinArguments(std::vector<std::string> arguments) {
|
||||
std::ostringstream result;
|
||||
static std::wstring JoinArguments(std::vector<std::wstring> arguments) {
|
||||
std::wostringstream result;
|
||||
|
||||
std::copy(arguments.begin(), arguments.end(),
|
||||
std::ostream_iterator<std::string>(result, " "));
|
||||
std::ostream_iterator<std::wstring, wchar_t>(result, L" "));
|
||||
|
||||
return result.str();
|
||||
}
|
||||
@ -28,22 +28,22 @@ static std::string JoinArguments(std::vector<std::string> arguments) {
|
||||
// Make sure to delete the result after you're done
|
||||
// with it by calling `delete[] result;`.
|
||||
// See http://stackoverflow.com/a/1201471
|
||||
static LPCTSTR ConvertStringToLPCTSTR(const std::string &string) {
|
||||
char *result = new char[string.size() + 1];
|
||||
static LPCWSTR ConvertStringToLPCWSTR(const std::wstring &string) {
|
||||
wchar_t *result = new wchar_t[string.size() + 1];
|
||||
std::copy(string.begin(), string.end(), result);
|
||||
result[string.size()] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
etcher::ELEVATE_RESULT etcher::Elevate(const std::string &command,
|
||||
std::vector<std::string> arguments) {
|
||||
etcher::ELEVATE_RESULT etcher::Elevate(const std::wstring &command,
|
||||
std::vector<std::wstring> arguments) {
|
||||
// Initialize the SHELLEXECUTEINFO structure. We zero it out
|
||||
// in order to be on the safe side, and set cbSize to the size
|
||||
// of the structure as recommend by MSDN
|
||||
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb759784(v=vs.85).aspx
|
||||
SHELLEXECUTEINFO shellExecuteInfo;
|
||||
SHELLEXECUTEINFOW shellExecuteInfo;
|
||||
ZeroMemory(&shellExecuteInfo, sizeof(shellExecuteInfo));
|
||||
shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||
shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||
|
||||
// Flags that indicate the content and validity of the other structure member.
|
||||
shellExecuteInfo.fMask =
|
||||
@ -60,7 +60,7 @@ etcher::ELEVATE_RESULT etcher::Elevate(const std::string &command,
|
||||
SEE_MASK_FLAG_NO_UI;
|
||||
|
||||
// The action to be performed.
|
||||
shellExecuteInfo.lpVerb = TEXT("runas");
|
||||
shellExecuteInfo.lpVerb = L"runas";
|
||||
|
||||
// Run the file in the background
|
||||
shellExecuteInfo.nShow = SW_HIDE;
|
||||
@ -70,14 +70,14 @@ etcher::ELEVATE_RESULT etcher::Elevate(const std::string &command,
|
||||
|
||||
// Set file and parameters
|
||||
// We can't just assign the result of `.c_str()`, since
|
||||
// that pointer is owned by the `std::string` instance,
|
||||
// that pointer is owned by the `std::wstring` instance,
|
||||
// and will not be safe after the instance is destroyed.
|
||||
LPCTSTR file = ConvertStringToLPCTSTR(command);
|
||||
LPCTSTR argv = ConvertStringToLPCTSTR(JoinArguments(arguments));
|
||||
LPCWSTR file = ConvertStringToLPCWSTR(command);
|
||||
LPCWSTR argv = ConvertStringToLPCWSTR(JoinArguments(arguments));
|
||||
shellExecuteInfo.lpFile = file;
|
||||
shellExecuteInfo.lpParameters = argv;
|
||||
|
||||
BOOL executeResult = ShellExecuteEx(&shellExecuteInfo);
|
||||
BOOL executeResult = ShellExecuteExW(&shellExecuteInfo);
|
||||
|
||||
delete[] file;
|
||||
delete[] argv;
|
||||
@ -127,32 +127,32 @@ etcher::ELEVATE_RESULT etcher::Elevate(const std::string &command,
|
||||
return etcher::ELEVATE_RESULT::ELEVATE_SUCCESS;
|
||||
}
|
||||
|
||||
std::string
|
||||
std::wstring
|
||||
etcher::ElevateResultToString(const etcher::ELEVATE_RESULT &result) {
|
||||
switch (result) {
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_SUCCESS:
|
||||
return "Success";
|
||||
return L"Success";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_CANCELLED:
|
||||
return "The user cancelled the elevation request";
|
||||
return L"The user cancelled the elevation request";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_FILE_NOT_FOUND:
|
||||
return "The specified file was not found";
|
||||
return L"The specified file was not found";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_PATH_NOT_FOUND:
|
||||
return "The specified path was not found";
|
||||
return L"The specified path was not found";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_DDE_FAIL:
|
||||
return "The Dynamic Data Exchange (DDE) transaction failed";
|
||||
return L"The Dynamic Data Exchange (DDE) transaction failed";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_NO_ASSOCIATION:
|
||||
return "There is no application associated with the "
|
||||
"specified file name extension";
|
||||
return L"There is no application associated with the "
|
||||
"specified file name extension";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_ACCESS_DENIED:
|
||||
return "Access to the specified file is denied";
|
||||
return L"Access to the specified file is denied";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_DLL_NOT_FOUND:
|
||||
return "One of the library files necessary to run the "
|
||||
"application can't be found";
|
||||
return L"One of the library files necessary to run the "
|
||||
"application can't be found";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_NOT_ENOUGH_MEMORY:
|
||||
return "There is not enough memory to perform the specified action";
|
||||
return L"There is not enough memory to perform the specified action";
|
||||
case etcher::ELEVATE_RESULT::ELEVATE_SHARING_VIOLATION:
|
||||
return "A sharing violation occurred";
|
||||
return L"A sharing violation occurred";
|
||||
default:
|
||||
return "Unknown error";
|
||||
return L"Unknown error";
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,17 @@
|
||||
|
||||
#include "utils/v8utils.h"
|
||||
|
||||
std::vector<std::string>
|
||||
std::vector<std::wstring>
|
||||
etcher::v8utils::GetArguments(v8::Local<v8::Array> arguments) {
|
||||
std::vector<std::string> result(0);
|
||||
std::vector<std::wstring> result(0);
|
||||
|
||||
for (uint32_t index = 0; index < arguments->Length(); index++) {
|
||||
std::string argument(
|
||||
// See https://stackoverflow.com/q/15615136/1641422
|
||||
std::string stringArgument(
|
||||
*v8::String::Utf8Value(arguments->Get(index)->ToString()));
|
||||
result.push_back(argument);
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> conversion;
|
||||
|
||||
result.push_back(conversion.from_bytes(stringArgument));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -20,18 +20,23 @@
|
||||
#include <nan.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <codecvt>
|
||||
|
||||
namespace etcher {
|
||||
namespace v8utils {
|
||||
std::vector<std::string> GetArguments(v8::Local<v8::Array> arguments);
|
||||
std::vector<std::wstring> GetArguments(v8::Local<v8::Array> arguments);
|
||||
} // namespace v8utils
|
||||
} // namespace etcher
|
||||
|
||||
#define YIELD_ERROR(CALLBACK, ERROR) \
|
||||
{ \
|
||||
v8::Local<v8::Value> argv[1] = {Nan::Error((ERROR))}; \
|
||||
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), (CALLBACK), 1, \
|
||||
argv); \
|
||||
const wchar_t *message = (ERROR).c_str(); \
|
||||
v8::Local<v8::Value> argv[1] = { \
|
||||
Nan::Error(v8::String::NewFromTwoByte(isolate, \
|
||||
(const uint16_t *)message)) \
|
||||
}; \
|
||||
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), (CALLBACK), \
|
||||
1, argv); \
|
||||
} \
|
||||
return;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user