OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/installer/util/delete_tree_work_item.h" | 5 #include "chrome/installer/util/delete_tree_work_item.h" |
6 | 6 |
| 7 #include <windows.h> |
| 8 |
| 9 #include <fstream> |
7 #include <memory> | 10 #include <memory> |
8 | 11 |
9 #include "base/files/file_path.h" | 12 #include "base/base_paths.h" |
10 #include "base/files/file_util.h" | 13 #include "base/files/file_util.h" |
11 #include "base/files/scoped_temp_dir.h" | 14 #include "base/files/scoped_temp_dir.h" |
12 #include "base/macros.h" | 15 #include "base/logging.h" |
| 16 #include "base/strings/string_util.h" |
13 #include "chrome/installer/util/work_item.h" | 17 #include "chrome/installer/util/work_item.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
15 | 19 |
16 namespace { | 20 namespace { |
17 | 21 |
18 constexpr char kTextContent[] = "delete me"; | |
19 | |
20 class DeleteTreeWorkItemTest : public testing::Test { | 22 class DeleteTreeWorkItemTest : public testing::Test { |
21 protected: | 23 protected: |
22 DeleteTreeWorkItemTest() = default; | 24 void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } |
23 | |
24 void SetUp() override { | |
25 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
26 | |
27 dir_name_ = temp_dir_.path().Append(FILE_PATH_LITERAL("to_be_deleted")); | |
28 ASSERT_TRUE(base::CreateDirectory(dir_name_)); | |
29 ASSERT_TRUE(base::PathExists(dir_name_)); | |
30 | |
31 dir_name_1_ = dir_name_.Append(FILE_PATH_LITERAL("1")); | |
32 ASSERT_TRUE(base::CreateDirectory(dir_name_1_)); | |
33 ASSERT_TRUE(base::PathExists(dir_name_1_)); | |
34 | |
35 dir_name_2_ = dir_name_.Append(FILE_PATH_LITERAL("2")); | |
36 ASSERT_TRUE(base::CreateDirectory(dir_name_2_)); | |
37 ASSERT_TRUE(base::PathExists(dir_name_2_)); | |
38 | |
39 file_name_1_ = dir_name_1_.Append(FILE_PATH_LITERAL("File_1.txt")); | |
40 ASSERT_TRUE( | |
41 base::WriteFile(file_name_1_, kTextContent, sizeof(kTextContent))); | |
42 ASSERT_TRUE(base::PathExists(file_name_1_)); | |
43 | |
44 file_name_2_ = dir_name_2_.Append(FILE_PATH_LITERAL("File_2.txt")); | |
45 ASSERT_TRUE( | |
46 base::WriteFile(file_name_2_, kTextContent, sizeof(kTextContent))); | |
47 ASSERT_TRUE(base::PathExists(file_name_2_)); | |
48 } | |
49 | |
50 void ExpectAllFilesExist() { | |
51 EXPECT_TRUE(base::PathExists(dir_name_)); | |
52 EXPECT_TRUE(base::PathExists(dir_name_1_)); | |
53 EXPECT_TRUE(base::PathExists(dir_name_2_)); | |
54 EXPECT_TRUE(base::PathExists(file_name_1_)); | |
55 EXPECT_TRUE(base::PathExists(file_name_2_)); | |
56 } | |
57 | |
58 void ExpectAllFilesDeleted() { | |
59 EXPECT_FALSE(base::PathExists(dir_name_)); | |
60 EXPECT_FALSE(base::PathExists(dir_name_1_)); | |
61 EXPECT_FALSE(base::PathExists(dir_name_2_)); | |
62 EXPECT_FALSE(base::PathExists(file_name_1_)); | |
63 EXPECT_FALSE(base::PathExists(file_name_2_)); | |
64 } | |
65 | 25 |
66 // The temporary directory used to contain the test operations. | 26 // The temporary directory used to contain the test operations. |
67 base::ScopedTempDir temp_dir_; | 27 base::ScopedTempDir temp_dir_; |
68 | |
69 base::FilePath dir_name_; | |
70 base::FilePath dir_name_1_; | |
71 base::FilePath dir_name_2_; | |
72 base::FilePath file_name_1_; | |
73 base::FilePath file_name_2_; | |
74 | |
75 DISALLOW_COPY_AND_ASSIGN(DeleteTreeWorkItemTest); | |
76 }; | 28 }; |
77 | 29 |
| 30 // Simple function to dump some text into a new file. |
| 31 void CreateTextFile(const std::wstring& filename, |
| 32 const std::wstring& contents) { |
| 33 std::ofstream file; |
| 34 file.open(filename.c_str()); |
| 35 ASSERT_TRUE(file.is_open()); |
| 36 file << contents; |
| 37 file.close(); |
| 38 } |
| 39 |
| 40 const wchar_t text_content_1[] = L"delete me"; |
| 41 |
78 } // namespace | 42 } // namespace |
79 | 43 |
80 // Delete a tree with rollback enabled and no file in use. Do() should delete | 44 // Delete a tree without key path. Everything should be deleted. |
81 // everything and Rollback() should bring back everything. | 45 TEST_F(DeleteTreeWorkItemTest, DeleteTreeNoKeyPath) { |
82 TEST_F(DeleteTreeWorkItemTest, Delete) { | 46 // Create tree to be deleted. |
| 47 base::FilePath dir_name_delete(temp_dir_.path()); |
| 48 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); |
| 49 base::CreateDirectory(dir_name_delete); |
| 50 ASSERT_TRUE(base::PathExists(dir_name_delete)); |
| 51 |
| 52 base::FilePath dir_name_delete_1(dir_name_delete); |
| 53 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); |
| 54 base::CreateDirectory(dir_name_delete_1); |
| 55 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); |
| 56 |
| 57 base::FilePath dir_name_delete_2(dir_name_delete); |
| 58 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); |
| 59 base::CreateDirectory(dir_name_delete_2); |
| 60 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); |
| 61 |
| 62 base::FilePath file_name_delete_1(dir_name_delete_1); |
| 63 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); |
| 64 CreateTextFile(file_name_delete_1.value(), text_content_1); |
| 65 ASSERT_TRUE(base::PathExists(file_name_delete_1)); |
| 66 |
| 67 base::FilePath file_name_delete_2(dir_name_delete_2); |
| 68 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); |
| 69 CreateTextFile(file_name_delete_2.value(), text_content_1); |
| 70 ASSERT_TRUE(base::PathExists(file_name_delete_2)); |
| 71 |
| 72 // Test Do(). |
83 base::ScopedTempDir temp_dir; | 73 base::ScopedTempDir temp_dir; |
84 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 74 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
85 | 75 |
| 76 std::vector<base::FilePath> key_files; |
86 std::unique_ptr<DeleteTreeWorkItem> work_item( | 77 std::unique_ptr<DeleteTreeWorkItem> work_item( |
87 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path())); | 78 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), |
88 | 79 key_files)); |
89 EXPECT_TRUE(work_item->Do()); | 80 EXPECT_TRUE(work_item->Do()); |
90 ExpectAllFilesDeleted(); | 81 |
| 82 // everything should be gone |
| 83 EXPECT_FALSE(base::PathExists(file_name_delete_1)); |
| 84 EXPECT_FALSE(base::PathExists(file_name_delete_2)); |
| 85 EXPECT_FALSE(base::PathExists(dir_name_delete)); |
91 | 86 |
92 work_item->Rollback(); | 87 work_item->Rollback(); |
93 ExpectAllFilesExist(); | 88 // everything should come back |
94 } | 89 EXPECT_TRUE(base::PathExists(file_name_delete_1)); |
95 | 90 EXPECT_TRUE(base::PathExists(file_name_delete_2)); |
96 // Delete a tree with rollback disabled and no file in use. Do() should delete | 91 EXPECT_TRUE(base::PathExists(dir_name_delete)); |
97 // everything and Rollback() shouldn't bring back anything. | 92 } |
98 TEST_F(DeleteTreeWorkItemTest, DeleteRollbackDisabled) { | 93 |
| 94 |
| 95 // Delete a tree with keypath but not in use. Everything should be gone. |
| 96 // Rollback should bring back everything |
| 97 TEST_F(DeleteTreeWorkItemTest, DeleteTree) { |
| 98 // Create tree to be deleted |
| 99 base::FilePath dir_name_delete(temp_dir_.path()); |
| 100 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); |
| 101 base::CreateDirectory(dir_name_delete); |
| 102 ASSERT_TRUE(base::PathExists(dir_name_delete)); |
| 103 |
| 104 base::FilePath dir_name_delete_1(dir_name_delete); |
| 105 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); |
| 106 base::CreateDirectory(dir_name_delete_1); |
| 107 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); |
| 108 |
| 109 base::FilePath dir_name_delete_2(dir_name_delete); |
| 110 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); |
| 111 base::CreateDirectory(dir_name_delete_2); |
| 112 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); |
| 113 |
| 114 base::FilePath file_name_delete_1(dir_name_delete_1); |
| 115 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); |
| 116 CreateTextFile(file_name_delete_1.value(), text_content_1); |
| 117 ASSERT_TRUE(base::PathExists(file_name_delete_1)); |
| 118 |
| 119 base::FilePath file_name_delete_2(dir_name_delete_2); |
| 120 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); |
| 121 CreateTextFile(file_name_delete_2.value(), text_content_1); |
| 122 ASSERT_TRUE(base::PathExists(file_name_delete_2)); |
| 123 |
| 124 // test Do() |
99 base::ScopedTempDir temp_dir; | 125 base::ScopedTempDir temp_dir; |
100 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 126 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
101 | 127 |
| 128 std::vector<base::FilePath> key_files(1, file_name_delete_1); |
102 std::unique_ptr<DeleteTreeWorkItem> work_item( | 129 std::unique_ptr<DeleteTreeWorkItem> work_item( |
103 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path())); | 130 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), |
104 work_item->set_rollback_enabled(false); | 131 key_files)); |
105 | |
106 EXPECT_TRUE(work_item->Do()); | 132 EXPECT_TRUE(work_item->Do()); |
107 ExpectAllFilesDeleted(); | 133 |
| 134 // everything should be gone |
| 135 EXPECT_FALSE(base::PathExists(file_name_delete_1)); |
| 136 EXPECT_FALSE(base::PathExists(file_name_delete_2)); |
| 137 EXPECT_FALSE(base::PathExists(dir_name_delete)); |
108 | 138 |
109 work_item->Rollback(); | 139 work_item->Rollback(); |
110 ExpectAllFilesDeleted(); | 140 // everything should come back |
111 } | 141 EXPECT_TRUE(base::PathExists(file_name_delete_1)); |
| 142 EXPECT_TRUE(base::PathExists(file_name_delete_2)); |
| 143 EXPECT_TRUE(base::PathExists(dir_name_delete)); |
| 144 } |
| 145 |
| 146 // Delete a tree with key_path in use. Everything should still be there. |
| 147 TEST_F(DeleteTreeWorkItemTest, DeleteTreeInUse) { |
| 148 // Create tree to be deleted |
| 149 base::FilePath dir_name_delete(temp_dir_.path()); |
| 150 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); |
| 151 base::CreateDirectory(dir_name_delete); |
| 152 ASSERT_TRUE(base::PathExists(dir_name_delete)); |
| 153 |
| 154 base::FilePath dir_name_delete_1(dir_name_delete); |
| 155 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); |
| 156 base::CreateDirectory(dir_name_delete_1); |
| 157 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); |
| 158 |
| 159 base::FilePath dir_name_delete_2(dir_name_delete); |
| 160 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); |
| 161 base::CreateDirectory(dir_name_delete_2); |
| 162 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); |
| 163 |
| 164 base::FilePath file_name_delete_1(dir_name_delete_1); |
| 165 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); |
| 166 CreateTextFile(file_name_delete_1.value(), text_content_1); |
| 167 ASSERT_TRUE(base::PathExists(file_name_delete_1)); |
| 168 |
| 169 base::FilePath file_name_delete_2(dir_name_delete_2); |
| 170 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); |
| 171 CreateTextFile(file_name_delete_2.value(), text_content_1); |
| 172 ASSERT_TRUE(base::PathExists(file_name_delete_2)); |
| 173 |
| 174 // Create a key path file. |
| 175 base::FilePath key_path(dir_name_delete); |
| 176 key_path = key_path.AppendASCII("key_file.exe"); |
| 177 |
| 178 wchar_t exe_full_path_str[MAX_PATH]; |
| 179 ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); |
| 180 base::FilePath exe_full_path(exe_full_path_str); |
| 181 |
| 182 base::CopyFile(exe_full_path, key_path); |
| 183 ASSERT_TRUE(base::PathExists(key_path)); |
| 184 |
| 185 VLOG(1) << "copy ourself from " << exe_full_path.value() |
| 186 << " to " << key_path.value(); |
| 187 |
| 188 // Run the key path file to keep it in use. |
| 189 STARTUPINFOW si = {sizeof(si)}; |
| 190 PROCESS_INFORMATION pi = {0}; |
| 191 base::FilePath::StringType writable_key_path = key_path.value(); |
| 192 ASSERT_TRUE( |
| 193 ::CreateProcessW(NULL, &writable_key_path[0], |
| 194 NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, |
| 195 NULL, NULL, &si, &pi)); |
| 196 |
| 197 // test Do(). |
| 198 { |
| 199 base::ScopedTempDir temp_dir; |
| 200 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 201 |
| 202 std::vector<base::FilePath> key_paths(1, key_path); |
| 203 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 204 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), |
| 205 key_paths)); |
| 206 |
| 207 // delete should fail as file in use. |
| 208 EXPECT_FALSE(work_item->Do()); |
| 209 } |
| 210 |
| 211 { |
| 212 base::ScopedTempDir temp_dir; |
| 213 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 214 |
| 215 // No key paths, the deletion should succeed. |
| 216 std::vector<base::FilePath> key_paths; |
| 217 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 218 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), |
| 219 key_paths)); |
| 220 |
| 221 EXPECT_TRUE(work_item->Do()); |
| 222 work_item->Rollback(); |
| 223 } |
| 224 |
| 225 // verify everything is still there. |
| 226 EXPECT_TRUE(base::PathExists(key_path)); |
| 227 EXPECT_TRUE(base::PathExists(file_name_delete_1)); |
| 228 EXPECT_TRUE(base::PathExists(file_name_delete_2)); |
| 229 |
| 230 TerminateProcess(pi.hProcess, 0); |
| 231 // make sure the handle is closed. |
| 232 WaitForSingleObject(pi.hProcess, INFINITE); |
| 233 } |
OLD | NEW |