| // Copyright (c) 2010 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 "chrome/installer/util/copy_tree_work_item.h" |
| |
| #include <shlwapi.h> |
| |
| #include "base/file_util.h" |
| #include "base/logging.h" |
| #include "chrome/installer/util/logging_installer.h" |
| |
| CopyTreeWorkItem::~CopyTreeWorkItem() { |
| } |
| |
| CopyTreeWorkItem::CopyTreeWorkItem(const base::FilePath& source_path, |
| const base::FilePath& dest_path, |
| const base::FilePath& temp_dir, |
| CopyOverWriteOption overwrite_option, |
| const base::FilePath& alternative_path) |
| : source_path_(source_path), |
| dest_path_(dest_path), |
| temp_dir_(temp_dir), |
| overwrite_option_(overwrite_option), |
| alternative_path_(alternative_path), |
| copied_to_dest_path_(false), |
| moved_to_backup_(false), |
| copied_to_alternate_path_(false) { |
| } |
| |
| bool CopyTreeWorkItem::Do() { |
| if (!base::PathExists(source_path_)) { |
| LOG(ERROR) << source_path_.value() << " does not exist"; |
| return false; |
| } |
| |
| bool dest_exist = base::PathExists(dest_path_); |
| // handle overwrite_option_ = IF_DIFFERENT case. |
| if ((dest_exist) && |
| (overwrite_option_ == WorkItem::IF_DIFFERENT) && // only for single file |
| (!base::DirectoryExists(source_path_)) && |
| (!base::DirectoryExists(dest_path_)) && |
| (base::ContentsEqual(source_path_, dest_path_))) { |
| VLOG(1) << "Source file " << source_path_.value() |
| << " and destination file " << dest_path_.value() |
| << " are exactly same. Returning true."; |
| return true; |
| } else if ((dest_exist) && |
| (overwrite_option_ == WorkItem::NEW_NAME_IF_IN_USE) && |
| (!base::DirectoryExists(source_path_)) && |
| (!base::DirectoryExists(dest_path_)) && |
| (IsFileInUse(dest_path_))) { |
| // handle overwrite_option_ = NEW_NAME_IF_IN_USE case. |
| if (alternative_path_.empty() || |
| base::PathExists(alternative_path_) || |
| !base::CopyFile(source_path_, alternative_path_)) { |
| LOG(ERROR) << "failed to copy " << source_path_.value() |
| << " to " << alternative_path_.value(); |
| return false; |
| } else { |
| copied_to_alternate_path_ = true; |
| VLOG(1) << "Copied source file " << source_path_.value() |
| << " to alternative path " << alternative_path_.value(); |
| return true; |
| } |
| } else if ((dest_exist) && |
| (overwrite_option_ == WorkItem::IF_NOT_PRESENT)) { |
| // handle overwrite_option_ = IF_NOT_PRESENT case. |
| return true; |
| } |
| |
| // In all cases that reach here, move dest to a backup path. |
| if (dest_exist) { |
| if (!backup_path_.CreateUniqueTempDirUnderPath(temp_dir_)) { |
| PLOG(ERROR) << "Failed to get backup path in folder " |
| << temp_dir_.value(); |
| return false; |
| } |
| |
| base::FilePath backup = backup_path_.path().Append(dest_path_.BaseName()); |
| if (base::Move(dest_path_, backup)) { |
| moved_to_backup_ = true; |
| VLOG(1) << "Moved destination " << dest_path_.value() << |
| " to backup path " << backup.value(); |
| } else { |
| LOG(ERROR) << "failed moving " << dest_path_.value() |
| << " to " << backup.value(); |
| return false; |
| } |
| } |
| |
| // In all cases that reach here, copy source to destination. |
| if (base::CopyDirectory(source_path_, dest_path_, true)) { |
| copied_to_dest_path_ = true; |
| VLOG(1) << "Copied source " << source_path_.value() |
| << " to destination " << dest_path_.value(); |
| } else { |
| LOG(ERROR) << "failed copy " << source_path_.value() |
| << " to " << dest_path_.value(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void CopyTreeWorkItem::Rollback() { |
| // Normally the delete operations below should not fail unless some |
| // programs like anti-virus are inspecting the files we just copied. |
| // If this does happen sometimes, we may consider using Move instead of |
| // Delete here. For now we just log the error and continue with the |
| // rest of rollback operation. |
| if (copied_to_dest_path_ && !base::DeleteFile(dest_path_, true)) { |
| LOG(ERROR) << "Can not delete " << dest_path_.value(); |
| } |
| if (moved_to_backup_) { |
| base::FilePath backup(backup_path_.path().Append(dest_path_.BaseName())); |
| if (!base::Move(backup, dest_path_)) { |
| LOG(ERROR) << "failed move " << backup.value() |
| << " to " << dest_path_.value(); |
| } |
| } |
| if (copied_to_alternate_path_ && |
| !base::DeleteFile(alternative_path_, true)) { |
| LOG(ERROR) << "Can not delete " << alternative_path_.value(); |
| } |
| } |
| |
| bool CopyTreeWorkItem::IsFileInUse(const base::FilePath& path) { |
| if (!base::PathExists(path)) |
| return false; |
| |
| HANDLE handle = ::CreateFile(path.value().c_str(), FILE_ALL_ACCESS, |
| NULL, NULL, OPEN_EXISTING, NULL, NULL); |
| if (handle == INVALID_HANDLE_VALUE) |
| return true; |
| |
| CloseHandle(handle); |
| return false; |
| } |