1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/installer/util/delete_tree_work_item.h" 6 7#include <algorithm> 8#include <limits> 9 10#include "base/files/file_util.h" 11#include "base/logging.h" 12 13namespace { 14 15// Casts a value of an unsigned type to a signed type of the same size provided 16// that there is no overflow. 17template<typename L, typename R> 18bool SafeCast(L left, R* right) { 19 DCHECK(right); 20 COMPILE_ASSERT(sizeof(left) == sizeof(right), 21 must_add_support_for_crazy_data_types); 22 if (left > static_cast<L>(std::numeric_limits<R>::max())) 23 return false; 24 *right = static_cast<L>(left); 25 return true; 26} 27 28} // namespace 29 30DeleteTreeWorkItem::DeleteTreeWorkItem( 31 const base::FilePath& root_path, 32 const base::FilePath& temp_path, 33 const std::vector<base::FilePath>& key_paths) 34 : root_path_(root_path), 35 temp_path_(temp_path), 36 copied_to_backup_(false) { 37 if (!SafeCast(key_paths.size(), &num_key_files_)) { 38 NOTREACHED() << "Impossibly large key_paths collection"; 39 } else if (num_key_files_ != 0) { 40 key_paths_.reset(new base::FilePath[num_key_files_]); 41 key_backup_paths_.reset(new base::ScopedTempDir[num_key_files_]); 42 std::copy(key_paths.begin(), key_paths.end(), &key_paths_[0]); 43 } 44} 45 46DeleteTreeWorkItem::~DeleteTreeWorkItem() { 47} 48 49// We first try to move key_path_ to backup_path. If it succeeds, we go ahead 50// and move the rest. 51bool DeleteTreeWorkItem::Do() { 52 // Go through all the key files and see if we can open them exclusively 53 // with only the FILE_SHARE_DELETE flag. Once we know we have all of them, 54 // we can delete them. 55 std::vector<HANDLE> opened_key_files; 56 opened_key_files.reserve(num_key_files_); 57 bool abort = false; 58 for (ptrdiff_t i = 0; !abort && i != num_key_files_; ++i) { 59 base::FilePath& key_file = key_paths_[i]; 60 base::ScopedTempDir& backup = key_backup_paths_[i]; 61 if (!ignore_failure_) { 62 if (!backup.CreateUniqueTempDirUnderPath(temp_path_)) { 63 PLOG(ERROR) << "Could not create temp dir in " << temp_path_.value(); 64 abort = true; 65 } else if (!base::CopyFile(key_file, 66 backup.path().Append(key_file.BaseName()))) { 67 PLOG(ERROR) << "Could not back up " << key_file.value() 68 << " to directory " << backup.path().value(); 69 abort = true; 70 backup.Delete(); 71 } 72 } 73 if (!abort) { 74 HANDLE file = ::CreateFile(key_file.value().c_str(), FILE_ALL_ACCESS, 75 FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, 76 NULL); 77 if (file != INVALID_HANDLE_VALUE) { 78 VLOG(1) << "Acquired exclusive lock for key file: " << key_file.value(); 79 opened_key_files.push_back(file); 80 } else { 81 if (::GetLastError() != ERROR_FILE_NOT_FOUND) 82 abort = true; 83 PLOG(INFO) << "Failed to open " << key_file.value(); 84 } 85 } 86 } 87 88 if (!abort) { 89 // We now hold exclusive locks with "share delete" permissions for each 90 // of the key files and also have created backups of those files. 91 // We can safely delete the key files now. 92 for (ptrdiff_t i = 0; !abort && i != num_key_files_; ++i) { 93 base::FilePath& key_file = key_paths_[i]; 94 if (!base::DeleteFile(key_file, true)) { 95 // This should not really be possible because of the above. 96 PLOG(DFATAL) << "Unexpectedly could not delete " << key_file.value(); 97 abort = true; 98 } 99 } 100 } 101 102 std::for_each(opened_key_files.begin(), opened_key_files.end(), CloseHandle); 103 opened_key_files.clear(); 104 105 if (abort) { 106 LOG(ERROR) << "Could not exclusively hold all key files."; 107 return ignore_failure_; 108 } 109 110 // Now that we've taken care of the key files, take care of the rest. 111 if (!root_path_.empty() && base::PathExists(root_path_)) { 112 if (!ignore_failure_) { 113 if (!backup_path_.CreateUniqueTempDirUnderPath(temp_path_)) { 114 PLOG(ERROR) << "Failed to get backup path in folder " 115 << temp_path_.value(); 116 return false; 117 } else { 118 base::FilePath backup = 119 backup_path_.path().Append(root_path_.BaseName()); 120 if (!base::CopyDirectory(root_path_, backup, true)) { 121 LOG(ERROR) << "can not copy " << root_path_.value() 122 << " to backup path " << backup.value(); 123 return false; 124 } else { 125 copied_to_backup_ = true; 126 } 127 } 128 } 129 if (!base::DeleteFile(root_path_, true)) { 130 LOG(ERROR) << "can not delete " << root_path_.value(); 131 return ignore_failure_; 132 } 133 } 134 135 return true; 136} 137 138// If there are files in backup paths move them back. 139void DeleteTreeWorkItem::Rollback() { 140 if (ignore_failure_) 141 return; 142 143 if (copied_to_backup_) { 144 DCHECK(!backup_path_.path().empty()); 145 base::FilePath backup = backup_path_.path().Append(root_path_.BaseName()); 146 if (base::PathExists(backup)) 147 base::Move(backup, root_path_); 148 } 149 150 for (ptrdiff_t i = 0; i != num_key_files_; ++i) { 151 base::ScopedTempDir& backup_dir = key_backup_paths_[i]; 152 if (!backup_dir.path().empty()) { 153 base::FilePath& key_file = key_paths_[i]; 154 base::FilePath backup_file = 155 backup_dir.path().Append(key_file.BaseName()); 156 if (base::PathExists(backup_file) && 157 !base::Move(backup_file, key_file)) { 158 // This could happen if we could not delete the key file to begin with. 159 PLOG(WARNING) << "Rollback: Failed to move backup file back in place: " 160 << backup_file.value() << " to " << key_file.value(); 161 } 162 } 163 } 164} 165