delete_after_reboot_helper.cc revision 868fa2fe829687343ffae624259930155e16dbd8
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2010 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// This file defines helper methods used to schedule files for deletion 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// on next reboot. The code here is heavily borrowed and simplified from 74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// http://code.google.com/p/omaha/source/browse/trunk/common/file.cc and 84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// http://code.google.com/p/omaha/source/browse/trunk/common/utils.cc 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This implementation really is not fast, so do not use it where that will 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// matter. 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/installer/util/delete_after_reboot_helper.h" 141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <string> 161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <vector> 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/file_util.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/file_enumerator.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/registry.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_util.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the 2403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// registry. 25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const wchar_t kSessionManagerKey[] = 2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kPendingFileRenameOps[] = L"PendingFileRenameOperations"; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if this directory name is 'safe' for deletion (doesn't contain 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// "..", doesn't specify a drive root) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsSafeDirectoryNameForDeletion(const wchar_t* dir_name) { 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(dir_name); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // empty name isn't allowed 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!(dir_name && *dir_name)) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // require a character other than \/:. after the last : 417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // disallow anything with ".." 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool ok = false; 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (const wchar_t* s = dir_name; *s; ++s) { 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (*s != L'\\' && *s != L'/' && *s != L':' && *s != L'.') 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ok = true; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (*s == L'.' && s > dir_name && *(s - 1) == L'.') 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (*s == L':') 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ok = false; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ok; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // end namespace 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Must only be called for regular files or directories that will be empty. 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ScheduleFileSystemEntityForDeletion(const wchar_t* path) { 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check if the file exists, return false if not. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WIN32_FILE_ATTRIBUTE_DATA attrs = {0}; 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::GetFileAttributesEx(path, ::GetFileExInfoStandard, &attrs)) { 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PLOG(WARNING) << path << " does not exist."; 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!file_util::DirectoryExists(base::FilePath::FromWStringHack(path))) { 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This flag valid only for files 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) flags |= MOVEFILE_REPLACE_EXISTING; 697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::MoveFileEx(path, NULL, flags)) { 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PLOG(ERROR) << "Could not schedule " << path << " for deletion."; 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef NDEBUG 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Useful debugging code to track down what files are in use. 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (flags & MOVEFILE_REPLACE_EXISTING) { 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Attempt to open the file exclusively. 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HANDLE file = ::CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) OPEN_EXISTING, 0, NULL); 8203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (file != INVALID_HANDLE_VALUE) { 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(INFO) << " file not in use: " << path; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::CloseHandle(file); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) PLOG(INFO) << " file in use (or not found?): " << path; 87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#endif 90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) VLOG(1) << "Scheduled for deletion: " << path; 92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return true; 93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool ScheduleDirectoryForDeletion(const wchar_t* dir_name) { 96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!IsSafeDirectoryNameForDeletion(dir_name)) { 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) LOG(ERROR) << "Unsafe directory name for deletion: " << dir_name; 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return false; 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Make sure the directory exists (it is ok if it doesn't) 102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DWORD dir_attributes = ::GetFileAttributes(dir_name); 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (dir_attributes == INVALID_FILE_ATTRIBUTES) { 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (::GetLastError() == ERROR_FILE_NOT_FOUND) { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; // Ok if directory is missing 106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) PLOG(ERROR) << "Could not GetFileAttributes for " << dir_name; 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return false; 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Confirm it is a directory 112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) { 113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) LOG(ERROR) << "Scheduled directory is not a directory: " << dir_name; 114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return false; 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // First schedule all the normal files for deletion. 118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) { 119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) bool success = true; 120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::FileEnumerator file_enum(base::FilePath(dir_name), false, 121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::FileEnumerator::FILES); 122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for (base::FilePath file = file_enum.Next(); !file.empty(); 123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) file = file_enum.Next()) { 124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) success = ScheduleFileSystemEntityForDeletion(file.value().c_str()); 125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!success) { 126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) LOG(ERROR) << "Failed to schedule file for deletion: " << file.value(); 127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return false; 128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Then recurse to all the subdirectories. 133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool success = true; 135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::FileEnumerator dir_enum(base::FilePath(dir_name), false, 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::FileEnumerator::DIRECTORIES); 137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch for (base::FilePath sub_dir = dir_enum.Next(); !sub_dir.empty(); 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sub_dir = dir_enum.Next()) { 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) success = ScheduleDirectoryForDeletion(sub_dir.value().c_str()); 140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!success) { 141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch LOG(ERROR) << "Failed to schedule subdirectory for deletion: " 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << sub_dir.value(); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Now schedule the empty directory itself 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!ScheduleFileSystemEntityForDeletion(dir_name)) 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) LOG(ERROR) << "Failed to schedule directory for deletion: " << dir_name; 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Converts the strings found in |buffer| to a list of wstrings that is returned 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in |value|. 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |buffer| points to a series of pairs of null-terminated wchar_t strings 158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// followed by a terminating null character. 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// |byte_count| is the length of |buffer| in bytes. 160868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// |value| is a pointer to an empty vector of wstrings. On success, this vector 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// contains all of the strings extracted from |buffer|. 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns S_OK on success, E_INVALIDARG if buffer does not meet tha above 163868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// specification. 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)HRESULT MultiSZBytesToStringArray(const char* buffer, size_t byte_count, 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<PendingMove>* value) { 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(buffer); 1677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK(value); 168868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(value->empty()); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DWORD data_len = byte_count / sizeof(wchar_t); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const wchar_t* data = reinterpret_cast<const wchar_t*>(buffer); 172868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const wchar_t* data_end = data + data_len; 1737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (data_len > 1) { 1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // must be terminated by two null characters 1754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (data[data_len - 1] != 0 || data[data_len - 2] != 0) { 1764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) DLOG(ERROR) << "Invalid MULTI_SZ found."; 1774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return E_INVALIDARG; 1787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // put null-terminated strings into arrays 1817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch while (data < data_end) { 182868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) std::wstring str_from(data); 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data += str_from.length() + 1; 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data < data_end) { 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::wstring str_to(data); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data += str_to.length() + 1; 187868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) value->push_back(std::make_pair(str_from, str_to)); 1887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 189868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return S_OK; 192868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid StringArrayToMultiSZBytes(const std::vector<PendingMove>& strings, 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<char>* buffer) { 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(buffer); 1977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch buffer->clear(); 198868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (strings.empty()) { 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Leave buffer empty if we have no strings. 201868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return; 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 2044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) size_t total_wchars = 0; 2054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) { 2064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) std::vector<PendingMove>::const_iterator iter(strings.begin()); 2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) for (; iter != strings.end(); ++iter) { 2087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch total_wchars += iter->first.length(); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) total_wchars++; // Space for the null char. 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) total_wchars += iter->second.length(); 2117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch total_wchars++; // Space for the null char. 212868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) total_wchars++; // Space for the extra terminating null char. 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 215868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) size_t total_length = total_wchars * sizeof(wchar_t); 2174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) buffer->resize(total_length); 218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) wchar_t* write_pointer = reinterpret_cast<wchar_t*>(&((*buffer)[0])); 219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Keep an end pointer around for sanity checking. 220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) wchar_t* end_pointer = write_pointer + total_wchars; 2217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<PendingMove>::const_iterator copy_iter(strings.begin()); 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (; copy_iter != strings.end() && write_pointer < end_pointer; 2247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch copy_iter++) { 225868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // First copy the source string. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t string_length = copy_iter->first.length() + 1; 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memcpy(write_pointer, copy_iter->first.c_str(), 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) string_length * sizeof(wchar_t)); 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) write_pointer += string_length; 230868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Now copy the destination string. 2317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch string_length = copy_iter->second.length() + 1; 232868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) memcpy(write_pointer, copy_iter->second.c_str(), 2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) string_length * sizeof(wchar_t)); 234868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) write_pointer += string_length; 235868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We should never run off the end while in this loop. 2377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK(write_pointer < end_pointer); 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *write_pointer = L'\0'; // Explicitly set the final null char. 2407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK(++write_pointer == end_pointer); 241868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::wstring GetShortPathName(const wchar_t* path) { 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::wstring short_path; 245868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DWORD length = GetShortPathName(path, WriteInto(&short_path, MAX_PATH), 2467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch MAX_PATH); 247868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DWORD last_error = ::GetLastError(); 2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DLOG_IF(WARNING, length == 0 && last_error != ERROR_PATH_NOT_FOUND) 249868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) << __FUNCTION__ << " gle=" << last_error; 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (length == 0) { 2517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // GetShortPathName fails if the path is no longer present. Instead of 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // returning an empty string, just return the original string. This will 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // serve our purposes. 2547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return path; 255868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) short_path.resize(length); 25868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) return short_path; 25958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)} 2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 26158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)HRESULT GetPendingMovesValue( 262868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) std::vector<PendingMove>* pending_moves) { 2637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK(pending_moves); 264868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_moves->clear(); 2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 26668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // Get the current value of the key 26768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // If the Key is missing, that's totally acceptable. 2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey, 2697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch KEY_QUERY_VALUE); 2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) HKEY session_manager_handle = session_manager_key.Handle(); 2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!session_manager_handle) 2727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 273868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The base::RegKey Read code squashes the return code from 2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // ReqQueryValueEx, we have to do things ourselves: 27668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) DWORD buffer_size = 0; 2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<char> buffer; 278868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) buffer.resize(1); 2797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DWORD type; 280868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DWORD result = RegQueryValueEx(session_manager_handle, kPendingFileRenameOps, 2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 0, &type, reinterpret_cast<BYTE*>(&buffer[0]), 28268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) &buffer_size); 28368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (result == ERROR_FILE_NOT_FOUND) { 2857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // No pending moves were found. 2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return HRESULT_FROM_WIN32(result); 2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (result != ERROR_MORE_DATA) { 289868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // That was unexpected. 2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DLOG(ERROR) << "Unexpected result from RegQueryValueEx: " << result; 2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return HRESULT_FROM_WIN32(result); 2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 293868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (type != REG_MULTI_SZ) { 2947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DLOG(ERROR) << "Found PendingRename value of unexpected type."; 295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return E_UNEXPECTED; 2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (buffer_size % 2) { 298868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // The buffer size should be an even number (since we expect wchar_ts). 2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If this is not the case, fail here. 3007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DLOG(ERROR) << "Corrupt PendingRename value."; 3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return E_UNEXPECTED; 3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 304868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // There are pending file renames. Read them in. 3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) buffer.resize(buffer_size); 3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result = RegQueryValueEx(session_manager_handle, kPendingFileRenameOps, 307868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 0, &type, reinterpret_cast<LPBYTE>(&buffer[0]), 3087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch &buffer_size); 309868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (result != ERROR_SUCCESS) { 3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DLOG(ERROR) << "Failed to read from " << kPendingFileRenameOps; 311868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return HRESULT_FROM_WIN32(result); 312868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // We now have a buffer of bytes that is actually a sequence of 3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // null-terminated wchar_t strings terminated by an additional null character. 3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Stick this into a vector of strings for clarity. 3177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch HRESULT hr = MultiSZBytesToStringArray(&buffer[0], buffer.size(), 318868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_moves); 3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return hr; 3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 321868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 3227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochbool MatchPendingDeletePath(const std::wstring& short_form_needle, 323868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const std::wstring& reg_path) { 3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::wstring match_path(reg_path); // Stores the path stored in each entry. 3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 326868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // First chomp the prefix since that will mess up GetShortPathName. 3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::wstring prefix(L"\\??\\"); 3287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (StartsWith(match_path, prefix, false)) 3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) match_path = match_path.substr(4); 3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // Get the short path name of the entry. 332eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch std::wstring short_match_path(GetShortPathName(match_path.c_str())); 333eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 334eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Now compare the paths. If it isn't one we're looking for, add it 3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // to the list to keep. 336eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return StartsWith(short_match_path, short_form_needle, false); 337eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 3387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 339868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Removes all pending moves for the given |directory| and any contained 3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// files or subdirectories. Returns true on success 3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool RemoveFromMovesPendingReboot(const wchar_t* directory) { 342868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(directory); 3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<PendingMove> pending_moves; 3447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch HRESULT hr = GetPendingMovesValue(&pending_moves); 3454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { 3464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // No pending moves, nothing to do. 3474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return true; 3484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 3497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (FAILED(hr)) { 3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Couldn't read the key or the key was corrupt. 3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 35203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } 353 354 // Get the short form of |directory| and use that to match. 355 std::wstring short_directory(GetShortPathName(directory)); 356 357 std::vector<PendingMove> strings_to_keep; 358 for (std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 359 iter != pending_moves.end(); iter++) { 360 if (!MatchPendingDeletePath(short_directory, iter->first)) { 361 // This doesn't match the deletions we are looking for. Preserve 362 // this string pair, making sure that it is in fact a pair. 363 strings_to_keep.push_back(*iter); 364 } 365 } 366 367 if (strings_to_keep.size() == pending_moves.size()) { 368 // Nothing to remove, return true. 369 return true; 370 } 371 372 // Write the key back into a buffer. 373 base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey, 374 KEY_CREATE_SUB_KEY | KEY_SET_VALUE); 375 if (!session_manager_key.Handle()) { 376 // Couldn't open / create the key. 377 LOG(ERROR) << "Failed to open session manager key for writing."; 378 return false; 379 } 380 381 if (strings_to_keep.size() <= 1) { 382 // We have only the trailing NULL string. Don't bother writing that. 383 return (session_manager_key.DeleteValue(kPendingFileRenameOps) == 384 ERROR_SUCCESS); 385 } 386 std::vector<char> buffer; 387 StringArrayToMultiSZBytes(strings_to_keep, &buffer); 388 DCHECK_GT(buffer.size(), 0U); 389 if (buffer.empty()) 390 return false; 391 return (session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0], 392 buffer.size(), REG_MULTI_SZ) == ERROR_SUCCESS); 393} 394