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.
5 #include "chrome/installer/util/delete_tree_work_item.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
15 // Casts a value of an unsigned type to a signed type of the same size provided
16 // that there is no overflow.
17 template<typename L
, typename R
>
18 bool SafeCast(L left
, R
* 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()))
24 *right
= static_cast<L
>(left
);
30 DeleteTreeWorkItem::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]);
46 DeleteTreeWorkItem::~DeleteTreeWorkItem() {
49 // We first try to move key_path_ to backup_path. If it succeeds, we go ahead
51 bool 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_
);
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();
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();
74 HANDLE file
= ::CreateFile(key_file
.value().c_str(), FILE_ALL_ACCESS
,
75 FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, 0,
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
);
81 if (::GetLastError() != ERROR_FILE_NOT_FOUND
)
83 PLOG(INFO
) << "Failed to open " << key_file
.value();
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();
102 std::for_each(opened_key_files
.begin(), opened_key_files
.end(), CloseHandle
);
103 opened_key_files
.clear();
106 LOG(ERROR
) << "Could not exclusively hold all key files.";
107 return ignore_failure_
;
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();
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();
125 copied_to_backup_
= true;
129 if (!base::DeleteFile(root_path_
, true)) {
130 LOG(ERROR
) << "can not delete " << root_path_
.value();
131 return ignore_failure_
;
138 // If there are files in backup paths move them back.
139 void DeleteTreeWorkItem::Rollback() {
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_
);
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();