This source file includes following definitions.
- IsSafeDirectoryNameForDeletion
- ScheduleFileSystemEntityForDeletion
- ScheduleDirectoryForDeletion
- MultiSZBytesToStringArray
- StringArrayToMultiSZBytes
- GetShortPathName
- GetPendingMovesValue
- MatchPendingDeletePath
- RemoveFromMovesPendingReboot
#include "chrome/installer/util/delete_after_reboot_helper.h"
#include <string>
#include <vector>
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/strings/string_util.h"
#include "base/win/registry.h"
const wchar_t kSessionManagerKey[] =
L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
const wchar_t kPendingFileRenameOps[] = L"PendingFileRenameOperations";
namespace {
bool IsSafeDirectoryNameForDeletion(const base::FilePath& dir_name) {
if (dir_name.empty())
return false;
bool ok = false;
const wchar_t* dir_name_str = dir_name.value().c_str();
for (const wchar_t* s = dir_name_str; *s; ++s) {
if (*s != L'\\' && *s != L'/' && *s != L':' && *s != L'.')
ok = true;
if (*s == L'.' && s > dir_name_str && *(s - 1) == L'.')
return false;
if (*s == L':')
ok = false;
}
return ok;
}
}
bool ScheduleFileSystemEntityForDeletion(const base::FilePath& path) {
WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
if (!::GetFileAttributesEx(path.value().c_str(),
::GetFileExInfoStandard, &attrs)) {
PLOG(WARNING) << path.value() << " does not exist.";
return false;
}
DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT;
if (!base::DirectoryExists(path)) {
flags |= MOVEFILE_REPLACE_EXISTING;
}
if (!::MoveFileEx(path.value().c_str(), NULL, flags)) {
PLOG(ERROR) << "Could not schedule " << path.value() << " for deletion.";
return false;
}
#ifndef NDEBUG
if (flags & MOVEFILE_REPLACE_EXISTING) {
HANDLE file = ::CreateFileW(path.value().c_str(),
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
if (file != INVALID_HANDLE_VALUE) {
LOG(INFO) << " file not in use: " << path.value();
::CloseHandle(file);
} else {
PLOG(INFO) << " file in use (or not found?): " << path.value();
}
}
#endif
VLOG(1) << "Scheduled for deletion: " << path.value();
return true;
}
bool ScheduleDirectoryForDeletion(const base::FilePath& dir_name) {
if (!IsSafeDirectoryNameForDeletion(dir_name)) {
LOG(ERROR) << "Unsafe directory name for deletion: " << dir_name.value();
return false;
}
DWORD dir_attributes = ::GetFileAttributes(dir_name.value().c_str());
if (dir_attributes == INVALID_FILE_ATTRIBUTES) {
if (::GetLastError() == ERROR_FILE_NOT_FOUND) {
return true;
} else {
PLOG(ERROR) << "Could not GetFileAttributes for " << dir_name.value();
return false;
}
}
if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
LOG(ERROR) << "Scheduled directory is not a directory: "
<< dir_name.value();
return false;
}
{
bool success = true;
base::FileEnumerator file_enum(dir_name, false,
base::FileEnumerator::FILES);
for (base::FilePath file = file_enum.Next(); !file.empty();
file = file_enum.Next()) {
success = ScheduleFileSystemEntityForDeletion(file);
if (!success) {
LOG(ERROR) << "Failed to schedule file for deletion: " << file.value();
return false;
}
}
}
{
bool success = true;
base::FileEnumerator dir_enum(dir_name, false,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath sub_dir = dir_enum.Next(); !sub_dir.empty();
sub_dir = dir_enum.Next()) {
success = ScheduleDirectoryForDeletion(sub_dir);
if (!success) {
LOG(ERROR) << "Failed to schedule subdirectory for deletion: "
<< sub_dir.value();
return false;
}
}
}
if (!ScheduleFileSystemEntityForDeletion(dir_name)) {
LOG(ERROR) << "Failed to schedule directory for deletion: "
<< dir_name.value();
}
return true;
}
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) {
if (data[data_len - 1] != 0 || data[data_len - 2] != 0) {
DLOG(ERROR) << "Invalid MULTI_SZ found.";
return E_INVALIDARG;
}
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.empty()) {
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++;
total_wchars += iter->second.length();
total_wchars++;
}
total_wchars++;
}
size_t total_length = total_wchars * sizeof(wchar_t);
buffer->resize(total_length);
wchar_t* write_pointer = reinterpret_cast<wchar_t*>(&((*buffer)[0]));
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++) {
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;
string_length = copy_iter->second.length() + 1;
memcpy(write_pointer, copy_iter->second.c_str(),
string_length * sizeof(wchar_t));
write_pointer += string_length;
DCHECK(write_pointer < end_pointer);
}
*write_pointer = L'\0';
DCHECK(++write_pointer == end_pointer);
}
base::FilePath GetShortPathName(const base::FilePath& path) {
std::wstring short_path;
DWORD length = GetShortPathName(path.value().c_str(),
WriteInto(&short_path, MAX_PATH),
MAX_PATH);
DWORD last_error = ::GetLastError();
DLOG_IF(WARNING, length == 0 && last_error != ERROR_PATH_NOT_FOUND)
<< __FUNCTION__ << " gle=" << last_error;
if (length == 0) {
return path;
}
short_path.resize(length);
return base::FilePath(short_path);
}
HRESULT GetPendingMovesValue(std::vector<PendingMove>* pending_moves) {
DCHECK(pending_moves);
pending_moves->clear();
base::win::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);
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) {
return HRESULT_FROM_WIN32(result);
}
if (result != ERROR_MORE_DATA) {
DLOG(ERROR) << "Unexpected result from RegQueryValueEx: " << result;
return HRESULT_FROM_WIN32(result);
}
if (type != REG_MULTI_SZ) {
DLOG(ERROR) << "Found PendingRename value of unexpected type.";
return E_UNEXPECTED;
}
if (buffer_size % 2) {
DLOG(ERROR) << "Corrupt PendingRename value.";
return E_UNEXPECTED;
}
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);
}
HRESULT hr = MultiSZBytesToStringArray(&buffer[0], buffer.size(),
pending_moves);
return hr;
}
bool MatchPendingDeletePath(const base::FilePath& short_form_needle,
const base::FilePath& reg_path) {
std::wstring match_path(reg_path.value());
std::wstring prefix(L"\\??\\");
if (StartsWith(match_path, prefix, false))
match_path = match_path.substr(4);
base::FilePath short_match_path(GetShortPathName(base::FilePath(match_path)));
return StartsWith(short_match_path.value(), short_form_needle.value(), false);
}
bool RemoveFromMovesPendingReboot(const base::FilePath& directory) {
std::vector<PendingMove> pending_moves;
HRESULT hr = GetPendingMovesValue(&pending_moves);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
return true;
}
if (FAILED(hr)) {
return false;
}
base::FilePath short_directory(GetShortPathName(directory));
std::vector<PendingMove> strings_to_keep;
for (std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
iter != pending_moves.end(); ++iter) {
base::FilePath move_path(iter->first);
if (!MatchPendingDeletePath(short_directory, move_path)) {
strings_to_keep.push_back(*iter);
}
}
if (strings_to_keep.size() == pending_moves.size()) {
return true;
}
base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
KEY_CREATE_SUB_KEY | KEY_SET_VALUE);
if (!session_manager_key.Handle()) {
LOG(ERROR) << "Failed to open session manager key for writing.";
return false;
}
if (strings_to_keep.size() <= 1) {
return (session_manager_key.DeleteValue(kPendingFileRenameOps) ==
ERROR_SUCCESS);
}
std::vector<char> buffer;
StringArrayToMultiSZBytes(strings_to_keep, &buffer);
DCHECK_GT(buffer.size(), 0U);
if (buffer.empty())
return false;
return (session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0],
buffer.size(), REG_MULTI_SZ) == ERROR_SUCCESS);
}