1// Copyright (c) 2010 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/copy_tree_work_item.h" 6 7#include <shlwapi.h> 8 9#include "base/file_util.h" 10#include "base/logging.h" 11#include "chrome/installer/util/logging_installer.h" 12 13CopyTreeWorkItem::~CopyTreeWorkItem() { 14} 15 16CopyTreeWorkItem::CopyTreeWorkItem(const base::FilePath& source_path, 17 const base::FilePath& dest_path, 18 const base::FilePath& temp_dir, 19 CopyOverWriteOption overwrite_option, 20 const base::FilePath& alternative_path) 21 : source_path_(source_path), 22 dest_path_(dest_path), 23 temp_dir_(temp_dir), 24 overwrite_option_(overwrite_option), 25 alternative_path_(alternative_path), 26 copied_to_dest_path_(false), 27 moved_to_backup_(false), 28 copied_to_alternate_path_(false) { 29} 30 31bool CopyTreeWorkItem::Do() { 32 if (!base::PathExists(source_path_)) { 33 LOG(ERROR) << source_path_.value() << " does not exist"; 34 return false; 35 } 36 37 bool dest_exist = base::PathExists(dest_path_); 38 // handle overwrite_option_ = IF_DIFFERENT case. 39 if ((dest_exist) && 40 (overwrite_option_ == WorkItem::IF_DIFFERENT) && // only for single file 41 (!base::DirectoryExists(source_path_)) && 42 (!base::DirectoryExists(dest_path_)) && 43 (base::ContentsEqual(source_path_, dest_path_))) { 44 VLOG(1) << "Source file " << source_path_.value() 45 << " and destination file " << dest_path_.value() 46 << " are exactly same. Returning true."; 47 return true; 48 } else if ((dest_exist) && 49 (overwrite_option_ == WorkItem::NEW_NAME_IF_IN_USE) && 50 (!base::DirectoryExists(source_path_)) && 51 (!base::DirectoryExists(dest_path_)) && 52 (IsFileInUse(dest_path_))) { 53 // handle overwrite_option_ = NEW_NAME_IF_IN_USE case. 54 if (alternative_path_.empty() || 55 base::PathExists(alternative_path_) || 56 !base::CopyFile(source_path_, alternative_path_)) { 57 LOG(ERROR) << "failed to copy " << source_path_.value() 58 << " to " << alternative_path_.value(); 59 return false; 60 } else { 61 copied_to_alternate_path_ = true; 62 VLOG(1) << "Copied source file " << source_path_.value() 63 << " to alternative path " << alternative_path_.value(); 64 return true; 65 } 66 } else if ((dest_exist) && 67 (overwrite_option_ == WorkItem::IF_NOT_PRESENT)) { 68 // handle overwrite_option_ = IF_NOT_PRESENT case. 69 return true; 70 } 71 72 // In all cases that reach here, move dest to a backup path. 73 if (dest_exist) { 74 if (!backup_path_.CreateUniqueTempDirUnderPath(temp_dir_)) { 75 PLOG(ERROR) << "Failed to get backup path in folder " 76 << temp_dir_.value(); 77 return false; 78 } 79 80 base::FilePath backup = backup_path_.path().Append(dest_path_.BaseName()); 81 if (base::Move(dest_path_, backup)) { 82 moved_to_backup_ = true; 83 VLOG(1) << "Moved destination " << dest_path_.value() << 84 " to backup path " << backup.value(); 85 } else { 86 LOG(ERROR) << "failed moving " << dest_path_.value() 87 << " to " << backup.value(); 88 return false; 89 } 90 } 91 92 // In all cases that reach here, copy source to destination. 93 if (base::CopyDirectory(source_path_, dest_path_, true)) { 94 copied_to_dest_path_ = true; 95 VLOG(1) << "Copied source " << source_path_.value() 96 << " to destination " << dest_path_.value(); 97 } else { 98 LOG(ERROR) << "failed copy " << source_path_.value() 99 << " to " << dest_path_.value(); 100 return false; 101 } 102 103 return true; 104} 105 106void CopyTreeWorkItem::Rollback() { 107 // Normally the delete operations below should not fail unless some 108 // programs like anti-virus are inspecting the files we just copied. 109 // If this does happen sometimes, we may consider using Move instead of 110 // Delete here. For now we just log the error and continue with the 111 // rest of rollback operation. 112 if (copied_to_dest_path_ && !base::DeleteFile(dest_path_, true)) { 113 LOG(ERROR) << "Can not delete " << dest_path_.value(); 114 } 115 if (moved_to_backup_) { 116 base::FilePath backup(backup_path_.path().Append(dest_path_.BaseName())); 117 if (!base::Move(backup, dest_path_)) { 118 LOG(ERROR) << "failed move " << backup.value() 119 << " to " << dest_path_.value(); 120 } 121 } 122 if (copied_to_alternate_path_ && 123 !base::DeleteFile(alternative_path_, true)) { 124 LOG(ERROR) << "Can not delete " << alternative_path_.value(); 125 } 126} 127 128bool CopyTreeWorkItem::IsFileInUse(const base::FilePath& path) { 129 if (!base::PathExists(path)) 130 return false; 131 132 HANDLE handle = ::CreateFile(path.value().c_str(), FILE_ALL_ACCESS, 133 NULL, NULL, OPEN_EXISTING, NULL, NULL); 134 if (handle == INVALID_HANDLE_VALUE) 135 return true; 136 137 CloseHandle(handle); 138 return false; 139} 140