Home | History | Annotate | Download | only in util
      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/files/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     const wchar_t* test_name;
    104     const 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<const char*>(tests[i].str),
    117                                   tests[i].length,
    118                                   &string_list)))
    119         << tests[i].test_name;
    120     EXPECT_EQ(tests[i].count, string_list.size()) << tests[i].test_name;
    121     std::vector<char> buffer;
    122     buffer.resize(WStringPairListSize(string_list));
    123     StringArrayToMultiSZBytes(string_list, &buffer);
    124     EXPECT_TRUE(CompareBuffers(const_cast<const char*>(&buffer[0]),
    125                                reinterpret_cast<const char*>(tests[i].str),
    126                                tests[i].length))
    127         << tests[i].test_name;
    128   }
    129 
    130   StringTest failures[] = {
    131       L"malformed", reinterpret_cast<const wchar_t*>("oddnumb\0\0"), 9, 1};
    132 
    133   for (int i = 0; i < arraysize(failures); i++) {
    134     std::vector<PendingMove> string_list;
    135     EXPECT_FALSE(SUCCEEDED(MultiSZBytesToStringArray(
    136         reinterpret_cast<const char*>(failures[i].str),
    137         failures[i].length,
    138         &string_list)))
    139         << failures[i].test_name;
    140   }
    141 }
    142 
    143 
    144 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteScheduleAndUnschedule) {
    145   if (!IsUserAnAdmin()) {
    146     return;
    147   }
    148 
    149   EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_));
    150 
    151   std::vector<PendingMove> pending_moves;
    152   EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves)));
    153 
    154   // We should see, somewhere in this key, deletion writs for
    155   // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
    156   EXPECT_GT(pending_moves.size(), 3U);
    157 
    158   // Get the short form of temp_file_ and use that to match.
    159   base::FilePath short_temp_file(GetShortPathName(temp_file_));
    160 
    161   // Scan for the first expected delete.
    162   std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
    163   for (; iter != pending_moves.end(); iter++) {
    164     base::FilePath move_path(iter->first);
    165     if (MatchPendingDeletePath(short_temp_file, move_path))
    166       break;
    167   }
    168 
    169   // Check that each of the deletes we expect are there in order.
    170   base::FilePath expected_paths[] =
    171       { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ };
    172   for (int i = 0; i < arraysize(expected_paths); ++i) {
    173     EXPECT_FALSE(iter == pending_moves.end());
    174     if (iter != pending_moves.end()) {
    175       base::FilePath short_path_name(GetShortPathName(expected_paths[i]));
    176       base::FilePath move_path(iter->first);
    177       EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path));
    178       ++iter;
    179     }
    180   }
    181 
    182   // Test that we can remove the pending deletes.
    183   EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_));
    184   HRESULT hr = GetPendingMovesValue(&pending_moves);
    185   EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
    186 
    187   std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin());
    188   for (; check_iter != pending_moves.end(); ++check_iter) {
    189     base::FilePath move_path(check_iter->first);
    190     EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path));
    191   }
    192 }
    193 
    194 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteSchedulingWithActualDeletes) {
    195   if (!IsUserAnAdmin()) {
    196     return;
    197   }
    198 
    199   std::vector<PendingMove> initial_pending_moves;
    200   GetPendingMovesValue(&initial_pending_moves);
    201   size_t initial_pending_moves_size = initial_pending_moves.size();
    202 
    203   EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_));
    204 
    205   std::vector<PendingMove> pending_moves;
    206   EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves)));
    207 
    208   // We should see, somewhere in this key, deletion writs for
    209   // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
    210   EXPECT_TRUE(pending_moves.size() > 3);
    211 
    212   // Get the short form of temp_file_ and use that to match.
    213   base::FilePath short_temp_file(GetShortPathName(temp_file_));
    214 
    215   // Scan for the first expected delete.
    216   std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
    217   for (; iter != pending_moves.end(); iter++) {
    218     base::FilePath move_path(iter->first);
    219     if (MatchPendingDeletePath(short_temp_file, move_path))
    220       break;
    221   }
    222 
    223   // Check that each of the deletes we expect are there in order.
    224   base::FilePath expected_paths[] =
    225       { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ };
    226   for (int i = 0; i < arraysize(expected_paths); ++i) {
    227     EXPECT_FALSE(iter == pending_moves.end());
    228     if (iter != pending_moves.end()) {
    229       base::FilePath short_path_name(GetShortPathName(expected_paths[i]));
    230       base::FilePath move_path(iter->first);
    231       EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path));
    232       ++iter;
    233     }
    234   }
    235 
    236   // Delete the temporary directory.
    237   base::DeleteFile(temp_dir_, true);
    238 
    239   // Test that we can remove the pending deletes.
    240   EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_));
    241   HRESULT hr = GetPendingMovesValue(&pending_moves);
    242   EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
    243 
    244   EXPECT_EQ(initial_pending_moves_size, pending_moves.size());
    245 
    246   std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin());
    247   for (; check_iter != pending_moves.end(); ++check_iter) {
    248     base::FilePath move_path(check_iter->first);
    249     EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path));
    250   }
    251 }
    252 
    253