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 "base/test/test_file_util.h" 6 7 #include <shlwapi.h> 8 #include <windows.h> 9 10 #include <vector> 11 12 #include "base/file_path.h" 13 #include "base/file_util.h" 14 #include "base/logging.h" 15 #include "base/win/scoped_handle.h" 16 #include "base/threading/platform_thread.h" 17 18 namespace file_util { 19 20 static const ptrdiff_t kOneMB = 1024 * 1024; 21 22 bool DieFileDie(const FilePath& file, bool recurse) { 23 // It turns out that to not induce flakiness a long timeout is needed. 24 const int kTimeoutMs = 10000; 25 26 if (!file_util::PathExists(file)) 27 return true; 28 29 // Sometimes Delete fails, so try a few more times. Divide the timeout 30 // into short chunks, so that if a try succeeds, we won't delay the test 31 // for too long. 32 for (int i = 0; i < 25; ++i) { 33 if (file_util::Delete(file, recurse)) 34 return true; 35 base::PlatformThread::Sleep(kTimeoutMs / 25); 36 } 37 return false; 38 } 39 40 bool EvictFileFromSystemCache(const FilePath& file) { 41 // Request exclusive access to the file and overwrite it with no buffering. 42 base::win::ScopedHandle file_handle( 43 CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, 44 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); 45 if (!file_handle) 46 return false; 47 48 // Get some attributes to restore later. 49 BY_HANDLE_FILE_INFORMATION bhi = {0}; 50 CHECK(::GetFileInformationByHandle(file_handle, &bhi)); 51 52 // Execute in chunks. It could be optimized. We want to do few of these since 53 // these operations will be slow without the cache. 54 55 // Allocate a buffer for the reads and the writes. 56 char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL, 57 kOneMB, 58 MEM_COMMIT | MEM_RESERVE, 59 PAGE_READWRITE)); 60 61 // If the file size isn't a multiple of kOneMB, we'll need special 62 // processing. 63 bool file_is_aligned = true; 64 int total_bytes = 0; 65 DWORD bytes_read, bytes_written; 66 for (;;) { 67 bytes_read = 0; 68 ::ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL); 69 if (bytes_read == 0) 70 break; 71 72 if (bytes_read < kOneMB) { 73 // Zero out the remaining part of the buffer. 74 // WriteFile will fail if we provide a buffer size that isn't a 75 // sector multiple, so we'll have to write the entire buffer with 76 // padded zeros and then use SetEndOfFile to truncate the file. 77 ZeroMemory(buffer + bytes_read, kOneMB - bytes_read); 78 file_is_aligned = false; 79 } 80 81 // Move back to the position we just read from. 82 // Note that SetFilePointer will also fail if total_bytes isn't sector 83 // aligned, but that shouldn't happen here. 84 DCHECK((total_bytes % kOneMB) == 0); 85 SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN); 86 if (!::WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) || 87 bytes_written != kOneMB) { 88 BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE); 89 DCHECK(freed); 90 NOTREACHED(); 91 return false; 92 } 93 94 total_bytes += bytes_read; 95 96 // If this is false, then we just processed the last portion of the file. 97 if (!file_is_aligned) 98 break; 99 } 100 101 BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE); 102 DCHECK(freed); 103 104 if (!file_is_aligned) { 105 // The size of the file isn't a multiple of 1 MB, so we'll have 106 // to open the file again, this time without the FILE_FLAG_NO_BUFFERING 107 // flag and use SetEndOfFile to mark EOF. 108 file_handle.Set(NULL); 109 file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL, 110 OPEN_EXISTING, 0, NULL)); 111 CHECK_NE(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN), 112 INVALID_SET_FILE_POINTER); 113 CHECK(::SetEndOfFile(file_handle)); 114 } 115 116 // Restore the file attributes. 117 CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, 118 &bhi.ftLastWriteTime)); 119 120 return true; 121 } 122 123 // Like CopyFileNoCache but recursively copies all files and subdirectories 124 // in the given input directory to the output directory. 125 bool CopyRecursiveDirNoCache(const FilePath& source_dir, 126 const FilePath& dest_dir) { 127 // Try to create the directory if it doesn't already exist. 128 if (!CreateDirectory(dest_dir)) { 129 if (GetLastError() != ERROR_ALREADY_EXISTS) 130 return false; 131 } 132 133 std::vector<std::wstring> files_copied; 134 135 std::wstring src(source_dir.value()); 136 file_util::AppendToPath(&src, L"*"); 137 138 WIN32_FIND_DATA fd; 139 HANDLE fh = FindFirstFile(src.c_str(), &fd); 140 if (fh == INVALID_HANDLE_VALUE) 141 return false; 142 143 do { 144 std::wstring cur_file(fd.cFileName); 145 if (cur_file == L"." || cur_file == L"..") 146 continue; // Skip these special entries. 147 148 FilePath cur_source_path = source_dir.Append(cur_file); 149 FilePath cur_dest_path = dest_dir.Append(cur_file); 150 151 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 152 // Recursively copy a subdirectory. We stripped "." and ".." already. 153 if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) { 154 FindClose(fh); 155 return false; 156 } 157 } else { 158 // Copy the file. 159 if (!::CopyFile(cur_source_path.value().c_str(), 160 cur_dest_path.value().c_str(), false)) { 161 FindClose(fh); 162 return false; 163 } 164 165 // We don't check for errors from this function, often, we are copying 166 // files that are in the repository, and they will have read-only set. 167 // This will prevent us from evicting from the cache, but these don't 168 // matter anyway. 169 EvictFileFromSystemCache(cur_dest_path); 170 } 171 } while (FindNextFile(fh, &fd)); 172 173 FindClose(fh); 174 return true; 175 } 176 177 // Checks if the volume supports Alternate Data Streams. This is required for 178 // the Zone Identifier implementation. 179 bool VolumeSupportsADS(const FilePath& path) { 180 wchar_t drive[MAX_PATH] = {0}; 181 wcscpy_s(drive, MAX_PATH, path.value().c_str()); 182 183 if (!PathStripToRootW(drive)) 184 return false; 185 186 DWORD fs_flags = 0; 187 if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) 188 return false; 189 190 if (fs_flags & FILE_NAMED_STREAMS) 191 return true; 192 193 return false; 194 } 195 196 // Return whether the ZoneIdentifier is correctly set to "Internet" (3) 197 // Only returns a valid result when called from same process as the 198 // one that (was supposed to have) set the zone identifier. 199 bool HasInternetZoneIdentifier(const FilePath& full_path) { 200 FilePath zone_path(full_path.value() + L":Zone.Identifier"); 201 std::string zone_path_contents; 202 if (!file_util::ReadFileToString(zone_path, &zone_path_contents)) 203 return false; 204 205 static const char kInternetIdentifier[] = "[ZoneTransfer]\nZoneId=3"; 206 static const size_t kInternetIdentifierSize = 207 // Don't include null byte in size of identifier. 208 arraysize(kInternetIdentifier) - 1; 209 210 // Our test is that the initial characters match the above, and that 211 // the character after the end of the string is eof, null, or newline; any 212 // of those three will invoke the Window Finder cautionary dialog. 213 return ((zone_path_contents.compare(0, kInternetIdentifierSize, 214 kInternetIdentifier) == 0) && 215 (kInternetIdentifierSize == zone_path_contents.length() || 216 zone_path_contents[kInternetIdentifierSize] == '\0' || 217 zone_path_contents[kInternetIdentifierSize] == '\n')); 218 } 219 220 std::wstring FilePathAsWString(const FilePath& path) { 221 return path.value(); 222 } 223 FilePath WStringAsFilePath(const std::wstring& path) { 224 return FilePath(path); 225 } 226 227 } // namespace file_util 228