diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-02 19:53:46 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-02 19:53:46 +0000 |
commit | 4d0f36e5227612dacc8e3d8b5c8e3ff44d0a5869 (patch) | |
tree | 9111811103f24e9c98623944cbf6936afc6e88ea | |
parent | f726b1a863b6558b1c5f0886420fe9a2d1ac570c (diff) | |
download | chromium_src-4d0f36e5227612dacc8e3d8b5c8e3ff44d0a5869.zip chromium_src-4d0f36e5227612dacc8e3d8b5c8e3ff44d0a5869.tar.gz chromium_src-4d0f36e5227612dacc8e3d8b5c8e3ff44d0a5869.tar.bz2 |
Reverting 27876.
Review URL: http://codereview.chromium.org/256043
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27880 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/installer/installer.gyp | 3 | ||||
-rw-r--r-- | chrome/installer/setup/install.cc | 12 | ||||
-rw-r--r-- | chrome/installer/setup/setup_main.cc | 46 | ||||
-rw-r--r-- | chrome/installer/setup/uninstall.cc | 62 | ||||
-rw-r--r-- | chrome/installer/util/delete_after_reboot_helper.cc | 375 | ||||
-rw-r--r-- | chrome/installer/util/delete_after_reboot_helper.h | 66 | ||||
-rw-r--r-- | chrome/installer/util/delete_after_reboot_helper_unittest.cc | 184 | ||||
-rw-r--r-- | chrome/installer/util/util_constants.h | 3 |
8 files changed, 17 insertions, 734 deletions
diff --git a/chrome/installer/installer.gyp b/chrome/installer/installer.gyp index df05ed1..b49f877 100644 --- a/chrome/installer/installer.gyp +++ b/chrome/installer/installer.gyp @@ -41,8 +41,6 @@ 'util/create_dir_work_item.h', 'util/create_reg_key_work_item.cc', 'util/create_reg_key_work_item.h', - 'util/delete_after_reboot_helper.cc', - 'util/delete_after_reboot_helper.h', 'util/delete_reg_value_work_item.cc', 'util/delete_reg_value_work_item.h', 'util/delete_tree_work_item.cc', @@ -152,7 +150,6 @@ 'util/copy_tree_work_item_unittest.cc', 'util/create_dir_work_item_unittest.cc', 'util/create_reg_key_work_item_unittest.cc', - 'util/delete_after_reboot_helper_unittest.cc', 'util/delete_reg_value_work_item_unittest.cc', 'util/delete_tree_work_item_unittest.cc', 'util/google_chrome_distribution_unittest.cc', diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc index 1bc8164..24d2f80 100644 --- a/chrome/installer/setup/install.cc +++ b/chrome/installer/setup/install.cc @@ -15,7 +15,6 @@ #include "chrome/installer/setup/setup_constants.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/create_reg_key_work_item.h" -#include "chrome/installer/util/delete_after_reboot_helper.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" @@ -96,7 +95,7 @@ void AddUninstallShortcutWorkItems(HKEY reg_root, uninstall_cmd.append(L"\" --"); uninstall_cmd.append(installer_util::switches::kUninstall); -#if defined(CHROME_FRAME_BUILD) +#ifdef CHROME_FRAME_BUILD uninstall_cmd.append(L" --"); uninstall_cmd.append(installer_util::switches::kForceUninstall); uninstall_cmd.append(L" --"); @@ -247,7 +246,7 @@ bool CreateOrUpdateChromeShortcuts(const std::wstring& exe_path, std::wstring arguments(L" --"); arguments.append(installer_util::switches::kUninstall); -#if defined(CHROME_FRAME_BUILD) +#ifdef CHROME_FRAME_BUILD arguments.append(L" --"); arguments.append(installer_util::switches::kForceUninstall); arguments.append(L" --"); @@ -460,13 +459,6 @@ bool InstallNewVersion(const std::wstring& exe_path, if (reg_root != HKEY_LOCAL_MACHINE && reg_root != HKEY_CURRENT_USER) return false; -#if defined(CHROME_FRAME_BUILD) - // Make sure that we don't end up deleting installed files on next reboot. - if (!RemoveFromMovesPendingReboot(install_path.c_str())) { - LOG(ERROR) << "Error accessing pending moves value."; - } -#endif - scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); // A temp directory that work items need and the actual install directory. install_list->AddCreateDirWorkItem(FilePath::FromWStringHack(temp_dir)); diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 3b5377d..8b70a1b 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -5,7 +5,6 @@ #include <string> #include <windows.h> #include <msi.h> -#include <shellapi.h> #include <shlobj.h> #include "base/at_exit.h" @@ -14,7 +13,6 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/registry.h" -#include "base/scoped_handle_win.h" #include "base/string_util.h" #include "base/win_util.h" #include "chrome/installer/setup/install.h" @@ -415,7 +413,7 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, exit_code = ShowEULADialog(inner_frame); if (installer_util::EULA_REJECTED != exit_code) GoogleUpdateSettings::SetEULAConsent(true); - return true; + return true;; } else if (cmd_line.HasSwitch( installer_util::switches::kRegisterChromeBrowser)) { // If --register-chrome-browser option is specified, register all @@ -466,40 +464,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, return false; } -bool ShowRebootDialog() { - // Get a token for this process. - HANDLE token; - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &token)) { - LOG(ERROR) << "Failed to open token."; - return false; - } - - // Use a ScopedHandle to keep track of and eventually close our handle. - // TODO(robertshield): Add a Receive() method to base's ScopedHandle. - ScopedHandle scoped_handle(token); - - // Get the LUID for the shutdown privilege. - TOKEN_PRIVILEGES tkp = {0}; - LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); - tkp.PrivilegeCount = 1; - tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - // Get the shutdown privilege for this process. - AdjustTokenPrivileges(token, FALSE, &tkp, 0, - reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0); - if (GetLastError() != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to get shutdown privileges."; - return false; - } - - // Popup a dialog that will prompt to reboot using the default system message. - // TODO(robertshield): Add a localized, more specific string to the prompt. - RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG); - return true; -} - } // namespace int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, @@ -586,15 +550,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, prefs.get()); } - if (install_status == installer_util::UNINSTALL_REQUIRES_REBOOT) { - install_status = installer_util::UNINSTALL_SUCCESSFUL; -#if defined(CHROME_FRAME_BUILD) - ShowRebootDialog(); -#endif - } - CoUninitialize(); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); return dist->GetInstallReturnCode(install_status); } diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 2ad8cf2..978c056 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -17,7 +17,6 @@ #include "chrome/installer/setup/install.h" #include "chrome/installer/setup/setup_constants.h" #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/delete_after_reboot_helper.h" #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/logging_installer.h" @@ -138,27 +137,18 @@ bool DeleteEmptyParentDir(const std::wstring& path) { return ret; } -enum DeleteResult { - DELETE_SUCCEEDED, - DELETE_FAILED, - DELETE_REQUIRES_REBOOT -}; - -// Deletes all installed files of Chromium and Folders or schedules them for -// deletion on reboot if they are in use. Before deleting it +// Deletes all installed files of Chromium and Folders. Before deleting it // needs to move setup.exe in a temp folder because the current process -// is using that file. -// Returns DELETE_SUCCEEDED if all files were successfully delete. -// Returns DELETE_FAILED if it could not get the path to the install dir. -// Returns DELETE_REQUIRES_REBOOT if the files were in use and so were -// scheduled for deletion on next reboot. -DeleteResult DeleteFilesAndFolders(const std::wstring& exe_path, - bool system_uninstall, const installer::Version& installed_version, +// is using that file. It returns false when it can not get the path to +// installation folder, in all other cases it returns true even in case +// of error (only logs the error). +bool DeleteFilesAndFolders(const std::wstring& exe_path, bool system_uninstall, + const installer::Version& installed_version, std::wstring* local_state_path, bool delete_profile) { std::wstring install_path(installer::GetChromeInstallPath(system_uninstall)); if (install_path.empty()) { LOG(ERROR) << "Could not get installation destination path."; - return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. + return false; // Nothing else we can do for uninstall, so we return. } else { LOG(INFO) << "install destination path: " << install_path; } @@ -189,46 +179,27 @@ DeleteResult DeleteFilesAndFolders(const std::wstring& exe_path, file_util::CopyFile(user_local_file, path); } - DeleteResult result = DELETE_SUCCEEDED; - LOG(INFO) << "Deleting install path " << install_path; if (!file_util::Delete(install_path, true)) { LOG(ERROR) << "Failed to delete folder (1st try): " << install_path; -#if defined(CHROME_FRAME_BUILD) - // We don't try killing Chrome processes for Chrome Frame builds since - // that is unlikely to help. Instead, schedule files for deletion and - // return a value that will trigger a reboot prompt. - ScheduleDirectoryForDeletion(install_path.c_str()); - result = DELETE_REQUIRES_REBOOT; -#else // Try closing any running chrome processes and deleting files once again. CloseAllChromeProcesses(); - if (!file_util::Delete(install_path, true)) { + if (!file_util::Delete(install_path, true)) LOG(ERROR) << "Failed to delete folder (2nd try): " << install_path; - result = DELETE_FAILED; - } -#endif } if (delete_profile) { LOG(INFO) << "Deleting user profile" << user_local_state.value(); - if (!file_util::Delete(user_local_state, true)) { - LOG(ERROR) << "Failed to delete user profile dir: " + if (!file_util::Delete(user_local_state, true)) + LOG(ERROR) << "Failed to delete user profle dir: " << user_local_state.value(); -#if defined(CHROME_FRAME_BUILD) - ScheduleDirectoryForDeletion(user_local_state.value().c_str()); - result = DELETE_REQUIRES_REBOOT; -#else - result = DELETE_FAILED; -#endif - } DeleteEmptyParentDir(user_local_state.value()); } // Now check and delete if the parent directories are empty // For example Google\Chrome or Chromium DeleteEmptyParentDir(install_path); - return result; + return true; } // This method tries to delete a registry key and logs an error message @@ -397,9 +368,7 @@ installer_util::InstallStatus installer_setup::UninstallChrome( if (force_uninstall) { // Since --force-uninstall command line option is used, we are going to // do silent uninstall. Try to close all running Chrome instances. -#if !defined(CHROME_FRAME_BUILD) CloseAllChromeProcesses(); -#endif } else { // no --force-uninstall so lets show some UI dialog boxes. status = IsChromeActiveOrUserCancelled(system_uninstall); if (status != installer_util::UNINSTALL_CONFIRMED && @@ -500,14 +469,9 @@ installer_util::InstallStatus installer_setup::UninstallChrome( (cmd_line.HasSwitch(installer_util::switches::kDeleteProfile)); std::wstring local_state_path; ret = installer_util::UNINSTALL_SUCCESSFUL; - - DeleteResult delete_result = DeleteFilesAndFolders(exe_path, - system_uninstall, *installed_version, &local_state_path, delete_profile); - if (delete_result == DELETE_FAILED) { + if (!DeleteFilesAndFolders(exe_path, system_uninstall, *installed_version, + &local_state_path, delete_profile)) ret = installer_util::UNINSTALL_FAILED; - } else if (delete_result == DELETE_REQUIRES_REBOOT) { - ret = installer_util::UNINSTALL_REQUIRES_REBOOT; - } if (!force_uninstall) { LOG(INFO) << "Uninstallation complete. Launching Uninstall survey."; diff --git a/chrome/installer/util/delete_after_reboot_helper.cc b/chrome/installer/util/delete_after_reboot_helper.cc deleted file mode 100644 index a54acb3..0000000 --- a/chrome/installer/util/delete_after_reboot_helper.cc +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This file defines helper methods used to schedule files for deletion -// on next reboot. The code here is heavily borrowed and simplified from -// http://code.google.com/p/omaha/source/browse/trunk/common/file.cc and -// http://code.google.com/p/omaha/source/browse/trunk/common/utils.cc -// -// This implementation really is not fast, so do not use it where that will -// matter. - -#include "chrome/installer/util/delete_after_reboot_helper.h" - -#include <string> -#include <vector> - -#include "base/file_util.h" -#include "base/registry.h" -#include "base/string_util.h" - -// The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the -// registry. -const wchar_t kSessionManagerKey[] = - L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; -const wchar_t kPendingFileRenameOps[] = L"PendingFileRenameOperations"; - -namespace { - -// Returns true if this directory name is 'safe' for deletion (doesn't contain -// "..", doesn't specify a drive root) -bool IsSafeDirectoryNameForDeletion(const wchar_t* dir_name) { - DCHECK(dir_name); - - // empty name isn't allowed - if (!(dir_name && *dir_name)) { - return false; - } - - // require a character other than \/:. after the last : - // disallow anything with ".." - bool ok = false; - for (const wchar_t* s = dir_name; *s; ++s) { - if (*s != L'\\' && *s != L'/' && *s != L':' && *s != L'.') { - ok = true; - } - if (*s == L'.' && s > dir_name && *(s - 1) == L'.') { - return false; - } - if (*s == L':') { - ok = false; - } - } - return ok; -} - -// Must only be called for regular files or directories that will be empty. -bool ScheduleFileSystemEntityForDeletion(const wchar_t* path) { - // Check if the file exists, return false if not. - WIN32_FILE_ATTRIBUTE_DATA attrs = {0}; - if (!::GetFileAttributesEx(path, ::GetFileExInfoStandard, &attrs)) { - LOG(ERROR) << path << " for deletion does not exist." << GetLastError(); - return false; - } - - DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT; - if (!file_util::DirectoryExists(path)) { - // This flag valid only for files - flags |= MOVEFILE_REPLACE_EXISTING; - } - - if (!::MoveFileEx(path, NULL, flags)) { - LOG(ERROR) << "Could not schedule " << path << " for deletion."; - return false; - } - - LOG(INFO) << "Scheduled for deletion: " << path; - return true; -} -} // end namespace - -bool ScheduleDirectoryForDeletion(const wchar_t* dir_name) { - if (!IsSafeDirectoryNameForDeletion(dir_name)) { - LOG(ERROR) << "Unsafe directory name for deletion: " << dir_name; - return false; - } - - // Make sure the directory exists (it is ok if it doesn't) - DWORD dir_attributes = ::GetFileAttributes(dir_name); - if (dir_attributes == INVALID_FILE_ATTRIBUTES) { - if (::GetLastError() == ERROR_FILE_NOT_FOUND) { - return true; // Ok if directory is missing - } else { - LOG(ERROR) << "Could not GetFileAttributes for " << dir_name; - return false; - } - } - // Confirm it is a directory - if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) { - LOG(ERROR) << "Scheduled directory is not a directory: " << dir_name; - return false; - } - - // First schedule all the normal files for deletion. - { - bool success = true; - file_util::FileEnumerator file_enum(FilePath(dir_name), false, - file_util::FileEnumerator::FILES); - for (FilePath file = file_enum.Next(); !file.empty(); - file = file_enum.Next()) { - success = ScheduleFileSystemEntityForDeletion(file.value().c_str()); - if (!success) { - LOG(ERROR) << "Failed to schedule file for deletion: " << file.value(); - return false; - } - } - } - - // Then recurse to all the subdirectories. - { - bool success = true; - file_util::FileEnumerator dir_enum(FilePath(dir_name), false, - file_util::FileEnumerator::DIRECTORIES); - for (FilePath sub_dir = dir_enum.Next(); !sub_dir.empty(); - sub_dir = dir_enum.Next()) { - success = ScheduleDirectoryForDeletion(sub_dir.value().c_str()); - if (!success) { - LOG(ERROR) << "Failed to schedule subdirectory for deletion: " - << sub_dir.value(); - return false; - } - } - } - - // Now schedule the empty directory itself - if (!ScheduleFileSystemEntityForDeletion(dir_name)) { - LOG(ERROR) << "Failed to schedule directory for deletion: " << dir_name; - } - - return true; -} - -// Converts the strings found in |buffer| to a list of wstrings that is returned -// in |value|. -// |buffer| points to a series of pairs of null-terminated wchar_t strings -// followed by a terminating null character. -// |byte_count| is the length of |buffer| in bytes. -// |value| is a pointer to an empty vector of wstrings. On success, this vector -// contains all of the strings extracted from |buffer|. -// Returns S_OK on success, E_INVALIDARG if buffer does not meet tha above -// specification. -HRESULT MultiSZBytesToStringArray(const char* buffer, size_t byte_count, - std::vector<PendingMove>* value) { - DCHECK(buffer); - DCHECK(value); - DCHECK(value->empty()); - - DWORD data_len = byte_count / sizeof(wchar_t); - const wchar_t* data = reinterpret_cast<const wchar_t*>(buffer); - const wchar_t* data_end = data + data_len; - if (data_len > 1) { - // must be terminated by two null characters - if (data[data_len - 1] != 0 || data[data_len - 2] != 0) { - DLOG(ERROR) << "Invalid MULTI_SZ found."; - return E_INVALIDARG; - } - - // put null-terminated strings into arrays - while (data < data_end) { - std::wstring str_from(data); - data += str_from.length() + 1; - if (data < data_end) { - std::wstring str_to(data); - data += str_to.length() + 1; - value->push_back(std::make_pair(str_from, str_to)); - } - } - } - return S_OK; -} - -void StringArrayToMultiSZBytes(const std::vector<PendingMove>& strings, - std::vector<char>* buffer) { - DCHECK(buffer); - buffer->clear(); - - if (strings.size() == 0) { - // Leave buffer empty if we have no strings. - return; - } - - size_t total_wchars = 0; - { - std::vector<PendingMove>::const_iterator iter(strings.begin()); - for (; iter != strings.end(); ++iter) { - total_wchars += iter->first.length(); - total_wchars++; // Space for the null char. - total_wchars += iter->second.length(); - total_wchars++; // Space for the null char. - } - total_wchars++; // Space for the extra terminating null char. - } - - size_t total_length = total_wchars * sizeof(wchar_t); - buffer->resize(total_length); - wchar_t* write_pointer = reinterpret_cast<wchar_t*>(&((*buffer)[0])); - // Keep an end pointer around for sanity checking. - wchar_t* end_pointer = write_pointer + total_wchars; - - std::vector<PendingMove>::const_iterator copy_iter(strings.begin()); - for (; copy_iter != strings.end() && write_pointer < end_pointer; - copy_iter++) { - // First copy the source string. - size_t string_length = copy_iter->first.length() + 1; - memcpy(write_pointer, copy_iter->first.c_str(), - string_length * sizeof(wchar_t)); - write_pointer += string_length; - // Now copy the destination string. - string_length = copy_iter->second.length() + 1; - memcpy(write_pointer, copy_iter->second.c_str(), - string_length * sizeof(wchar_t)); - write_pointer += string_length; - - // We should never run off the end while in this loop. - DCHECK(write_pointer < end_pointer); - } - *write_pointer = L'\0'; // Explicitly set the final null char. - DCHECK(++write_pointer == end_pointer); -} - -std::wstring GetShortPathName(const wchar_t* path) { - std::wstring short_path; - DWORD length = GetShortPathName(path, WriteInto(&short_path, MAX_PATH), - MAX_PATH); - DLOG_IF(WARNING, length == 0) << __FUNCTION__ << " gle=" << GetLastError(); - short_path.resize(length); - return short_path; -} - -HRESULT GetPendingMovesValue( - std::vector<PendingMove>* pending_moves) { - DCHECK(pending_moves); - pending_moves->clear(); - - // Get the current value of the key - // If the Key is missing, that's totally acceptable. - RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey, - KEY_QUERY_VALUE); - HKEY session_manager_handle = session_manager_key.Handle(); - if (!session_manager_handle) { - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - } - - // The base::RegKey Read code squashes the return code from - // ReqQueryValueEx, we have to do things ourselves: - DWORD buffer_size = 0; - std::vector<char> buffer; - buffer.resize(1); - DWORD type; - DWORD result = RegQueryValueEx(session_manager_handle, kPendingFileRenameOps, - 0, &type, reinterpret_cast<BYTE*>(&buffer[0]), - &buffer_size); - - if (result == ERROR_FILE_NOT_FOUND) { - // No pending moves were found. - return HRESULT_FROM_WIN32(result); - } else if (result == ERROR_MORE_DATA) { - if (type != REG_MULTI_SZ) { - DLOG(ERROR) << "Found PendingRename value of unexpected type."; - return E_UNEXPECTED; - } - if (buffer_size % 2) { - // The buffer size should be an even number (since we expect wchar_ts). - // If this is not the case, fail here. - DLOG(ERROR) << "Corrupt PendingRename value."; - return E_UNEXPECTED; - } - - // There are pending file renames. Read them in. - buffer.resize(buffer_size); - result = RegQueryValueEx(session_manager_handle, kPendingFileRenameOps, - 0, &type, reinterpret_cast<LPBYTE>(&buffer[0]), - &buffer_size); - if (result != ERROR_SUCCESS) { - DLOG(ERROR) << "Failed to read from " << kPendingFileRenameOps; - return HRESULT_FROM_WIN32(result); - } - } else { - // That was unexpected. - DLOG(ERROR) << "Unexpected result from RegQueryValueEx: " << result; - return HRESULT_FROM_WIN32(result); - } - - // We now have a buffer of bytes that is actually a sequence of - // null-terminated wchar_t strings terminated by an additional null character. - // Stick this into a vector of strings for clarity. - HRESULT hr = MultiSZBytesToStringArray(&buffer[0], buffer.size(), - pending_moves); - return hr; -} - -bool MatchPendingDeletePath(const std::wstring& short_form_needle, - const std::wstring& reg_path) { - std::wstring match_path(reg_path); // Stores the path stored in each entry. - - // First chomp the prefix since that will mess up GetShortPathName. - std::wstring prefix(L"\\??\\"); - if (StartsWith(match_path, prefix, false)) { - match_path = match_path.substr(4); - } - - // Get the short path name of the entry. - std::wstring short_match_path(GetShortPathName(match_path.c_str())); - - // Now compare the paths. If it isn't one we're looking for, add it - // to the list to keep. - return StartsWith(short_match_path, short_form_needle, false); -} - -// Removes all pending moves for the given |directory| and any contained -// files or subdirectories. Returns true on success -bool RemoveFromMovesPendingReboot(const wchar_t* directory) { - DCHECK(directory); - std::vector<PendingMove> pending_moves; - HRESULT hr = GetPendingMovesValue(&pending_moves); - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { - // No pending moves, nothing to do. - return true; - } else if (FAILED(hr)) { - // Couldn't read the key or the key was corrupt. - return false; - } - - // Get the short form of |directory| and use that to match. - std::wstring short_directory(GetShortPathName(directory)); - - std::vector<PendingMove> strings_to_keep; - std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); - for (; iter != pending_moves.end(); iter++) { - if (!MatchPendingDeletePath(short_directory, iter->first)) { - // This doesn't match the deletions we are looking for. Preserve - // this string pair, making sure that it is in fact a pair. - strings_to_keep.push_back(*iter); - } - } - - if (strings_to_keep.size() == pending_moves.size()) { - // Nothing to remove, return true. - return true; - } - - // Write the key back into a buffer. - RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey, - KEY_CREATE_SUB_KEY | KEY_SET_VALUE); - if (!session_manager_key.Handle()) { - // Couldn't open / create the key. - LOG(ERROR) << "Failed to open session manager key for writing."; - return false; - } - - if (strings_to_keep.size() > 1) { - std::vector<char> buffer; - StringArrayToMultiSZBytes(strings_to_keep, &buffer); - DCHECK(buffer.size() > 0); - if (buffer.size() > 0) { - return session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0], - buffer.size(), REG_MULTI_SZ); - } else { - return false; - } - } else { - // We have only the trailing NULL string. Don't bother writing that. - return session_manager_key.DeleteValue(kPendingFileRenameOps); - } -} diff --git a/chrome/installer/util/delete_after_reboot_helper.h b/chrome/installer/util/delete_after_reboot_helper.h deleted file mode 100644 index 064a634..0000000 --- a/chrome/installer/util/delete_after_reboot_helper.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This file declares helper methods used to schedule files for deletion -// on next reboot. - -#ifndef CHROME_INSTALLER_UTIL_DELETE_AFTER_REBOOT_HELPER_H_ -#define CHROME_INSTALLER_UTIL_DELETE_AFTER_REBOOT_HELPER_H_ - -#include <string> -#include <vector> - -#include <windows.h> - -// Used by the unit tests. -extern const wchar_t kSessionManagerKey[]; -extern const wchar_t kPendingFileRenameOps[]; - -typedef std::pair<std::wstring, std::wstring> PendingMove; - -// Attempts to schedule the directory for deletion. -bool ScheduleDirectoryForDeletion(const wchar_t* dir_name); - -// Removes all pending moves that are registered for |directory| and all -// elements contained in |directory|. -bool RemoveFromMovesPendingReboot(const wchar_t* directory); - -// Retrieves the list of pending renames from the registry and returns a vector -// containing pairs of strings that represent the operations. If the list -// contains only deletes then every other element will be an empty string -// as per http://msdn.microsoft.com/en-us/library/aa365240(VS.85).aspx. -HRESULT GetPendingMovesValue(std::vector<PendingMove>* pending_moves); - -// This returns true if |short_form_needle| is contained in |reg_path| where -// |short_form_needle| is a file system path that has been shortened by -// GetShortPathName and |reg_path| is a path stored in the -// PendingFileRenameOperations key. -bool MatchPendingDeletePath(const std::wstring& short_form_needle, - const std::wstring& reg_path); - -// Converts the strings found in |buffer| to a list of PendingMoves that is -// returned in |value|. -// |buffer| points to a series of pairs of null-terminated wchar_t strings -// followed by a terminating null character. -// |byte_count| is the length of |buffer| in bytes. -// |value| is a pointer to an empty vector of PendingMoves (string pairs). -// On success, this vector contains all of the string pairs extracted from -// |buffer|. -// Returns S_OK on success, E_INVALIDARG if buffer does not meet the above -// specification. -HRESULT MultiSZBytesToStringArray(const char* buffer, size_t byte_count, - std::vector<PendingMove>* value); - -// The inverse of MultiSZBytesToStringArray, this function converts a list -// of string pairs into a byte array format suitable for writing to the -// kPendingFileRenameOps registry value. It concatenates the strings and -// appends an additional terminating null character. -void StringArrayToMultiSZBytes(const std::vector<PendingMove>& strings, - std::vector<char>* buffer); - -// A helper function for the win32 GetShortPathName that more conveniently -// returns a correctly sized wstring. -std::wstring GetShortPathName(const wchar_t* path); - -#endif // CHROME_INSTALLER_UTIL_DELETE_AFTER_REBOOT_HELPER_H_ diff --git a/chrome/installer/util/delete_after_reboot_helper_unittest.cc b/chrome/installer/util/delete_after_reboot_helper_unittest.cc deleted file mode 100644 index bf5af3b..0000000 --- a/chrome/installer/util/delete_after_reboot_helper_unittest.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <windows.h> -#include <shlobj.h> - -#include "base/file_util.h" -#include "base/registry.h" -#include "base/scoped_ptr.h" -#include "base/string_util.h" -#include "chrome/installer/util/delete_after_reboot_helper.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -// These tests exercise the Delete-After-Reboot code which requires -// modifications to HKLM. This will fail on Vista and above if the user -// is not an admin or if UAC is on. -// I tried using RegOverridePredefKey to test, but MoveFileEx ignore this -// even on 32 bit machines :-( As such, running this test may pollute -// your PendingFileRenameOperations value. -class DeleteAfterRebootHelperTest : public testing::Test { - protected: - virtual void SetUp() { - // Create a temporary directory for testing and fill it with some files. - std::wstring no_prefix; - file_util::CreateNewTempDirectory(no_prefix, &temp_dir_); - file_util::CreateTemporaryFileInDir(temp_dir_, &temp_file_); - - temp_subdir_ = temp_dir_.Append(L"subdir"); - file_util::CreateDirectory(temp_subdir_); - file_util::CreateTemporaryFileInDir(temp_subdir_, &temp_subdir_file_); - - // Copy the current pending moves and then clear it if we can: - if (IsUserAnAdmin()) { - GetPendingMovesValue(&original_pending_moves_); - } - } - virtual void TearDown() { - // Delete the temporary directory if it's still there. - file_util::Delete(temp_dir_, true); - - // Try and restore the pending moves value, if we have one. - if (IsUserAnAdmin() && original_pending_moves_.size() > 1) { - RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey, - KEY_CREATE_SUB_KEY | KEY_SET_VALUE); - if (!session_manager_key.Handle()) { - // Couldn't open / create the key. - DLOG(ERROR) << "Failed to open session manager key for writing."; - } - - std::vector<char> buffer; - StringArrayToMultiSZBytes(original_pending_moves_, &buffer); - session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0], - buffer.size(), REG_MULTI_SZ); - } - } - - // Compares two buffers of size len. Returns true if they are equal, - // false otherwise. Standard warnings about making sure the buffers - // are at least len chars long apply. - template<class Type> - bool CompareBuffers(Type* buf1, Type* buf2, int len) { - Type* comp1 = buf1; - Type* comp2 = buf2; - for (int i = 0; i < len; i++) { - if (*comp1 != *comp2) - return false; - comp1++; - comp2++; - } - return true; - } - - // Returns the size of the given list of wstrings in bytes, including - // null chars, plus an additional terminating null char. - // e.g. the length of all the strings * sizeof(wchar_t). - virtual size_t WStringPairListSize( - const std::vector<PendingMove>& string_list) { - size_t length = 0; - std::vector<PendingMove>::const_iterator iter(string_list.begin()); - for (; iter != string_list.end(); ++iter) { - length += iter->first.size() + 1; // +1 for the null char. - length += iter->second.size() + 1; // +1 for the null char. - } - length++; // for the additional null char. - return length * sizeof(wchar_t); - } - - std::vector<PendingMove> original_pending_moves_; - - FilePath temp_dir_; - FilePath temp_file_; - FilePath temp_subdir_; - FilePath temp_subdir_file_; -}; -} - -TEST_F(DeleteAfterRebootHelperTest, TestStringListToMultiSZConversions) { - struct StringTest { - wchar_t* test_name; - wchar_t* str; - DWORD length; - size_t count; - } tests[] = { - { L"basic", L"foo\0bar\0fee\0bee\0boo\0bong\0\0", 26 * sizeof(wchar_t), 3 }, - { L"empty", L"\0\0", 2 * sizeof(wchar_t), 1 }, - { L"deletes", L"foo\0\0bar\0\0bizz\0\0", 16 * sizeof(wchar_t), 3 }, - }; - - for (int i = 0; i < arraysize(tests); i++) { - std::vector<PendingMove> string_list; - EXPECT_TRUE(SUCCEEDED( - MultiSZBytesToStringArray(reinterpret_cast<char*>(tests[i].str), - tests[i].length, &string_list))) - << tests[i].test_name; - EXPECT_EQ(tests[i].count, string_list.size()) << tests[i].test_name; - std::vector<char> buffer; - buffer.resize(WStringPairListSize(string_list)); - StringArrayToMultiSZBytes(string_list, &buffer); - EXPECT_TRUE(CompareBuffers(&buffer[0], - reinterpret_cast<char*>(tests[i].str), - tests[i].length)) << tests[i].test_name; - } - - StringTest failures[] = - { L"malformed", reinterpret_cast<wchar_t*>("oddnumb\0\0"), 9, 1 }; - - for (int i = 0; i < arraysize(failures); i++) { - std::vector<PendingMove> string_list; - EXPECT_FALSE(SUCCEEDED( - MultiSZBytesToStringArray(reinterpret_cast<char*>(failures[i].str), - failures[i].length, &string_list))) - << failures[i].test_name; - } -} - - -TEST_F(DeleteAfterRebootHelperTest, TestFileDeletes) { - if (!IsUserAnAdmin()) { - return; - } - - EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_.value().c_str())); - - std::vector<PendingMove> pending_moves; - EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); - - // We should see, somewhere in this key, deletion writs for - // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. - EXPECT_TRUE(pending_moves.size() > 3); - - // Get the short form of temp_file_ and use that to match. - std::wstring short_temp_file(GetShortPathName(temp_file_.value().c_str())); - - // Scan for the first expected delete. - std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); - for (; iter != pending_moves.end(); iter++) { - if (MatchPendingDeletePath(short_temp_file, iter->first)) - break; - } - - // Check that each of the deletes we expect are there in order. - FilePath expected_paths[] = - { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; - for (int i = 0; i < arraysize(expected_paths); i++) { - EXPECT_FALSE(iter == pending_moves.end()); - if (iter != pending_moves.end()) { - std::wstring short_path_name( - GetShortPathName(expected_paths[i].value().c_str())); - EXPECT_TRUE(MatchPendingDeletePath(short_path_name, iter->first)); - iter++; - } - } - - // Test that we can remove the pending deletes. - EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_.value().c_str())); - EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); - std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); - for (; check_iter != pending_moves.end(); ++check_iter) { - EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, check_iter->first)); - } -} diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index 61f6805..534d770 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -39,8 +39,7 @@ enum InstallStatus { EULA_REJECTED, // EULA dialog was not accepted by user. EULA_ACCEPTED, // EULA dialog was accepted by user. EULA_ACCEPTED_OPT_IN, // EULA accepted wtih the crash optin selected. - INSTALL_DIR_IN_USE, // Installation directory is in use by another process - UNINSTALL_REQUIRES_REBOOT // Uninstallation required a reboot. + INSTALL_DIR_IN_USE // Installation directory is in use by another process }; namespace switches { |