1 // Copyright (c) 2012 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 <windows.h> 8 #include <aclapi.h> 9 #include <shlwapi.h> 10 11 #include <vector> 12 13 #include "base/file_util.h" 14 #include "base/files/file_path.h" 15 #include "base/logging.h" 16 #include "base/strings/string_split.h" 17 #include "base/threading/platform_thread.h" 18 #include "base/win/scoped_handle.h" 19 20 namespace base { 21 22 static const ptrdiff_t kOneMB = 1024 * 1024; 23 24 namespace { 25 26 struct PermissionInfo { 27 PSECURITY_DESCRIPTOR security_descriptor; 28 ACL dacl; 29 }; 30 31 // Deny |permission| on the file |path|, for the current user. 32 bool DenyFilePermission(const FilePath& path, DWORD permission) { 33 PACL old_dacl; 34 PSECURITY_DESCRIPTOR security_descriptor; 35 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), 36 SE_FILE_OBJECT, 37 DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, 38 NULL, &security_descriptor) != ERROR_SUCCESS) { 39 return false; 40 } 41 42 EXPLICIT_ACCESS change; 43 change.grfAccessPermissions = permission; 44 change.grfAccessMode = DENY_ACCESS; 45 change.grfInheritance = 0; 46 change.Trustee.pMultipleTrustee = NULL; 47 change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; 48 change.Trustee.TrusteeForm = TRUSTEE_IS_NAME; 49 change.Trustee.TrusteeType = TRUSTEE_IS_USER; 50 change.Trustee.ptstrName = L"CURRENT_USER"; 51 52 PACL new_dacl; 53 if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) { 54 LocalFree(security_descriptor); 55 return false; 56 } 57 58 DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), 59 SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 60 NULL, NULL, new_dacl, NULL); 61 LocalFree(security_descriptor); 62 LocalFree(new_dacl); 63 64 return rc == ERROR_SUCCESS; 65 } 66 67 // Gets a blob indicating the permission information for |path|. 68 // |length| is the length of the blob. Zero on failure. 69 // Returns the blob pointer, or NULL on failure. 70 void* GetPermissionInfo(const FilePath& path, size_t* length) { 71 DCHECK(length != NULL); 72 *length = 0; 73 PACL dacl = NULL; 74 PSECURITY_DESCRIPTOR security_descriptor; 75 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), 76 SE_FILE_OBJECT, 77 DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, 78 NULL, &security_descriptor) != ERROR_SUCCESS) { 79 return NULL; 80 } 81 DCHECK(dacl != NULL); 82 83 *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize; 84 PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]); 85 info->security_descriptor = security_descriptor; 86 memcpy(&info->dacl, dacl, dacl->AclSize); 87 88 return info; 89 } 90 91 // Restores the permission information for |path|, given the blob retrieved 92 // using |GetPermissionInfo()|. 93 // |info| is the pointer to the blob. 94 // |length| is the length of the blob. 95 // Either |info| or |length| may be NULL/0, in which case nothing happens. 96 bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) { 97 if (!info || !length) 98 return false; 99 100 PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info); 101 102 DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), 103 SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 104 NULL, NULL, &perm->dacl, NULL); 105 LocalFree(perm->security_descriptor); 106 107 char* char_array = reinterpret_cast<char*>(info); 108 delete [] char_array; 109 110 return rc == ERROR_SUCCESS; 111 } 112 113 } // namespace 114 115 bool DieFileDie(const FilePath& file, bool recurse) { 116 // It turns out that to not induce flakiness a long timeout is needed. 117 const int kIterations = 25; 118 const TimeDelta kTimeout = TimeDelta::FromSeconds(10) / kIterations; 119 120 if (!PathExists(file)) 121 return true; 122 123 // Sometimes Delete fails, so try a few more times. Divide the timeout 124 // into short chunks, so that if a try succeeds, we won't delay the test 125 // for too long. 126 for (int i = 0; i < kIterations; ++i) { 127 if (DeleteFile(file, recurse)) 128 return true; 129 PlatformThread::Sleep(kTimeout); 130 } 131 return false; 132 } 133 134 bool EvictFileFromSystemCache(const FilePath& file) { 135 // Request exclusive access to the file and overwrite it with no buffering. 136 base::win::ScopedHandle file_handle( 137 CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, 138 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); 139 if (!file_handle) 140 return false; 141 142 // Get some attributes to restore later. 143 BY_HANDLE_FILE_INFORMATION bhi = {0}; 144 CHECK(::GetFileInformationByHandle(file_handle, &bhi)); 145 146 // Execute in chunks. It could be optimized. We want to do few of these since 147 // these operations will be slow without the cache. 148 149 // Allocate a buffer for the reads and the writes. 150 char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL, 151 kOneMB, 152 MEM_COMMIT | MEM_RESERVE, 153 PAGE_READWRITE)); 154 155 // If the file size isn't a multiple of kOneMB, we'll need special 156 // processing. 157 bool file_is_aligned = true; 158 int total_bytes = 0; 159 DWORD bytes_read, bytes_written; 160 for (;;) { 161 bytes_read = 0; 162 ::ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL); 163 if (bytes_read == 0) 164 break; 165 166 if (bytes_read < kOneMB) { 167 // Zero out the remaining part of the buffer. 168 // WriteFile will fail if we provide a buffer size that isn't a 169 // sector multiple, so we'll have to write the entire buffer with 170 // padded zeros and then use SetEndOfFile to truncate the file. 171 ZeroMemory(buffer + bytes_read, kOneMB - bytes_read); 172 file_is_aligned = false; 173 } 174 175 // Move back to the position we just read from. 176 // Note that SetFilePointer will also fail if total_bytes isn't sector 177 // aligned, but that shouldn't happen here. 178 DCHECK((total_bytes % kOneMB) == 0); 179 SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN); 180 if (!::WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) || 181 bytes_written != kOneMB) { 182 BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE); 183 DCHECK(freed); 184 NOTREACHED(); 185 return false; 186 } 187 188 total_bytes += bytes_read; 189 190 // If this is false, then we just processed the last portion of the file. 191 if (!file_is_aligned) 192 break; 193 } 194 195 BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE); 196 DCHECK(freed); 197 198 if (!file_is_aligned) { 199 // The size of the file isn't a multiple of 1 MB, so we'll have 200 // to open the file again, this time without the FILE_FLAG_NO_BUFFERING 201 // flag and use SetEndOfFile to mark EOF. 202 file_handle.Set(NULL); 203 file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL, 204 OPEN_EXISTING, 0, NULL)); 205 CHECK_NE(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN), 206 INVALID_SET_FILE_POINTER); 207 CHECK(::SetEndOfFile(file_handle)); 208 } 209 210 // Restore the file attributes. 211 CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, 212 &bhi.ftLastWriteTime)); 213 214 return true; 215 } 216 217 // Checks if the volume supports Alternate Data Streams. This is required for 218 // the Zone Identifier implementation. 219 bool VolumeSupportsADS(const FilePath& path) { 220 wchar_t drive[MAX_PATH] = {0}; 221 wcscpy_s(drive, MAX_PATH, path.value().c_str()); 222 223 if (!PathStripToRootW(drive)) 224 return false; 225 226 DWORD fs_flags = 0; 227 if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) 228 return false; 229 230 if (fs_flags & FILE_NAMED_STREAMS) 231 return true; 232 233 return false; 234 } 235 236 // Return whether the ZoneIdentifier is correctly set to "Internet" (3) 237 // Only returns a valid result when called from same process as the 238 // one that (was supposed to have) set the zone identifier. 239 bool HasInternetZoneIdentifier(const FilePath& full_path) { 240 FilePath zone_path(full_path.value() + L":Zone.Identifier"); 241 std::string zone_path_contents; 242 if (!ReadFileToString(zone_path, &zone_path_contents)) 243 return false; 244 245 std::vector<std::string> lines; 246 // This call also trims whitespaces, including carriage-returns (\r). 247 SplitString(zone_path_contents, '\n', &lines); 248 249 switch (lines.size()) { 250 case 3: 251 // optional empty line at end of file: 252 if (lines[2] != "") 253 return false; 254 // fall through: 255 case 2: 256 return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3"; 257 default: 258 return false; 259 } 260 } 261 262 } // namespace base 263 264 namespace file_util { 265 266 using base::DenyFilePermission; 267 using base::GetPermissionInfo; 268 using base::RestorePermissionInfo; 269 270 std::wstring FilePathAsWString(const base::FilePath& path) { 271 return path.value(); 272 } 273 base::FilePath WStringAsFilePath(const std::wstring& path) { 274 return base::FilePath(path); 275 } 276 277 bool MakeFileUnreadable(const base::FilePath& path) { 278 return DenyFilePermission(path, GENERIC_READ); 279 } 280 281 bool MakeFileUnwritable(const base::FilePath& path) { 282 return DenyFilePermission(path, GENERIC_WRITE); 283 } 284 285 PermissionRestorer::PermissionRestorer(const base::FilePath& path) 286 : path_(path), info_(NULL), length_(0) { 287 info_ = GetPermissionInfo(path_, &length_); 288 DCHECK(info_ != NULL); 289 DCHECK_NE(0u, length_); 290 } 291 292 PermissionRestorer::~PermissionRestorer() { 293 if (!RestorePermissionInfo(path_, info_, length_)) 294 NOTREACHED(); 295 } 296 297 } // namespace file_util 298