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 file_util { 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 base::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 base::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 base::FilePath& path, 97 void* info, size_t length) { 98 if (!info || !length) 99 return false; 100 101 PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info); 102 103 DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), 104 SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 105 NULL, NULL, &perm->dacl, NULL); 106 LocalFree(perm->security_descriptor); 107 108 char* char_array = reinterpret_cast<char*>(info); 109 delete [] char_array; 110 111 return rc == ERROR_SUCCESS; 112 } 113 114 } // namespace 115 116 bool DieFileDie(const base::FilePath& file, bool recurse) { 117 // It turns out that to not induce flakiness a long timeout is needed. 118 const int kIterations = 25; 119 const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(10) / 120 kIterations; 121 122 if (!base::PathExists(file)) 123 return true; 124 125 // Sometimes Delete fails, so try a few more times. Divide the timeout 126 // into short chunks, so that if a try succeeds, we won't delay the test 127 // for too long. 128 for (int i = 0; i < kIterations; ++i) { 129 if (base::DeleteFile(file, recurse)) 130 return true; 131 base::PlatformThread::Sleep(kTimeout); 132 } 133 return false; 134 } 135 136 bool EvictFileFromSystemCache(const base::FilePath& file) { 137 // Request exclusive access to the file and overwrite it with no buffering. 138 base::win::ScopedHandle file_handle( 139 CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, 140 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); 141 if (!file_handle) 142 return false; 143 144 // Get some attributes to restore later. 145 BY_HANDLE_FILE_INFORMATION bhi = {0}; 146 CHECK(::GetFileInformationByHandle(file_handle, &bhi)); 147 148 // Execute in chunks. It could be optimized. We want to do few of these since 149 // these operations will be slow without the cache. 150 151 // Allocate a buffer for the reads and the writes. 152 char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL, 153 kOneMB, 154 MEM_COMMIT | MEM_RESERVE, 155 PAGE_READWRITE)); 156 157 // If the file size isn't a multiple of kOneMB, we'll need special 158 // processing. 159 bool file_is_aligned = true; 160 int total_bytes = 0; 161 DWORD bytes_read, bytes_written; 162 for (;;) { 163 bytes_read = 0; 164 ::ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL); 165 if (bytes_read == 0) 166 break; 167 168 if (bytes_read < kOneMB) { 169 // Zero out the remaining part of the buffer. 170 // WriteFile will fail if we provide a buffer size that isn't a 171 // sector multiple, so we'll have to write the entire buffer with 172 // padded zeros and then use SetEndOfFile to truncate the file. 173 ZeroMemory(buffer + bytes_read, kOneMB - bytes_read); 174 file_is_aligned = false; 175 } 176 177 // Move back to the position we just read from. 178 // Note that SetFilePointer will also fail if total_bytes isn't sector 179 // aligned, but that shouldn't happen here. 180 DCHECK((total_bytes % kOneMB) == 0); 181 SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN); 182 if (!::WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) || 183 bytes_written != kOneMB) { 184 BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE); 185 DCHECK(freed); 186 NOTREACHED(); 187 return false; 188 } 189 190 total_bytes += bytes_read; 191 192 // If this is false, then we just processed the last portion of the file. 193 if (!file_is_aligned) 194 break; 195 } 196 197 BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE); 198 DCHECK(freed); 199 200 if (!file_is_aligned) { 201 // The size of the file isn't a multiple of 1 MB, so we'll have 202 // to open the file again, this time without the FILE_FLAG_NO_BUFFERING 203 // flag and use SetEndOfFile to mark EOF. 204 file_handle.Set(NULL); 205 file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL, 206 OPEN_EXISTING, 0, NULL)); 207 CHECK_NE(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN), 208 INVALID_SET_FILE_POINTER); 209 CHECK(::SetEndOfFile(file_handle)); 210 } 211 212 // Restore the file attributes. 213 CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, 214 &bhi.ftLastWriteTime)); 215 216 return true; 217 } 218 219 // Checks if the volume supports Alternate Data Streams. This is required for 220 // the Zone Identifier implementation. 221 bool VolumeSupportsADS(const base::FilePath& path) { 222 wchar_t drive[MAX_PATH] = {0}; 223 wcscpy_s(drive, MAX_PATH, path.value().c_str()); 224 225 if (!PathStripToRootW(drive)) 226 return false; 227 228 DWORD fs_flags = 0; 229 if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) 230 return false; 231 232 if (fs_flags & FILE_NAMED_STREAMS) 233 return true; 234 235 return false; 236 } 237 238 // Return whether the ZoneIdentifier is correctly set to "Internet" (3) 239 // Only returns a valid result when called from same process as the 240 // one that (was supposed to have) set the zone identifier. 241 bool HasInternetZoneIdentifier(const base::FilePath& full_path) { 242 base::FilePath zone_path(full_path.value() + L":Zone.Identifier"); 243 std::string zone_path_contents; 244 if (!file_util::ReadFileToString(zone_path, &zone_path_contents)) 245 return false; 246 247 std::vector<std::string> lines; 248 // This call also trims whitespaces, including carriage-returns (\r). 249 base::SplitString(zone_path_contents, '\n', &lines); 250 251 switch (lines.size()) { 252 case 3: 253 // optional empty line at end of file: 254 if (lines[2] != "") 255 return false; 256 // fall through: 257 case 2: 258 return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3"; 259 default: 260 return false; 261 } 262 } 263 264 std::wstring FilePathAsWString(const base::FilePath& path) { 265 return path.value(); 266 } 267 base::FilePath WStringAsFilePath(const std::wstring& path) { 268 return base::FilePath(path); 269 } 270 271 bool MakeFileUnreadable(const base::FilePath& path) { 272 return DenyFilePermission(path, GENERIC_READ); 273 } 274 275 bool MakeFileUnwritable(const base::FilePath& path) { 276 return DenyFilePermission(path, GENERIC_WRITE); 277 } 278 279 PermissionRestorer::PermissionRestorer(const base::FilePath& path) 280 : path_(path), info_(NULL), length_(0) { 281 info_ = GetPermissionInfo(path_, &length_); 282 DCHECK(info_ != NULL); 283 DCHECK_NE(0u, length_); 284 } 285 286 PermissionRestorer::~PermissionRestorer() { 287 if (!RestorePermissionInfo(path_, info_, length_)) 288 NOTREACHED(); 289 } 290 291 } // namespace file_util 292