| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/test/test_file_util.h" | 5 #include "base/test/test_file_util.h" |
| 6 | 6 |
| 7 #include <aclapi.h> | 7 #include <aclapi.h> |
| 8 #include <shlwapi.h> | 8 #include <shlwapi.h> |
| 9 #include <windows.h> | 9 #include <windows.h> |
| 10 | 10 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 static const ptrdiff_t kOneMB = 1024 * 1024; | 22 static const ptrdiff_t kOneMB = 1024 * 1024; |
| 23 | 23 |
| 24 namespace { | 24 namespace { |
| 25 | 25 |
| 26 struct PermissionInfo { | 26 struct PermissionInfo { |
| 27 PSECURITY_DESCRIPTOR security_descriptor; | 27 PSECURITY_DESCRIPTOR security_descriptor; |
| 28 ACL dacl; | 28 ACL dacl; |
| 29 }; | 29 }; |
| 30 | 30 |
| 31 // Deny |permission| on the file |path|, for the current user. | 31 // Deny |permission| on the file |path|, for the current user. |
| 32 bool DenyFilePermission(const FilePath& path, DWORD permission) { | 32 bool DenyFilePermission(const base::FilePath& path, DWORD permission) { |
| 33 PACL old_dacl; | 33 PACL old_dacl; |
| 34 PSECURITY_DESCRIPTOR security_descriptor; | 34 PSECURITY_DESCRIPTOR security_descriptor; |
| 35 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), | 35 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), |
| 36 SE_FILE_OBJECT, | 36 SE_FILE_OBJECT, |
| 37 DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, | 37 DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, |
| 38 NULL, &security_descriptor) != ERROR_SUCCESS) { | 38 NULL, &security_descriptor) != ERROR_SUCCESS) { |
| 39 return false; | 39 return false; |
| 40 } | 40 } |
| 41 | 41 |
| 42 EXPLICIT_ACCESS change; | 42 EXPLICIT_ACCESS change; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 60 NULL, NULL, new_dacl, NULL); | 60 NULL, NULL, new_dacl, NULL); |
| 61 LocalFree(security_descriptor); | 61 LocalFree(security_descriptor); |
| 62 LocalFree(new_dacl); | 62 LocalFree(new_dacl); |
| 63 | 63 |
| 64 return rc == ERROR_SUCCESS; | 64 return rc == ERROR_SUCCESS; |
| 65 } | 65 } |
| 66 | 66 |
| 67 // Gets a blob indicating the permission information for |path|. | 67 // Gets a blob indicating the permission information for |path|. |
| 68 // |length| is the length of the blob. Zero on failure. | 68 // |length| is the length of the blob. Zero on failure. |
| 69 // Returns the blob pointer, or NULL on failure. | 69 // Returns the blob pointer, or NULL on failure. |
| 70 void* GetPermissionInfo(const FilePath& path, size_t* length) { | 70 void* GetPermissionInfo(const base::FilePath& path, size_t* length) { |
| 71 DCHECK(length != NULL); | 71 DCHECK(length != NULL); |
| 72 *length = 0; | 72 *length = 0; |
| 73 PACL dacl = NULL; | 73 PACL dacl = NULL; |
| 74 PSECURITY_DESCRIPTOR security_descriptor; | 74 PSECURITY_DESCRIPTOR security_descriptor; |
| 75 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), | 75 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), |
| 76 SE_FILE_OBJECT, | 76 SE_FILE_OBJECT, |
| 77 DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, | 77 DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, |
| 78 NULL, &security_descriptor) != ERROR_SUCCESS) { | 78 NULL, &security_descriptor) != ERROR_SUCCESS) { |
| 79 return NULL; | 79 return NULL; |
| 80 } | 80 } |
| 81 DCHECK(dacl != NULL); | 81 DCHECK(dacl != NULL); |
| 82 | 82 |
| 83 *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize; | 83 *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize; |
| 84 PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]); | 84 PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]); |
| 85 info->security_descriptor = security_descriptor; | 85 info->security_descriptor = security_descriptor; |
| 86 memcpy(&info->dacl, dacl, dacl->AclSize); | 86 memcpy(&info->dacl, dacl, dacl->AclSize); |
| 87 | 87 |
| 88 return info; | 88 return info; |
| 89 } | 89 } |
| 90 | 90 |
| 91 // Restores the permission information for |path|, given the blob retrieved | 91 // Restores the permission information for |path|, given the blob retrieved |
| 92 // using |GetPermissionInfo()|. | 92 // using |GetPermissionInfo()|. |
| 93 // |info| is the pointer to the blob. | 93 // |info| is the pointer to the blob. |
| 94 // |length| is the length of the blob. | 94 // |length| is the length of the blob. |
| 95 // Either |info| or |length| may be NULL/0, in which case nothing happens. | 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) { | 96 bool RestorePermissionInfo(const base::FilePath& path, |
| 97 void* info, size_t length) { |
| 97 if (!info || !length) | 98 if (!info || !length) |
| 98 return false; | 99 return false; |
| 99 | 100 |
| 100 PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info); | 101 PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info); |
| 101 | 102 |
| 102 DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), | 103 DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()), |
| 103 SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, | 104 SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, |
| 104 NULL, NULL, &perm->dacl, NULL); | 105 NULL, NULL, &perm->dacl, NULL); |
| 105 LocalFree(perm->security_descriptor); | 106 LocalFree(perm->security_descriptor); |
| 106 | 107 |
| 107 char* char_array = reinterpret_cast<char*>(info); | 108 char* char_array = reinterpret_cast<char*>(info); |
| 108 delete [] char_array; | 109 delete [] char_array; |
| 109 | 110 |
| 110 return rc == ERROR_SUCCESS; | 111 return rc == ERROR_SUCCESS; |
| 111 } | 112 } |
| 112 | 113 |
| 113 } // namespace | 114 } // namespace |
| 114 | 115 |
| 115 bool DieFileDie(const FilePath& file, bool recurse) { | 116 bool DieFileDie(const base::FilePath& file, bool recurse) { |
| 116 // It turns out that to not induce flakiness a long timeout is needed. | 117 // It turns out that to not induce flakiness a long timeout is needed. |
| 117 const int kIterations = 25; | 118 const int kIterations = 25; |
| 118 const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(10) / | 119 const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(10) / |
| 119 kIterations; | 120 kIterations; |
| 120 | 121 |
| 121 if (!file_util::PathExists(file)) | 122 if (!file_util::PathExists(file)) |
| 122 return true; | 123 return true; |
| 123 | 124 |
| 124 // Sometimes Delete fails, so try a few more times. Divide the timeout | 125 // Sometimes Delete fails, so try a few more times. Divide the timeout |
| 125 // into short chunks, so that if a try succeeds, we won't delay the test | 126 // into short chunks, so that if a try succeeds, we won't delay the test |
| 126 // for too long. | 127 // for too long. |
| 127 for (int i = 0; i < kIterations; ++i) { | 128 for (int i = 0; i < kIterations; ++i) { |
| 128 if (file_util::Delete(file, recurse)) | 129 if (file_util::Delete(file, recurse)) |
| 129 return true; | 130 return true; |
| 130 base::PlatformThread::Sleep(kTimeout); | 131 base::PlatformThread::Sleep(kTimeout); |
| 131 } | 132 } |
| 132 return false; | 133 return false; |
| 133 } | 134 } |
| 134 | 135 |
| 135 bool EvictFileFromSystemCache(const FilePath& file) { | 136 bool EvictFileFromSystemCache(const base::FilePath& file) { |
| 136 // Request exclusive access to the file and overwrite it with no buffering. | 137 // Request exclusive access to the file and overwrite it with no buffering. |
| 137 base::win::ScopedHandle file_handle( | 138 base::win::ScopedHandle file_handle( |
| 138 CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, | 139 CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, |
| 139 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); | 140 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); |
| 140 if (!file_handle) | 141 if (!file_handle) |
| 141 return false; | 142 return false; |
| 142 | 143 |
| 143 // Get some attributes to restore later. | 144 // Get some attributes to restore later. |
| 144 BY_HANDLE_FILE_INFORMATION bhi = {0}; | 145 BY_HANDLE_FILE_INFORMATION bhi = {0}; |
| 145 CHECK(::GetFileInformationByHandle(file_handle, &bhi)); | 146 CHECK(::GetFileInformationByHandle(file_handle, &bhi)); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 | 211 |
| 211 // Restore the file attributes. | 212 // Restore the file attributes. |
| 212 CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, | 213 CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, |
| 213 &bhi.ftLastWriteTime)); | 214 &bhi.ftLastWriteTime)); |
| 214 | 215 |
| 215 return true; | 216 return true; |
| 216 } | 217 } |
| 217 | 218 |
| 218 // Like CopyFileNoCache but recursively copies all files and subdirectories | 219 // Like CopyFileNoCache but recursively copies all files and subdirectories |
| 219 // in the given input directory to the output directory. | 220 // in the given input directory to the output directory. |
| 220 bool CopyRecursiveDirNoCache(const FilePath& source_dir, | 221 bool CopyRecursiveDirNoCache(const base::FilePath& source_dir, |
| 221 const FilePath& dest_dir) { | 222 const base::FilePath& dest_dir) { |
| 222 // Try to create the directory if it doesn't already exist. | 223 // Try to create the directory if it doesn't already exist. |
| 223 if (!CreateDirectory(dest_dir)) { | 224 if (!CreateDirectory(dest_dir)) { |
| 224 if (GetLastError() != ERROR_ALREADY_EXISTS) | 225 if (GetLastError() != ERROR_ALREADY_EXISTS) |
| 225 return false; | 226 return false; |
| 226 } | 227 } |
| 227 | 228 |
| 228 std::vector<std::wstring> files_copied; | 229 std::vector<std::wstring> files_copied; |
| 229 | 230 |
| 230 FilePath src(source_dir.AppendASCII("*")); | 231 base::FilePath src(source_dir.AppendASCII("*")); |
| 231 | 232 |
| 232 WIN32_FIND_DATA fd; | 233 WIN32_FIND_DATA fd; |
| 233 HANDLE fh = FindFirstFile(src.value().c_str(), &fd); | 234 HANDLE fh = FindFirstFile(src.value().c_str(), &fd); |
| 234 if (fh == INVALID_HANDLE_VALUE) | 235 if (fh == INVALID_HANDLE_VALUE) |
| 235 return false; | 236 return false; |
| 236 | 237 |
| 237 do { | 238 do { |
| 238 std::wstring cur_file(fd.cFileName); | 239 std::wstring cur_file(fd.cFileName); |
| 239 if (cur_file == L"." || cur_file == L"..") | 240 if (cur_file == L"." || cur_file == L"..") |
| 240 continue; // Skip these special entries. | 241 continue; // Skip these special entries. |
| 241 | 242 |
| 242 FilePath cur_source_path = source_dir.Append(cur_file); | 243 base::FilePath cur_source_path = source_dir.Append(cur_file); |
| 243 FilePath cur_dest_path = dest_dir.Append(cur_file); | 244 base::FilePath cur_dest_path = dest_dir.Append(cur_file); |
| 244 | 245 |
| 245 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | 246 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { |
| 246 // Recursively copy a subdirectory. We stripped "." and ".." already. | 247 // Recursively copy a subdirectory. We stripped "." and ".." already. |
| 247 if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) { | 248 if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) { |
| 248 FindClose(fh); | 249 FindClose(fh); |
| 249 return false; | 250 return false; |
| 250 } | 251 } |
| 251 } else { | 252 } else { |
| 252 // Copy the file. | 253 // Copy the file. |
| 253 if (!::CopyFile(cur_source_path.value().c_str(), | 254 if (!::CopyFile(cur_source_path.value().c_str(), |
| 254 cur_dest_path.value().c_str(), false)) { | 255 cur_dest_path.value().c_str(), false)) { |
| 255 FindClose(fh); | 256 FindClose(fh); |
| 256 return false; | 257 return false; |
| 257 } | 258 } |
| 258 | 259 |
| 259 // We don't check for errors from this function, often, we are copying | 260 // We don't check for errors from this function, often, we are copying |
| 260 // files that are in the repository, and they will have read-only set. | 261 // files that are in the repository, and they will have read-only set. |
| 261 // This will prevent us from evicting from the cache, but these don't | 262 // This will prevent us from evicting from the cache, but these don't |
| 262 // matter anyway. | 263 // matter anyway. |
| 263 EvictFileFromSystemCache(cur_dest_path); | 264 EvictFileFromSystemCache(cur_dest_path); |
| 264 } | 265 } |
| 265 } while (FindNextFile(fh, &fd)); | 266 } while (FindNextFile(fh, &fd)); |
| 266 | 267 |
| 267 FindClose(fh); | 268 FindClose(fh); |
| 268 return true; | 269 return true; |
| 269 } | 270 } |
| 270 | 271 |
| 271 // Checks if the volume supports Alternate Data Streams. This is required for | 272 // Checks if the volume supports Alternate Data Streams. This is required for |
| 272 // the Zone Identifier implementation. | 273 // the Zone Identifier implementation. |
| 273 bool VolumeSupportsADS(const FilePath& path) { | 274 bool VolumeSupportsADS(const base::FilePath& path) { |
| 274 wchar_t drive[MAX_PATH] = {0}; | 275 wchar_t drive[MAX_PATH] = {0}; |
| 275 wcscpy_s(drive, MAX_PATH, path.value().c_str()); | 276 wcscpy_s(drive, MAX_PATH, path.value().c_str()); |
| 276 | 277 |
| 277 if (!PathStripToRootW(drive)) | 278 if (!PathStripToRootW(drive)) |
| 278 return false; | 279 return false; |
| 279 | 280 |
| 280 DWORD fs_flags = 0; | 281 DWORD fs_flags = 0; |
| 281 if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) | 282 if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) |
| 282 return false; | 283 return false; |
| 283 | 284 |
| 284 if (fs_flags & FILE_NAMED_STREAMS) | 285 if (fs_flags & FILE_NAMED_STREAMS) |
| 285 return true; | 286 return true; |
| 286 | 287 |
| 287 return false; | 288 return false; |
| 288 } | 289 } |
| 289 | 290 |
| 290 // Return whether the ZoneIdentifier is correctly set to "Internet" (3) | 291 // Return whether the ZoneIdentifier is correctly set to "Internet" (3) |
| 291 // Only returns a valid result when called from same process as the | 292 // Only returns a valid result when called from same process as the |
| 292 // one that (was supposed to have) set the zone identifier. | 293 // one that (was supposed to have) set the zone identifier. |
| 293 bool HasInternetZoneIdentifier(const FilePath& full_path) { | 294 bool HasInternetZoneIdentifier(const base::FilePath& full_path) { |
| 294 FilePath zone_path(full_path.value() + L":Zone.Identifier"); | 295 base::FilePath zone_path(full_path.value() + L":Zone.Identifier"); |
| 295 std::string zone_path_contents; | 296 std::string zone_path_contents; |
| 296 if (!file_util::ReadFileToString(zone_path, &zone_path_contents)) | 297 if (!file_util::ReadFileToString(zone_path, &zone_path_contents)) |
| 297 return false; | 298 return false; |
| 298 | 299 |
| 299 std::vector<std::string> lines; | 300 std::vector<std::string> lines; |
| 300 // This call also trims whitespaces, including carriage-returns (\r). | 301 // This call also trims whitespaces, including carriage-returns (\r). |
| 301 base::SplitString(zone_path_contents, '\n', &lines); | 302 base::SplitString(zone_path_contents, '\n', &lines); |
| 302 | 303 |
| 303 switch (lines.size()) { | 304 switch (lines.size()) { |
| 304 case 3: | 305 case 3: |
| 305 // optional empty line at end of file: | 306 // optional empty line at end of file: |
| 306 if (lines[2] != "") | 307 if (lines[2] != "") |
| 307 return false; | 308 return false; |
| 308 // fall through: | 309 // fall through: |
| 309 case 2: | 310 case 2: |
| 310 return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3"; | 311 return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3"; |
| 311 default: | 312 default: |
| 312 return false; | 313 return false; |
| 313 } | 314 } |
| 314 } | 315 } |
| 315 | 316 |
| 316 std::wstring FilePathAsWString(const FilePath& path) { | 317 std::wstring FilePathAsWString(const base::FilePath& path) { |
| 317 return path.value(); | 318 return path.value(); |
| 318 } | 319 } |
| 319 FilePath WStringAsFilePath(const std::wstring& path) { | 320 base::FilePath WStringAsFilePath(const std::wstring& path) { |
| 320 return FilePath(path); | 321 return base::FilePath(path); |
| 321 } | 322 } |
| 322 | 323 |
| 323 bool MakeFileUnreadable(const FilePath& path) { | 324 bool MakeFileUnreadable(const base::FilePath& path) { |
| 324 return DenyFilePermission(path, GENERIC_READ); | 325 return DenyFilePermission(path, GENERIC_READ); |
| 325 } | 326 } |
| 326 | 327 |
| 327 bool MakeFileUnwritable(const FilePath& path) { | 328 bool MakeFileUnwritable(const base::FilePath& path) { |
| 328 return DenyFilePermission(path, GENERIC_WRITE); | 329 return DenyFilePermission(path, GENERIC_WRITE); |
| 329 } | 330 } |
| 330 | 331 |
| 331 PermissionRestorer::PermissionRestorer(const FilePath& path) | 332 PermissionRestorer::PermissionRestorer(const base::FilePath& path) |
| 332 : path_(path), info_(NULL), length_(0) { | 333 : path_(path), info_(NULL), length_(0) { |
| 333 info_ = GetPermissionInfo(path_, &length_); | 334 info_ = GetPermissionInfo(path_, &length_); |
| 334 DCHECK(info_ != NULL); | 335 DCHECK(info_ != NULL); |
| 335 DCHECK_NE(0u, length_); | 336 DCHECK_NE(0u, length_); |
| 336 } | 337 } |
| 337 | 338 |
| 338 PermissionRestorer::~PermissionRestorer() { | 339 PermissionRestorer::~PermissionRestorer() { |
| 339 if (!RestorePermissionInfo(path_, info_, length_)) | 340 if (!RestorePermissionInfo(path_, info_, length_)) |
| 340 NOTREACHED(); | 341 NOTREACHED(); |
| 341 } | 342 } |
| 342 | 343 |
| 343 } // namespace file_util | 344 } // namespace file_util |
| OLD | NEW |