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 <windows.h> 6 #include <shlobj.h> 7 8 #include "base/file_util.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/strings/string_util.h" 11 #include "base/win/registry.h" 12 #include "chrome/installer/util/delete_after_reboot_helper.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace { 16 17 // These tests exercise the Delete-After-Reboot code which requires 18 // modifications to HKLM. This will fail on Vista and above if the user 19 // is not an admin or if UAC is on. 20 // I tried using RegOverridePredefKey to test, but MoveFileEx ignore this 21 // even on 32 bit machines :-( As such, running this test may pollute 22 // your PendingFileRenameOperations value. 23 class DeleteAfterRebootHelperTest : public testing::Test { 24 protected: 25 virtual void SetUp() { 26 // Create a temporary directory for testing and fill it with some files. 27 base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir_); 28 base::CreateTemporaryFileInDir(temp_dir_, &temp_file_); 29 30 temp_subdir_ = temp_dir_.Append(L"subdir"); 31 base::CreateDirectory(temp_subdir_); 32 base::CreateTemporaryFileInDir(temp_subdir_, &temp_subdir_file_); 33 34 // Copy the current pending moves and then clear it if we can: 35 if (IsUserAnAdmin()) { 36 GetPendingMovesValue(&original_pending_moves_); 37 } 38 } 39 virtual void TearDown() { 40 // Delete the temporary directory if it's still there. 41 base::DeleteFile(temp_dir_, true); 42 43 // Try and restore the pending moves value, if we have one. 44 if (IsUserAnAdmin() && original_pending_moves_.size() > 1) { 45 base::win::RegKey session_manager_key( 46 HKEY_LOCAL_MACHINE, kSessionManagerKey, 47 KEY_CREATE_SUB_KEY | KEY_SET_VALUE); 48 if (!session_manager_key.Handle()) { 49 // Couldn't open / create the key. 50 DLOG(ERROR) << "Failed to open session manager key for writing."; 51 } 52 53 std::vector<char> buffer; 54 StringArrayToMultiSZBytes(original_pending_moves_, &buffer); 55 session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0], 56 buffer.size(), REG_MULTI_SZ); 57 } 58 } 59 60 // Compares two buffers of size len. Returns true if they are equal, 61 // false otherwise. Standard warnings about making sure the buffers 62 // are at least len chars long apply. 63 template<class Type> 64 bool CompareBuffers(Type* buf1, Type* buf2, int len) { 65 Type* comp1 = buf1; 66 Type* comp2 = buf2; 67 for (int i = 0; i < len; i++) { 68 if (*comp1 != *comp2) 69 return false; 70 comp1++; 71 comp2++; 72 } 73 return true; 74 } 75 76 // Returns the size of the given list of wstrings in bytes, including 77 // null chars, plus an additional terminating null char. 78 // e.g. the length of all the strings * sizeof(wchar_t). 79 virtual size_t WStringPairListSize( 80 const std::vector<PendingMove>& string_list) { 81 size_t length = 0; 82 std::vector<PendingMove>::const_iterator iter(string_list.begin()); 83 for (; iter != string_list.end(); ++iter) { 84 length += iter->first.size() + 1; // +1 for the null char. 85 length += iter->second.size() + 1; // +1 for the null char. 86 } 87 length++; // for the additional null char. 88 return length * sizeof(wchar_t); 89 } 90 91 std::vector<PendingMove> original_pending_moves_; 92 93 base::FilePath temp_dir_; 94 base::FilePath temp_file_; 95 base::FilePath temp_subdir_; 96 base::FilePath temp_subdir_file_; 97 }; 98 99 } // namespace 100 101 TEST_F(DeleteAfterRebootHelperTest, TestStringListToMultiSZConversions) { 102 struct StringTest { 103 wchar_t* test_name; 104 wchar_t* str; 105 DWORD length; 106 size_t count; 107 } tests[] = { 108 { L"basic", L"foo\0bar\0fee\0bee\0boo\0bong\0\0", 26 * sizeof(wchar_t), 3 }, 109 { L"empty", L"\0\0", 2 * sizeof(wchar_t), 1 }, 110 { L"deletes", L"foo\0\0bar\0\0bizz\0\0", 16 * sizeof(wchar_t), 3 }, 111 }; 112 113 for (int i = 0; i < arraysize(tests); i++) { 114 std::vector<PendingMove> string_list; 115 EXPECT_TRUE(SUCCEEDED( 116 MultiSZBytesToStringArray(reinterpret_cast<char*>(tests[i].str), 117 tests[i].length, &string_list))) 118 << tests[i].test_name; 119 EXPECT_EQ(tests[i].count, string_list.size()) << tests[i].test_name; 120 std::vector<char> buffer; 121 buffer.resize(WStringPairListSize(string_list)); 122 StringArrayToMultiSZBytes(string_list, &buffer); 123 EXPECT_TRUE(CompareBuffers(&buffer[0], 124 reinterpret_cast<char*>(tests[i].str), 125 tests[i].length)) << tests[i].test_name; 126 } 127 128 StringTest failures[] = 129 { L"malformed", reinterpret_cast<wchar_t*>("oddnumb\0\0"), 9, 1 }; 130 131 for (int i = 0; i < arraysize(failures); i++) { 132 std::vector<PendingMove> string_list; 133 EXPECT_FALSE(SUCCEEDED( 134 MultiSZBytesToStringArray(reinterpret_cast<char*>(failures[i].str), 135 failures[i].length, &string_list))) 136 << failures[i].test_name; 137 } 138 } 139 140 141 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteScheduleAndUnschedule) { 142 if (!IsUserAnAdmin()) { 143 return; 144 } 145 146 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_)); 147 148 std::vector<PendingMove> pending_moves; 149 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); 150 151 // We should see, somewhere in this key, deletion writs for 152 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. 153 EXPECT_GT(pending_moves.size(), 3U); 154 155 // Get the short form of temp_file_ and use that to match. 156 base::FilePath short_temp_file(GetShortPathName(temp_file_)); 157 158 // Scan for the first expected delete. 159 std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 160 for (; iter != pending_moves.end(); iter++) { 161 base::FilePath move_path(iter->first); 162 if (MatchPendingDeletePath(short_temp_file, move_path)) 163 break; 164 } 165 166 // Check that each of the deletes we expect are there in order. 167 base::FilePath expected_paths[] = 168 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; 169 for (int i = 0; i < arraysize(expected_paths); ++i) { 170 EXPECT_FALSE(iter == pending_moves.end()); 171 if (iter != pending_moves.end()) { 172 base::FilePath short_path_name(GetShortPathName(expected_paths[i])); 173 base::FilePath move_path(iter->first); 174 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path)); 175 ++iter; 176 } 177 } 178 179 // Test that we can remove the pending deletes. 180 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_)); 181 HRESULT hr = GetPendingMovesValue(&pending_moves); 182 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); 183 184 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); 185 for (; check_iter != pending_moves.end(); ++check_iter) { 186 base::FilePath move_path(check_iter->first); 187 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path)); 188 } 189 } 190 191 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteSchedulingWithActualDeletes) { 192 if (!IsUserAnAdmin()) { 193 return; 194 } 195 196 std::vector<PendingMove> initial_pending_moves; 197 GetPendingMovesValue(&initial_pending_moves); 198 size_t initial_pending_moves_size = initial_pending_moves.size(); 199 200 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_)); 201 202 std::vector<PendingMove> pending_moves; 203 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); 204 205 // We should see, somewhere in this key, deletion writs for 206 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. 207 EXPECT_TRUE(pending_moves.size() > 3); 208 209 // Get the short form of temp_file_ and use that to match. 210 base::FilePath short_temp_file(GetShortPathName(temp_file_)); 211 212 // Scan for the first expected delete. 213 std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 214 for (; iter != pending_moves.end(); iter++) { 215 base::FilePath move_path(iter->first); 216 if (MatchPendingDeletePath(short_temp_file, move_path)) 217 break; 218 } 219 220 // Check that each of the deletes we expect are there in order. 221 base::FilePath expected_paths[] = 222 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; 223 for (int i = 0; i < arraysize(expected_paths); ++i) { 224 EXPECT_FALSE(iter == pending_moves.end()); 225 if (iter != pending_moves.end()) { 226 base::FilePath short_path_name(GetShortPathName(expected_paths[i])); 227 base::FilePath move_path(iter->first); 228 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path)); 229 ++iter; 230 } 231 } 232 233 // Delete the temporary directory. 234 base::DeleteFile(temp_dir_, true); 235 236 // Test that we can remove the pending deletes. 237 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_)); 238 HRESULT hr = GetPendingMovesValue(&pending_moves); 239 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); 240 241 EXPECT_EQ(initial_pending_moves_size, pending_moves.size()); 242 243 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); 244 for (; check_iter != pending_moves.end(); ++check_iter) { 245 base::FilePath move_path(check_iter->first); 246 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path)); 247 } 248 } 249 250