Chromium Code Reviews| Index: chrome/install_static/install_util.cc |
| diff --git a/chrome/install_static/install_util.cc b/chrome/install_static/install_util.cc |
| index fc7f95fd5a00f97bd023a78bf474340bac53b538..755d0707a90cfe85f024be38a528413ac10b7d8e 100644 |
| --- a/chrome/install_static/install_util.cc |
| +++ b/chrome/install_static/install_util.cc |
| @@ -12,6 +12,7 @@ |
| #include <memory> |
| #include <sstream> |
| +#include "chrome/install_static/policy_path_parser.h" |
| #include "chrome_elf/nt_registry/nt_registry.h" |
| namespace install_static { |
| @@ -61,12 +62,19 @@ const wchar_t kShowRestart[] = L"CHROME_CRASHED"; |
| const wchar_t kRestartInfo[] = L"CHROME_RESTART"; |
| const wchar_t kRtlLocale[] = L"RIGHT_TO_LEFT"; |
| -const char kGpuProcess[] = "gpu-process"; |
| -const char kPpapiPluginProcess[] = "ppapi"; |
| -const char kRendererProcess[] = "renderer"; |
| -const char kUtilityProcess[] = "utility"; |
| -const char kProcessType[] = "type"; |
| -const char kCrashpadHandler[] = "crashpad-handler"; |
| +const wchar_t kCrashpadHandler[] = L"crashpad-handler"; |
| +const wchar_t kProcessType[] = L"type"; |
| +const wchar_t kUserDataDirSwitch[] = L"user-data-dir"; |
| +const wchar_t kUtilityProcess[] = L"utility"; |
| + |
| +#if defined(GOOGLE_CHROME_BUILD) |
| +const wchar_t kRegistryChromePolicyKey[] = |
| + L"SOFTWARE\\Policies\\Google\\Chrome"; |
| +#else |
| +const wchar_t kRegistryChromePolicyKey[] = |
| + L"SOFTWARE\\Policies\\Chromium"; |
| +#endif |
| +const wchar_t kUserDataDirRegKey[] = L"UserDataDir"; |
| namespace { |
| @@ -186,6 +194,20 @@ std::wstring GetCurrentProcessExePath() { |
| return exe_path; |
| } |
| +std::wstring MakeAbsoluteFilePath(const std::wstring& input) { |
| + wchar_t file_path[MAX_PATH]; |
| + if (!_wfullpath(file_path, input.c_str(), MAX_PATH)) |
| + return std::wstring(); |
| + return file_path; |
| +} |
| + |
| +bool DirectoryExists(const std::wstring& path) { |
| + DWORD file_attributes = ::GetFileAttributes(path.c_str()); |
| + if (file_attributes != INVALID_FILE_ATTRIBUTES) |
| + return (file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; |
| + return false; |
| +} |
| + |
| bool RecursiveDirectoryCreate(const std::wstring& full_path) { |
| // If the path exists, we've succeeded if it's a directory, failed otherwise. |
| const wchar_t* full_path_str = full_path.c_str(); |
| @@ -216,20 +238,16 @@ bool RecursiveDirectoryCreate(const std::wstring& full_path) { |
| } |
| if (!::CreateDirectory(full_path_str, nullptr)) { |
| DWORD error_code = ::GetLastError(); |
| - if (error_code == ERROR_ALREADY_EXISTS) { |
| - DWORD file_attributes = ::GetFileAttributes(full_path_str); |
| - if ((file_attributes != INVALID_FILE_ATTRIBUTES) && |
|
scottmg
2016/11/15 23:23:15
The "else" was at the wrong level (no error was re
|
| - ((file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { |
| - // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we |
| - // were racing with someone creating the same directory, or a file |
| - // with the same path. If the directory exists, we lost the |
| - // race to create the same directory. |
| - return true; |
| - } else { |
| - Trace(L"Failed to create directory %ls, last error is %d\n", |
| - full_path_str, error_code); |
| - return false; |
| - } |
| + if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path_str)) { |
| + // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we |
| + // were racing with someone creating the same directory, or a file |
| + // with the same path. If the directory exists, we lost the |
| + // race to create the same directory. |
| + return true; |
| + } else { |
| + Trace(L"Failed to create directory %ls, last error is %d\n", |
| + full_path_str, error_code); |
| + return false; |
| } |
| } |
| return true; |
| @@ -376,6 +394,33 @@ std::vector<StringType> TokenizeStringT( |
| return tokens; |
| } |
| +// Checks if the key exists in the given hive and expands any string variables. |
| +bool LoadUserDataDirPolicyFromRegistry(nt::ROOT_KEY hive, |
| + const wchar_t* key_name, |
| + std::wstring* dir) { |
| + std::wstring value; |
| + bool result = false; |
| + HANDLE key; |
| + if (nt::OpenRegKey(hive, kRegistryChromePolicyKey, KEY_READ, &key, nullptr)) { |
| + if (nt::QueryRegValueSZ(key, key_name, &value)) { |
| + *dir = install_static::ExpandPathVariables(value); |
| + result = true; |
| + } |
| + nt::CloseRegKey(key); |
| + } |
| + return result; |
| +} |
| + |
| +void CheckUserDataDirPolicy(std::wstring* user_data_dir) { |
| + assert(user_data_dir); |
| + // Policy from the HKLM hive has precedence over HKCU. |
| + if (!LoadUserDataDirPolicyFromRegistry(nt::HKLM, kUserDataDirRegKey, |
| + user_data_dir)) { |
| + LoadUserDataDirPolicyFromRegistry(nt::HKCU, kUserDataDirRegKey, |
| + user_data_dir); |
| + } |
| +} |
| + |
| } // namespace |
| bool IsSxSChrome(const wchar_t* exe_path) { |
| @@ -531,12 +576,55 @@ bool GetDefaultUserDataDirectory(std::wstring* result) { |
| return true; |
| } |
| -bool GetDefaultCrashDumpLocation(std::wstring* crash_dir) { |
| - // In order to be able to start crash handling very early, we do not rely on |
| - // chrome's PathService entries (for DIR_CRASH_DUMPS) being available on |
| - // Windows. See https://crbug.com/564398. |
| - if (!GetDefaultUserDataDirectory(crash_dir)) |
| +bool GetUserDataDirectory(const std::wstring& user_data_dir_from_command_line, |
| + std::wstring* result) { |
| + // WARNING! This has to mirror the logic in chrome/app/chrome_main_delegate.cc |
| + // in InitializeUserDataDir(). This function derives the path in the same way, |
| + // but without using base, nor reporting on failure as the Chrome |
| + // implementation does. |
| + std::wstring user_data_dir = user_data_dir_from_command_line; |
| + |
| + CheckUserDataDirPolicy(&user_data_dir); |
| + |
| + // On Windows, trailing separators leave Chrome in a bad state. See |
| + // crbug.com/464616. |
| + while (!user_data_dir.empty() && |
| + (user_data_dir.back() == '\\' || user_data_dir.back() == '/')) { |
| + user_data_dir.pop_back(); |
| + } |
| + |
| + bool have_valid_directory = |
| + !user_data_dir.empty() && RecursiveDirectoryCreate(user_data_dir); |
| + |
| + if (!have_valid_directory) { |
| + if (!GetDefaultUserDataDirectory(&user_data_dir)) { |
| + // The Chrome implementation CHECKs() here in the browser process. We |
| + // don't as this function is used to initialize crash reporting, so |
| + // we would get no report of this failure. |
| + assert(false); |
| + return false; |
| + } |
| + } |
| + |
| + *result = MakeAbsoluteFilePath(user_data_dir); |
| + return true; |
| +} |
| + |
| +bool GetUserDataDirectoryUsingProcessCommandLine(std::wstring* result) { |
| + return GetUserDataDirectory( |
| + GetSwitchValueFromCommandLine(::GetCommandLine(), |
| + install_static::kUserDataDirSwitch), |
| + result); |
| +} |
| + |
| +bool GetCrashDumpLocation(std::wstring* crash_dir) { |
| + // In order to be able to start crash handling very early and in chrome_elf, |
| + // we cannot rely on chrome's PathService entries (for DIR_CRASH_DUMPS) |
| + // being |
| + // available on Windows. See https://crbug.com/564398. |
| + if (!GetUserDataDirectoryUsingProcessCommandLine(crash_dir)) { |
| return false; |
| + } |
| // We have to make sure the user data dir exists on first run. See |
| // http://crbug.com/591504. |
| @@ -844,45 +932,45 @@ bool CompareVersionStrings(const std::string& version1, |
| return true; |
| } |
| -std::string GetSwitchValueFromCommandLine(const std::string& command_line, |
| - const std::string& switch_name) { |
| +std::wstring GetSwitchValueFromCommandLine(const std::wstring& command_line, |
| + const std::wstring& switch_name) { |
| assert(!command_line.empty()); |
| assert(!switch_name.empty()); |
| - std::string command_line_copy = command_line; |
| + std::wstring command_line_copy = command_line; |
| // Remove leading and trailing spaces. |
| - TrimT<std::string>(&command_line_copy); |
| + TrimT<std::wstring>(&command_line_copy); |
| // Find the switch in the command line. If we don't find the switch, return |
| // an empty string. |
| - std::string switch_token = "--"; |
| + std::wstring switch_token = L"--"; |
| switch_token += switch_name; |
| - switch_token += "="; |
| + switch_token += L"="; |
| size_t switch_offset = command_line_copy.find(switch_token); |
| if (switch_offset == std::string::npos) |
| - return std::string(); |
| + return std::wstring(); |
| // The format is "--<switch name>=blah". Look for a space after the |
| // "--<switch name>=" string. If we don't find a space assume that the switch |
| // value ends at the end of the command line. |
| size_t switch_value_start_offset = switch_offset + switch_token.length(); |
| - if (std::string(kWhiteSpaces).find( |
| - command_line_copy[switch_value_start_offset]) != std::string::npos) { |
| + if (std::wstring(kWhiteSpaces16).find( |
| + command_line_copy[switch_value_start_offset]) != std::wstring::npos) { |
| switch_value_start_offset = command_line_copy.find_first_not_of( |
| - GetWhiteSpacesForType<std::string>(), switch_value_start_offset); |
| - if (switch_value_start_offset == std::string::npos) |
| - return std::string(); |
| + GetWhiteSpacesForType<std::wstring>(), switch_value_start_offset); |
| + if (switch_value_start_offset == std::wstring::npos) |
| + return std::wstring(); |
| } |
| size_t switch_value_end_offset = |
| - command_line_copy.find_first_of(GetWhiteSpacesForType<std::string>(), |
| + command_line_copy.find_first_of(GetWhiteSpacesForType<std::wstring>(), |
| switch_value_start_offset); |
| - if (switch_value_end_offset == std::string::npos) |
| + if (switch_value_end_offset == std::wstring::npos) |
| switch_value_end_offset = command_line_copy.length(); |
| - std::string switch_value = command_line_copy.substr( |
| + std::wstring switch_value = command_line_copy.substr( |
| switch_value_start_offset, |
| switch_value_end_offset - (switch_offset + switch_token.length())); |
| - TrimT<std::string>(&switch_value); |
| + TrimT<std::wstring>(&switch_value); |
| return switch_value; |
| } |