test_file_util_win.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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 20namespace file_util { 21 22static const ptrdiff_t kOneMB = 1024 * 1024; 23 24namespace { 25 26struct PermissionInfo { 27 PSECURITY_DESCRIPTOR security_descriptor; 28 ACL dacl; 29}; 30 31// Deny |permission| on the file |path|, for the current user. 32bool 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. 70void* 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. 96bool 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 116bool 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 (!file_util::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::Delete(file, recurse)) 130 return true; 131 base::PlatformThread::Sleep(kTimeout); 132 } 133 return false; 134} 135 136bool 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. 221bool 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. 241bool 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 264std::wstring FilePathAsWString(const base::FilePath& path) { 265 return path.value(); 266} 267base::FilePath WStringAsFilePath(const std::wstring& path) { 268 return base::FilePath(path); 269} 270 271bool MakeFileUnreadable(const base::FilePath& path) { 272 return DenyFilePermission(path, GENERIC_READ); 273} 274 275bool MakeFileUnwritable(const base::FilePath& path) { 276 return DenyFilePermission(path, GENERIC_WRITE); 277} 278 279PermissionRestorer::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 286PermissionRestorer::~PermissionRestorer() { 287 if (!RestorePermissionInfo(path_, info_, length_)) 288 NOTREACHED(); 289} 290 291} // namespace file_util 292