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