| OLD | NEW |
| (Empty) |
| 1 // Copyright 2003-2009 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 // File handling routines | |
| 16 // | |
| 17 // Possible performance improvement: make a subclass or alternate class | |
| 18 // that reads an entire file into memory and fulfills read/write requests | |
| 19 // in memory (or equivalently, use a memory mapped file). This can greatly | |
| 20 // improve performance if there are files we do a lot of read/write requests on. | |
| 21 // | |
| 22 // We generally are dealing with files that can be very large and want to | |
| 23 // minimize memory usage, so this is not high priority | |
| 24 // | |
| 25 // this has the beginnings of asynchronous access support | |
| 26 // | |
| 27 // Unfortunately, doing asynchronous reads with FILE_FLAG_OVERLAPPED buys us | |
| 28 // nothing because the system cache manager enforces serial requests. | |
| 29 // | |
| 30 // Hence, we need to also use FILE_FLAG_NO_BUFFERING. this has a number of | |
| 31 // constraints: | |
| 32 // - file read/write position must be aligned on multiples of the disk sector | |
| 33 // size | |
| 34 // - file read/write length must be a multiple of the sector size | |
| 35 // - read/write buffer must be aligned on multiples of the disk sector size | |
| 36 // | |
| 37 // In particular, this means that we cannot write 8 bytes, for example, because | |
| 38 // we have to write an entire sector. | |
| 39 // | |
| 40 // Currently, the implementation only supports enough to do some simple read | |
| 41 // tests | |
| 42 // | |
| 43 // The general idea is code that wants to to a sequence of asynchronous actions | |
| 44 // will look like the following, for an example of reading multiple event | |
| 45 // records asynchronously: | |
| 46 // | |
| 47 // uint32 async_id = File::GetNextAsyncId() | |
| 48 // while (!done) { | |
| 49 // for (everything_to_do, e.g., for each event to read) { | |
| 50 // call File::Read to read items needed; | |
| 51 // returns TR_E_FILE_ASYNC_PENDING if queued; or returns data if done | |
| 52 // process the item (e.g., event) if desired | |
| 53 // } | |
| 54 // call some routine to process pending completions; initiate delayed action | |
| 55 // } | |
| 56 // call some cleanup routine | |
| 57 | |
| 58 #include "omaha/base/file.h" | |
| 59 #include <algorithm> | |
| 60 #include "base/scoped_ptr.h" | |
| 61 #include "omaha/base/app_util.h" | |
| 62 #include "omaha/base/const_config.h" | |
| 63 #include "omaha/base/debug.h" | |
| 64 #include "omaha/base/error.h" | |
| 65 #include "omaha/base/logging.h" | |
| 66 #include "omaha/base/path.h" | |
| 67 #include "omaha/base/reg_key.h" | |
| 68 #include "omaha/base/scoped_any.h" | |
| 69 #include "omaha/base/scoped_ptr_address.h" | |
| 70 #include "omaha/base/string.h" | |
| 71 #include "omaha/base/system.h" | |
| 72 #include "omaha/base/timer.h" | |
| 73 #include "omaha/base/utils.h" | |
| 74 | |
| 75 namespace omaha { | |
| 76 | |
| 77 // Constants | |
| 78 const uint32 kZeroSize = 4096; // Buffer size used for clearing data in a file. | |
| 79 | |
| 80 // The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the | |
| 81 // registry. | |
| 82 static const TCHAR* kSessionManagerKey = | |
| 83 _T("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager"); | |
| 84 static const TCHAR* kPendingFileRenameOps = _T("PendingFileRenameOperations"); | |
| 85 | |
| 86 File::File() | |
| 87 : handle_(INVALID_HANDLE_VALUE), read_only_(false), sequence_id_(0) { | |
| 88 } | |
| 89 | |
| 90 File::~File() { | |
| 91 if (handle_ != INVALID_HANDLE_VALUE) { | |
| 92 VERIFY1(SUCCEEDED(Close())); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 // open for reading only if write == false, otherwise both reading and writing | |
| 97 // allow asynchronous operations if async == true. Use this function when you | |
| 98 // need exclusive access to the file. | |
| 99 HRESULT File::Open(const TCHAR* file_name, bool write, bool async) { | |
| 100 return OpenShareMode(file_name, write, async, 0); | |
| 101 } | |
| 102 | |
| 103 // Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise, | |
| 104 // this is identical to File::Open(). | |
| 105 HRESULT File::OpenShareMode(const TCHAR* file_name, | |
| 106 bool write, | |
| 107 bool async, | |
| 108 DWORD share_mode) { | |
| 109 ASSERT1(file_name && *file_name); | |
| 110 ASSERT1(handle_ == INVALID_HANDLE_VALUE); | |
| 111 VERIFY1(!async); | |
| 112 | |
| 113 file_name_ = file_name; | |
| 114 | |
| 115 // there are restrictions on what we can do if using FILE_FLAG_NO_BUFFERING | |
| 116 // if (!buffer) { flags |= FILE_FLAG_NO_BUFFERING; } | |
| 117 // FILE_FLAG_WRITE_THROUGH | |
| 118 // how efficient is NTFS encryption? FILE_ATTRIBUTE_ENCRYPTED | |
| 119 // FILE_ATTRIBUTE_TEMPORARY | |
| 120 // FILE_FLAG_RANDOM_ACCESS | |
| 121 // FILE_FLAG_SEQUENTIAL_SCAN | |
| 122 | |
| 123 handle_ = ::CreateFile(file_name, | |
| 124 write ? (FILE_WRITE_DATA | | |
| 125 FILE_WRITE_ATTRIBUTES | | |
| 126 FILE_READ_DATA) : FILE_READ_DATA, | |
| 127 share_mode, | |
| 128 NULL, | |
| 129 write ? OPEN_ALWAYS : OPEN_EXISTING, | |
| 130 FILE_FLAG_RANDOM_ACCESS, | |
| 131 NULL); | |
| 132 | |
| 133 if (handle_ == INVALID_HANDLE_VALUE) { | |
| 134 HRESULT hr = HRESULTFromLastError(); | |
| 135 UTIL_LOG(LEVEL_ERROR, | |
| 136 (_T("[File::OpenShareMode - CreateFile failed][%s][%d][%d][0x%x]"), | |
| 137 file_name, write, async, hr)); | |
| 138 return hr; | |
| 139 } | |
| 140 | |
| 141 // This attribute is not supported directly by the CreateFile function. | |
| 142 if (write && | |
| 143 !::SetFileAttributes(file_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) { | |
| 144 HRESULT hr = HRESULTFromLastError(); | |
| 145 UTIL_LOG(LEVEL_ERROR, | |
| 146 (_T("[File::OpenShareMode - SetFileAttributes failed][0x%x]"), hr)); | |
| 147 return hr; | |
| 148 } | |
| 149 | |
| 150 read_only_ = !write; | |
| 151 pos_ = 0; | |
| 152 return S_OK; | |
| 153 } | |
| 154 | |
| 155 // The path must not be enclosed in quotes. This is the Windows standard. | |
| 156 // ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths. | |
| 157 bool File::Exists(const TCHAR* file_name) { | |
| 158 ASSERT1(file_name && *file_name); | |
| 159 ASSERT1(lstrlen(file_name) > 0); | |
| 160 | |
| 161 // NOTE: This is the fastest implementation I found. The results were: | |
| 162 // CreateFile 1783739 avg ticks/call | |
| 163 // FindFirstFile 634148 avg ticks/call | |
| 164 // GetFileAttributes 428714 avg ticks/call | |
| 165 // GetFileAttributesEx 396324 avg ticks/call | |
| 166 WIN32_FILE_ATTRIBUTE_DATA attrs = {0}; | |
| 167 return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs); | |
| 168 } | |
| 169 | |
| 170 bool File::IsDirectory(const TCHAR* file_name) { | |
| 171 ASSERT1(file_name && *file_name); | |
| 172 | |
| 173 WIN32_FILE_ATTRIBUTE_DATA attrs; | |
| 174 SetZero(attrs); | |
| 175 if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) { | |
| 176 UTIL_LOG(LEVEL_ERROR, | |
| 177 (_T("[File::IsDirectory - GetFileAttributesEx failed][%s][0x%x]"), | |
| 178 file_name, HRESULTFromLastError())); | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | |
| 183 } | |
| 184 | |
| 185 HRESULT File::GetWildcards(const TCHAR* dir, | |
| 186 const TCHAR* wildcard, | |
| 187 std::vector<CString>* matching_paths) { | |
| 188 ASSERT1(dir && *dir); | |
| 189 ASSERT1(wildcard && *wildcard); | |
| 190 ASSERT1(matching_paths); | |
| 191 | |
| 192 matching_paths->clear(); | |
| 193 | |
| 194 // Make sure directory name ends with "\" | |
| 195 CString directory = String_MakeEndWith(dir, _T("\\"), false); | |
| 196 | |
| 197 WIN32_FIND_DATA find_data; | |
| 198 SetZero(find_data); | |
| 199 scoped_hfind hfind(::FindFirstFile(directory + wildcard, &find_data)); | |
| 200 if (!hfind) { | |
| 201 HRESULT hr = HRESULTFromLastError(); | |
| 202 UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr)); | |
| 203 return hr; | |
| 204 } | |
| 205 do { | |
| 206 if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || | |
| 207 !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 208 CString to_file(directory + find_data.cFileName); | |
| 209 matching_paths->push_back(to_file); | |
| 210 } | |
| 211 } while (::FindNextFile(get(hfind), &find_data)); | |
| 212 | |
| 213 HRESULT hr = HRESULTFromLastError(); | |
| 214 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { | |
| 215 UTIL_LOG(LEVEL_ERROR, | |
| 216 (_T("[File::GetWildcards - FindNextFile failed][0x%x]"), hr)); | |
| 217 return hr; | |
| 218 } | |
| 219 return S_OK; | |
| 220 } | |
| 221 | |
| 222 // returns error if cannot remove | |
| 223 // returns success if removed or already removed | |
| 224 HRESULT File::Remove(const TCHAR* file_name) { | |
| 225 ASSERT1(file_name && *file_name); | |
| 226 | |
| 227 if (!Exists(file_name)) { | |
| 228 return S_OK; | |
| 229 } | |
| 230 | |
| 231 if (!::DeleteFile(file_name)) { | |
| 232 return HRESULTFromLastError(); | |
| 233 } | |
| 234 | |
| 235 return S_OK; | |
| 236 } | |
| 237 | |
| 238 HRESULT File::CopyWildcards(const TCHAR* from_dir, | |
| 239 const TCHAR* to_dir, | |
| 240 const TCHAR* wildcard, | |
| 241 bool replace_existing_files) { | |
| 242 ASSERT1(from_dir && *from_dir); | |
| 243 ASSERT1(to_dir && *to_dir); | |
| 244 ASSERT1(wildcard && *wildcard); | |
| 245 | |
| 246 // Make sure dir names end with a "\" | |
| 247 CString from_directory = String_MakeEndWith(from_dir, _T("\\"), false); | |
| 248 CString to_directory = String_MakeEndWith(to_dir, _T("\\"), false); | |
| 249 | |
| 250 // Get full path to source files (which is a wildcard) | |
| 251 CString from_files(from_directory + wildcard); | |
| 252 | |
| 253 // Run over all files that match wildcard | |
| 254 WIN32_FIND_DATA find_data; | |
| 255 SetZero(find_data); | |
| 256 | |
| 257 scoped_hfind hfind(::FindFirstFile(from_files, &find_data)); | |
| 258 if (!hfind) { | |
| 259 HRESULT hr = HRESULTFromLastError(); | |
| 260 UTIL_LOG(LEVEL_ERROR, | |
| 261 (_T("[File::CopyWildcards - FindFirstFile failed][0x%x]"), hr)); | |
| 262 return hr; | |
| 263 } | |
| 264 do { | |
| 265 // Copy files | |
| 266 if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || | |
| 267 !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 268 CString from_file(from_directory + find_data.cFileName); | |
| 269 CString to_file(to_directory + find_data.cFileName); | |
| 270 | |
| 271 if (!replace_existing_files && Exists(to_file)) { | |
| 272 // Continue, since the caller has explicitly asked us to not replace an | |
| 273 // existing file | |
| 274 continue; | |
| 275 } | |
| 276 | |
| 277 RET_IF_FAILED(Copy(from_file, to_file, replace_existing_files)); | |
| 278 } | |
| 279 } while (::FindNextFile(get(hfind), &find_data)); | |
| 280 | |
| 281 HRESULT hr = HRESULTFromLastError(); | |
| 282 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { | |
| 283 UTIL_LOG(LEVEL_ERROR, | |
| 284 (_T("[File::CopyWildcards - FindNextFile failed][0x%x]"), hr)); | |
| 285 return hr; | |
| 286 } | |
| 287 return S_OK; | |
| 288 } | |
| 289 | |
| 290 HRESULT File::CopyTree(const TCHAR* from_dir, | |
| 291 const TCHAR* to_dir, | |
| 292 bool replace_existing_files) { | |
| 293 ASSERT1(from_dir && *from_dir); | |
| 294 ASSERT1(to_dir && *to_dir); | |
| 295 | |
| 296 UTIL_LOG(L3, (L"[File::CopyTree][from_dir %s][to_dir %s][replace %d]", | |
| 297 from_dir, to_dir, replace_existing_files)); | |
| 298 | |
| 299 // Make sure dir names end with a "\" | |
| 300 CString from_directory(String_MakeEndWith(from_dir, L"\\", false)); | |
| 301 CString to_directory(String_MakeEndWith(to_dir, L"\\", false)); | |
| 302 | |
| 303 RET_IF_FAILED(CreateDir(to_directory, NULL)); | |
| 304 RET_IF_FAILED(CopyWildcards(from_directory, | |
| 305 to_directory, | |
| 306 L"*.*", | |
| 307 replace_existing_files)); | |
| 308 | |
| 309 // Run over all directories | |
| 310 WIN32_FIND_DATA find_data; | |
| 311 SetZero(find_data); | |
| 312 | |
| 313 CString from_files(from_directory); | |
| 314 from_files += _T("*.*"); | |
| 315 | |
| 316 scoped_hfind hfind(::FindFirstFile(from_files, &find_data)); | |
| 317 if (!hfind) { | |
| 318 HRESULT hr = HRESULTFromLastError(); | |
| 319 UTIL_LOG(LEVEL_ERROR, | |
| 320 (_T("[File::CopyTree - FindFirstFile failed][0x%x]"), hr)); | |
| 321 return hr; | |
| 322 } | |
| 323 do { | |
| 324 // Copy files | |
| 325 if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && | |
| 326 String_StrNCmp(find_data.cFileName, L"..", 2, false) && | |
| 327 String_StrNCmp(find_data.cFileName, L".", 2, false)) { | |
| 328 CString from_subdir(from_directory + find_data.cFileName); | |
| 329 CString to_subdir(to_directory + find_data.cFileName); | |
| 330 RET_IF_FAILED(CopyTree(from_subdir, to_subdir, replace_existing_files)); | |
| 331 } | |
| 332 } while (::FindNextFile(get(hfind), &find_data)); | |
| 333 | |
| 334 HRESULT hr = HRESULTFromLastError(); | |
| 335 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { | |
| 336 UTIL_LOG(LEVEL_ERROR, | |
| 337 (_T("[File::CopyTree - FindNextFile failed][0x%x]"), hr)); | |
| 338 return hr; | |
| 339 } | |
| 340 | |
| 341 return S_OK; | |
| 342 } | |
| 343 | |
| 344 HRESULT File::Copy(const TCHAR* from, | |
| 345 const TCHAR* to, | |
| 346 bool replace_existing_file) { | |
| 347 ASSERT1(from && *from); | |
| 348 ASSERT1(to && *to); | |
| 349 | |
| 350 if (!replace_existing_file && Exists(to)) { | |
| 351 // Return success, since the caller has explicitly asked us to not replace | |
| 352 // an existing file | |
| 353 return S_OK; | |
| 354 } | |
| 355 | |
| 356 if (!::CopyFile(from, to, !replace_existing_file)) { | |
| 357 HRESULT hr = HRESULTFromLastError(); | |
| 358 UTIL_LOG(LEVEL_ERROR, (_T("[File::Copy - CopyFile failed]") | |
| 359 _T("[from=%s][to=%s][replace=%u][0x%x]"), | |
| 360 from, to, replace_existing_file, hr)); | |
| 361 return hr; | |
| 362 } | |
| 363 | |
| 364 return S_OK; | |
| 365 } | |
| 366 | |
| 367 // TODO(omaha): Combine common code in Move/MoveAfterReboot | |
| 368 HRESULT File::Move(const TCHAR* from, | |
| 369 const TCHAR* to, | |
| 370 bool replace_existing_file) { | |
| 371 ASSERT1(from && *from); | |
| 372 ASSERT1(to && *to); | |
| 373 | |
| 374 DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH; | |
| 375 if (replace_existing_file) { | |
| 376 flags |= MOVEFILE_REPLACE_EXISTING; | |
| 377 } | |
| 378 | |
| 379 if (!::MoveFileEx(from, to, flags)) { | |
| 380 HRESULT hr = HRESULTFromLastError(); | |
| 381 UTIL_LOG(LEVEL_ERROR, | |
| 382 (_T("[File::Move - MoveFileEx failed]") | |
| 383 _T("[from=%s][to=%s][replace=%u][0x%x]"), | |
| 384 from, to, replace_existing_file, hr)); | |
| 385 return hr; | |
| 386 } | |
| 387 | |
| 388 return S_OK; | |
| 389 } | |
| 390 | |
| 391 // DeleteAfterReboot tries to delete the files by either moving them to the TEMP | |
| 392 // directory and deleting them on reboot, or if that fails, by trying to delete | |
| 393 // them in-place on reboot | |
| 394 HRESULT File::DeleteAfterReboot(const TCHAR* from) { | |
| 395 ASSERT1(from && *from); | |
| 396 | |
| 397 if (File::Exists(from)) { | |
| 398 HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
| 399 CString from_temp; | |
| 400 | |
| 401 // No point in moving into TEMP if we're already there | |
| 402 if (!String_StartsWith(from, app_util::GetTempDir(), true)) { | |
| 403 // Try to move to the TEMP directory first | |
| 404 CString temp_dir(String_MakeEndWith(app_util::GetTempDir(), | |
| 405 _T("\\"), | |
| 406 false)); | |
| 407 // Of the form "C:\\Windows\\Temp\\FROM.EXE1f4c0b7f" | |
| 408 from_temp.Format(_T("%s%s%x"), | |
| 409 temp_dir, | |
| 410 GetFileFromPath(from), | |
| 411 ::GetTickCount()); | |
| 412 | |
| 413 hr = File::Move(from, from_temp, true); | |
| 414 UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - move %s to %s][0x%x]"), | |
| 415 from, from_temp, hr)); | |
| 416 } | |
| 417 | |
| 418 if (SUCCEEDED(hr)) { | |
| 419 UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - delete %s after reboot]"), | |
| 420 from_temp)); | |
| 421 // Move temp file after reboot | |
| 422 if (FAILED(hr = File::MoveAfterReboot(from_temp, NULL))) { | |
| 423 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") | |
| 424 _T("[failed to delete after reboot %s][0x%x]"), | |
| 425 from_temp, hr)); | |
| 426 } | |
| 427 } else { | |
| 428 // Move original file after reboot | |
| 429 if (FAILED(hr = File::MoveAfterReboot(from, NULL))) { | |
| 430 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") | |
| 431 _T("[failed to delete after reboot %s][0x%x]"), | |
| 432 from, hr)); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 return hr; | |
| 437 } | |
| 438 | |
| 439 return S_OK; | |
| 440 } | |
| 441 | |
| 442 | |
| 443 HRESULT File::MoveAfterReboot(const TCHAR* from, const TCHAR* to) { | |
| 444 ASSERT1(from && *from); | |
| 445 | |
| 446 if (!File::Exists(from)) { | |
| 447 // File/directory doesn't exist, should this return failure or success? | |
| 448 // Decision: Failure. Because the caller can decide if it is really | |
| 449 // failure or not in his specific case. | |
| 450 UTIL_LOG(LEVEL_WARNING, (_T("[File::MoveAfterReboot]") | |
| 451 _T("[file doesn't exist][from %s]"), from)); | |
| 452 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
| 453 } | |
| 454 | |
| 455 DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT; | |
| 456 if (!File::IsDirectory(from)) { | |
| 457 // This flag valid only for files | |
| 458 flags |= MOVEFILE_REPLACE_EXISTING; | |
| 459 } | |
| 460 | |
| 461 if (!::MoveFileEx(from, to, flags)) { | |
| 462 HRESULT hr = HRESULTFromLastError(); | |
| 463 UTIL_LOG(LEVEL_ERROR, (_T("[File::MoveAfterReboot]") | |
| 464 _T("[failed to MoveFileEx from '%s' to '%s'][0x%x]"), | |
| 465 from, to, hr)); | |
| 466 return hr; | |
| 467 } | |
| 468 | |
| 469 return S_OK; | |
| 470 } | |
| 471 | |
| 472 // See if we have any moves pending a reboot. Return SUCCESS if we do | |
| 473 // not encounter errors (not finding a move is not an error). We need to | |
| 474 // also check the value of *found_ptr for whether we actually found a move. | |
| 475 // On return, *value_multisz_ptr is the value within | |
| 476 // "PendingFileRenameOperations", but with any moves for in_directory removed | |
| 477 // from it. | |
| 478 // The prefix_match boolean controls whether we do an exact match on | |
| 479 // in_directory, or remove all entries with the in_directory prefix. | |
| 480 // NOTE: If the only values found were our own keys, the whole | |
| 481 // PendingFileRenameOperations MULTISZ needs to be deleted. | |
| 482 // This is signified by a returned *value_size_chars_ptr of 0. | |
| 483 HRESULT File::GetPendingRenamesValueMinusDir(const TCHAR* in_directory, | |
| 484 bool prefix_match, | |
| 485 TCHAR** value_multisz_ptr, | |
| 486 DWORD* value_size_chars_ptr, | |
| 487 bool* found_ptr) { | |
| 488 ASSERT1(in_directory && *in_directory); | |
| 489 | |
| 490 // Convert to references for easier-to-read-code: | |
| 491 TCHAR*& value_multisz = *value_multisz_ptr; | |
| 492 DWORD& value_size_chars = *value_size_chars_ptr; | |
| 493 bool& found = *found_ptr; | |
| 494 | |
| 495 // Initialize [out] parameters | |
| 496 value_multisz = NULL; | |
| 497 value_size_chars = 0; | |
| 498 found = false; | |
| 499 | |
| 500 // Locals mirroring the [out] parameters. | |
| 501 // We will only set the corresponding [out] parameters when we have something | |
| 502 // meaningful to return to the caller | |
| 503 scoped_array<TCHAR> value_multisz_local; | |
| 504 DWORD value_size_chars_local = 0; | |
| 505 | |
| 506 DWORD value_size_bytes = 0; | |
| 507 // Get the current value of the key | |
| 508 // If the Key is missing, that's totally acceptable. | |
| 509 RET_IF_FALSE( | |
| 510 RegKey::HasValue(kSessionManagerKey, kPendingFileRenameOps) && | |
| 511 SUCCEEDED(RegKey::GetValue(kSessionManagerKey, | |
| 512 kPendingFileRenameOps, | |
| 513 reinterpret_cast<byte**>(&value_multisz_local), | |
| 514 &value_size_bytes)), | |
| 515 S_OK); | |
| 516 | |
| 517 ASSERT1(value_multisz_local.get() || value_size_bytes == 0); | |
| 518 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") | |
| 519 _T("[read multisz %d bytes]"), | |
| 520 value_size_bytes)); | |
| 521 RET_IF_FALSE(value_size_bytes > 0, S_OK); | |
| 522 | |
| 523 // The size should always be aligned to a TCHAR boundary, otherwise the key | |
| 524 // is corrupted. | |
| 525 ASSERT1((value_size_bytes % sizeof(TCHAR)) == 0); | |
| 526 RET_IF_FALSE((value_size_bytes % sizeof(TCHAR)) == 0, | |
| 527 HRESULT_FROM_WIN32(ERROR_BADKEY)); | |
| 528 // Valid size, so convert to TCHARs: | |
| 529 value_size_chars_local = value_size_bytes / sizeof(TCHAR); | |
| 530 | |
| 531 // Buffer must terminate with two nulls | |
| 532 ASSERT(value_size_chars_local >= 2 && | |
| 533 !value_multisz_local[value_size_chars_local - 1] && | |
| 534 !value_multisz_local[value_size_chars_local - 2], | |
| 535 (_T("buffer must terminate with two nulls"))); | |
| 536 RET_IF_FALSE(value_size_chars_local >= 2 && | |
| 537 !value_multisz_local[value_size_chars_local - 1] && | |
| 538 !value_multisz_local[value_size_chars_local - 2], | |
| 539 HRESULT_FROM_WIN32(ERROR_BADKEY)); | |
| 540 // Mark the end of the string. | |
| 541 // multisz_end will point at the character past end of buffer: | |
| 542 TCHAR* multisz_end = value_multisz_local.get() + value_size_chars_local; | |
| 543 | |
| 544 // We're looking for \??\C:\... The \??\ was | |
| 545 // added by the OS to the directory name we specified. | |
| 546 CString from_dir(_T("\\??\\")); | |
| 547 from_dir += in_directory; | |
| 548 DWORD from_dir_len = from_dir.GetLength(); | |
| 549 | |
| 550 // A MULTISZ is a list of null terminated strings, terminated by a double | |
| 551 // null. We keep two pointers marching along the string in parallel. | |
| 552 TCHAR* str_read = value_multisz_local.get(); | |
| 553 TCHAR* str_write = str_read; | |
| 554 | |
| 555 while ((str_read < multisz_end) && *str_read) { | |
| 556 size_t str_len = ::lstrlen(str_read); | |
| 557 // A FALSE here indicates a corrupt PendingFileRenameOperations | |
| 558 RET_IF_FALSE((str_read + str_len + 1) < multisz_end, | |
| 559 HRESULT_FROM_WIN32(ERROR_BADKEY)); | |
| 560 if (0 == String_StrNCmp(str_read, | |
| 561 from_dir, | |
| 562 from_dir_len + (prefix_match ? 0 : 1), | |
| 563 true)) { | |
| 564 // String matches, we want to remove this string, so advance only the | |
| 565 // read pointer - past this string and the replacement string. | |
| 566 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") | |
| 567 _T("[skips past match '%s']"), | |
| 568 str_read)); | |
| 569 str_read += str_len + 1; | |
| 570 str_read += ::lstrlen(str_read) + 1; | |
| 571 continue; | |
| 572 } | |
| 573 // String doesn't match, we want to keep it. | |
| 574 if (str_read != str_write) { | |
| 575 // Here we're not in sync in the buffer, we've got to move two | |
| 576 // strings down. | |
| 577 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") | |
| 578 _T("[copying some other deletion][%s][%s]"), | |
| 579 str_read, str_read + ::lstrlen(str_read) + 1)); | |
| 580 ASSERT1(str_write < str_read); | |
| 581 String_StrNCpy(str_write, str_read, str_len+1); | |
| 582 str_read += str_len + 1; | |
| 583 str_write += str_len + 1; | |
| 584 str_len = ::lstrlen(str_read); | |
| 585 String_StrNCpy(str_write, str_read, str_len+1); | |
| 586 str_read += str_len + 1; | |
| 587 str_write += str_len + 1; | |
| 588 } else { | |
| 589 // We're in sync in the buffer, advance both pointers past two strings | |
| 590 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") | |
| 591 _T("[skipping past some other deletion][%s][%s]"), | |
| 592 str_read, str_read + ::lstrlen(str_read) + 1)); | |
| 593 str_read += str_len + 1; | |
| 594 str_read += ::lstrlen(str_read) + 1; | |
| 595 str_write = str_read; | |
| 596 } | |
| 597 } | |
| 598 | |
| 599 // A FALSE here indicates a corrupt PendingFileRenameOperations | |
| 600 RET_IF_FALSE(str_read < multisz_end, | |
| 601 HRESULT_FROM_WIN32(ERROR_BADKEY)); | |
| 602 | |
| 603 if (str_read != str_write) { | |
| 604 // We found some values | |
| 605 found = true; | |
| 606 | |
| 607 if (str_write == value_multisz_local.get()) { | |
| 608 // The only values were our own keys, | |
| 609 // and the whole PendingFileRenameOperations | |
| 610 // value needs to be deleted. We do not populate | |
| 611 // value_size_chars or value_multisz in this case. | |
| 612 ASSERT1(!value_size_chars); | |
| 613 ASSERT1(!value_multisz); | |
| 614 } else { | |
| 615 // The last string should have a NULL terminator: | |
| 616 ASSERT1(str_write[-1] == '\0'); | |
| 617 RET_IF_FALSE(str_write[-1] == '\0', | |
| 618 HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); | |
| 619 // a REG_MULTI_SZ needs to be terminated with an extra NULL. | |
| 620 *str_write = '\0'; | |
| 621 ++str_write; | |
| 622 | |
| 623 // Populate value_size_chars and value_multisz in this case. | |
| 624 value_multisz = value_multisz_local.release(); | |
| 625 value_size_chars = str_write - value_multisz; | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 return S_OK; | |
| 630 } | |
| 631 | |
| 632 // Remove any moves pending a reboot from the PendingFileRenameOperations | |
| 633 // in the registry. | |
| 634 // The prefix_match boolean controls whether we do an exact match on | |
| 635 // in_directory, or remove all entries with the in_directory prefix. | |
| 636 HRESULT File::RemoveFromMovesPendingReboot(const TCHAR* in_directory, | |
| 637 bool prefix_match) { | |
| 638 ASSERT1(in_directory && *in_directory); | |
| 639 | |
| 640 bool found = false; | |
| 641 // scoped_array will free the value_multisz buffer on stack unwind: | |
| 642 scoped_array<TCHAR> value_multisz; | |
| 643 DWORD value_size_chars = 0; | |
| 644 HRESULT hr = GetPendingRenamesValueMinusDir(in_directory, | |
| 645 prefix_match, | |
| 646 address(value_multisz), | |
| 647 &value_size_chars, | |
| 648 &found); | |
| 649 if (SUCCEEDED(hr) && found) { | |
| 650 if (value_multisz.get() == NULL) { | |
| 651 // There's no point in writing an empty value_multisz. | |
| 652 // Let's delete the PendingFileRenameOperations value | |
| 653 UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]") | |
| 654 _T("[deleting PendingFileRenameOperations value]"))); | |
| 655 RET_IF_FAILED(RegKey::DeleteValue(kSessionManagerKey, | |
| 656 kPendingFileRenameOps)); | |
| 657 } else { | |
| 658 // Let's write the modified value_multisz into the | |
| 659 // PendingFileRenameOperations value | |
| 660 UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]") | |
| 661 _T("[rewriting multisz %d bytes]"), | |
| 662 value_size_chars * sizeof(TCHAR))); | |
| 663 RET_IF_FAILED(RegKey::SetValueMultiSZ( | |
| 664 kSessionManagerKey, | |
| 665 kPendingFileRenameOps, | |
| 666 reinterpret_cast<byte*>(value_multisz.get()), | |
| 667 value_size_chars * sizeof(TCHAR))); | |
| 668 } | |
| 669 } | |
| 670 | |
| 671 // Failure of GetPendingRenamesValueMinusDir() may indicate something | |
| 672 // seriously wrong with the system. Propogate error. | |
| 673 return hr; | |
| 674 } | |
| 675 | |
| 676 // Did the user try to uninstall a previous install of the same version, and | |
| 677 // we couldn't clean up without a reboot? | |
| 678 // We check if there are any moves pending a reboot from the | |
| 679 // PendingFileRenameOperations in the registry. | |
| 680 // The prefix_match boolean controls whether we do an exact match on | |
| 681 // in_directory, or check all entries with the in_directory prefix. | |
| 682 bool File::AreMovesPendingReboot(const TCHAR* in_directory, bool prefix_match) { | |
| 683 ASSERT1(in_directory && *in_directory); | |
| 684 | |
| 685 bool found = false; | |
| 686 // scoped_array will free the value_multisz buffer on stack unwind: | |
| 687 scoped_array<TCHAR> value_multisz; | |
| 688 DWORD value_size_chars = 0; | |
| 689 | |
| 690 if (SUCCEEDED(GetPendingRenamesValueMinusDir(in_directory, | |
| 691 prefix_match, | |
| 692 address(value_multisz), | |
| 693 &value_size_chars, | |
| 694 &found)) && found) { | |
| 695 return true; | |
| 696 } | |
| 697 | |
| 698 return false; | |
| 699 } | |
| 700 | |
| 701 HRESULT File::GetFileTime(const TCHAR* file_name, | |
| 702 FILETIME* created, | |
| 703 FILETIME* accessed, | |
| 704 FILETIME* modified) { | |
| 705 ASSERT1(file_name && *file_name); | |
| 706 | |
| 707 bool is_dir = IsDirectory(file_name); | |
| 708 // To obtain a handle to a directory, call the CreateFile function with | |
| 709 // the FILE_FLAG_BACKUP_SEMANTICS flag | |
| 710 scoped_hfile file_handle( | |
| 711 ::CreateFile(file_name, | |
| 712 FILE_READ_DATA, | |
| 713 FILE_SHARE_READ, | |
| 714 NULL, | |
| 715 OPEN_EXISTING, | |
| 716 is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL, | |
| 717 NULL)); | |
| 718 HRESULT hr = S_OK; | |
| 719 | |
| 720 if (!file_handle) { | |
| 721 hr = HRESULTFromLastError(); | |
| 722 UTIL_LOG(LE, (_T("[File::GetFileTime]") | |
| 723 _T("[failed to open file][%s][0x%x]"), file_name, hr)); | |
| 724 } else { | |
| 725 if (!::GetFileTime(get(file_handle), created, accessed, modified)) { | |
| 726 hr = HRESULTFromLastError(); | |
| 727 UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]") | |
| 728 _T("[failed to get file time][%s][0x%x]"), | |
| 729 file_name, hr)); | |
| 730 } | |
| 731 } | |
| 732 | |
| 733 return hr; | |
| 734 } | |
| 735 | |
| 736 HRESULT File::SetFileTime(const TCHAR* file_name, | |
| 737 const FILETIME* created, | |
| 738 const FILETIME* accessed, | |
| 739 const FILETIME* modified) { | |
| 740 ASSERT1(file_name && *file_name); | |
| 741 | |
| 742 bool is_dir = IsDirectory(file_name); | |
| 743 // To obtain a handle to a directory, call the CreateFile function with | |
| 744 // the FILE_FLAG_BACKUP_SEMANTICS flag | |
| 745 scoped_hfile file_handle( | |
| 746 ::CreateFile(file_name, | |
| 747 FILE_WRITE_ATTRIBUTES, | |
| 748 FILE_SHARE_WRITE, | |
| 749 NULL, | |
| 750 OPEN_EXISTING, | |
| 751 is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL, | |
| 752 NULL)); | |
| 753 HRESULT hr = S_OK; | |
| 754 | |
| 755 if (!file_handle) { | |
| 756 hr = HRESULTFromLastError(); | |
| 757 UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]") | |
| 758 _T("[failed to open file][%s][0x%x]"), | |
| 759 file_name, hr)); | |
| 760 } else { | |
| 761 BOOL res = ::SetFileTime(get(file_handle), created, accessed, modified); | |
| 762 if (!res) { | |
| 763 hr = HRESULTFromLastError(); | |
| 764 UTIL_LOG(LEVEL_ERROR, (_T("[File::SetFileTime]") | |
| 765 _T("[failed to set file time][%s][0x%x]"), | |
| 766 file_name, hr)); | |
| 767 } | |
| 768 } | |
| 769 | |
| 770 return hr; | |
| 771 } | |
| 772 | |
| 773 HRESULT File::Sync() { | |
| 774 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 775 | |
| 776 if (!::FlushFileBuffers(handle_)) { | |
| 777 HRESULT hr = HRESULTFromLastError(); | |
| 778 UTIL_LOG(LEVEL_ERROR, (_T("[File::Sync]") | |
| 779 _T("[FlushFileBuffers failed][%s][0x%x]"), | |
| 780 file_name_, hr)); | |
| 781 return hr; | |
| 782 } | |
| 783 return S_OK; | |
| 784 } | |
| 785 | |
| 786 HRESULT File::SeekToBegin() { | |
| 787 return SeekFromBegin(0); | |
| 788 } | |
| 789 | |
| 790 HRESULT File::SeekFromBegin(uint32 n) { | |
| 791 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 792 | |
| 793 if (::SetFilePointer(handle_, n, NULL, FILE_BEGIN) == | |
| 794 INVALID_SET_FILE_POINTER) { | |
| 795 HRESULT hr = HRESULTFromLastError(); | |
| 796 UTIL_LOG(LEVEL_ERROR, (_T("[File::SeekFromBegin]") | |
| 797 _T("[SetFilePointer failed][%s][0x%x]"), | |
| 798 file_name_, hr)); | |
| 799 return hr; | |
| 800 } | |
| 801 pos_ = n; | |
| 802 return S_OK; | |
| 803 } | |
| 804 | |
| 805 // read nLen bytes starting at position n | |
| 806 // returns number of bytes read | |
| 807 // | |
| 808 // async operations: | |
| 809 // | |
| 810 // async_id - identifier of a sequence of async operations, 0 for synchronous | |
| 811 // | |
| 812 // if the async operation has not been initiated, we initiate it | |
| 813 // if it is in progress we do nothing | |
| 814 // if it has been completed we return the data | |
| 815 // does not delete async data entry | |
| 816 HRESULT File::ReadAt(const uint32 offset, byte* buf, const uint32 len, | |
| 817 const uint32, uint32* bytes_read) { | |
| 818 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 819 ASSERT1(buf); | |
| 820 ASSERT1(len); // reading 0 bytes is not valid (differs from CRT API) | |
| 821 | |
| 822 RET_IF_FAILED(SeekFromBegin(offset)); | |
| 823 | |
| 824 DWORD read = 0; | |
| 825 if (!::ReadFile(handle_, buf, len, &read, NULL)) { | |
| 826 HRESULT hr = HRESULTFromLastError(); | |
| 827 UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]") | |
| 828 _T("[ReadFile failed][%s][0x%x]"), file_name_, hr)); | |
| 829 return hr; | |
| 830 } | |
| 831 | |
| 832 if (bytes_read) { | |
| 833 *bytes_read = read; | |
| 834 } | |
| 835 | |
| 836 return (read == len) ? S_OK : E_FAIL; | |
| 837 } | |
| 838 | |
| 839 | |
| 840 // reads up to max_len bytes from the start of the file | |
| 841 // not considered an error if there are less than max_len bytes read | |
| 842 // returns number of bytes read | |
| 843 HRESULT File::ReadFromStartOfFile(const uint32 max_len, | |
| 844 byte* buf, | |
| 845 uint32* bytes_read) { | |
| 846 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 847 ASSERT1(buf); | |
| 848 ASSERT1(max_len); | |
| 849 | |
| 850 RET_IF_FAILED(SeekFromBegin(0)); | |
| 851 | |
| 852 uint32 file_len = 0; | |
| 853 RET_IF_FAILED(GetLength(&file_len)); | |
| 854 | |
| 855 if (!file_len) { | |
| 856 if (bytes_read) { | |
| 857 *bytes_read = 0; | |
| 858 } | |
| 859 return S_OK; | |
| 860 } | |
| 861 | |
| 862 uint32 len = max_len; | |
| 863 if (len > file_len) { | |
| 864 len = static_cast<uint32>(file_len); | |
| 865 } | |
| 866 | |
| 867 return Read(len, buf, bytes_read); | |
| 868 } | |
| 869 | |
| 870 // this function handles lines terminated with LF or CRLF | |
| 871 // all CR characters are removed from each line, and LF is assumed | |
| 872 // to be the end of line and is removed | |
| 873 HRESULT File::ReadLineAnsi(uint32 max_len, char* line, uint32* len) { | |
| 874 ASSERT1(line); | |
| 875 ASSERT1(max_len); | |
| 876 | |
| 877 char c = 0; | |
| 878 uint32 len_read = 0; | |
| 879 uint32 total_len = 0; | |
| 880 | |
| 881 while (SUCCEEDED(Read(1, reinterpret_cast<byte *>(&c), &len_read)) && | |
| 882 len_read && | |
| 883 c != '\n') { | |
| 884 if (total_len < max_len - 1 && c != '\r') { | |
| 885 line[total_len++] = c; | |
| 886 } | |
| 887 } | |
| 888 | |
| 889 ASSERT1(total_len < max_len); | |
| 890 line[total_len] = '\0'; | |
| 891 | |
| 892 if (len) { | |
| 893 *len = total_len; | |
| 894 } | |
| 895 | |
| 896 return (len_read || total_len) ? S_OK : E_FAIL; | |
| 897 } | |
| 898 | |
| 899 // used by ReadFromStartOfFile and ReadLineAnsi; not reading all requested bytes | |
| 900 // is not considered fatal | |
| 901 HRESULT File::Read(const uint32 len, byte* buf, uint32* bytes_read) { | |
| 902 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 903 ASSERT1(buf); | |
| 904 ASSERT1(len); | |
| 905 | |
| 906 DWORD read = 0; | |
| 907 if (!::ReadFile(handle_, buf, len, &read, NULL)) { | |
| 908 HRESULT hr = HRESULTFromLastError(); | |
| 909 UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]") | |
| 910 _T("[ReadFile failed][%s][0x%x]"), | |
| 911 file_name_, hr)); | |
| 912 return hr; | |
| 913 } | |
| 914 | |
| 915 if (bytes_read) { | |
| 916 *bytes_read = read; | |
| 917 } | |
| 918 | |
| 919 return S_OK; | |
| 920 } | |
| 921 | |
| 922 | |
| 923 // returns number of bytes written | |
| 924 HRESULT File::WriteAt(const uint32 offset, | |
| 925 const byte* buf, | |
| 926 const uint32 len, | |
| 927 uint32, | |
| 928 uint32* bytes_written) { | |
| 929 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 930 ASSERT1(!read_only_); | |
| 931 ASSERT1(buf); | |
| 932 ASSERT1(len); | |
| 933 | |
| 934 RET_IF_FAILED(SeekFromBegin(offset)); | |
| 935 | |
| 936 return Write(buf, len, bytes_written); | |
| 937 } | |
| 938 | |
| 939 | |
| 940 // write buffer n times | |
| 941 HRESULT File::WriteN(const byte* buf, | |
| 942 const uint32 len, | |
| 943 const uint32 n, | |
| 944 uint32* bytes_written) { | |
| 945 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 946 ASSERT1(!read_only_); | |
| 947 ASSERT1(buf); | |
| 948 ASSERT1(len); | |
| 949 ASSERT1(n); | |
| 950 | |
| 951 HRESULT hr = S_OK; | |
| 952 | |
| 953 uint32 total_wrote = 0; | |
| 954 | |
| 955 byte* temp_buf = const_cast<byte*>(buf); | |
| 956 | |
| 957 scoped_array<byte> encrypt_buf; | |
| 958 | |
| 959 uint32 to_go = n; | |
| 960 while (to_go) { | |
| 961 uint32 wrote = 0; | |
| 962 hr = Write(temp_buf, len, &wrote); | |
| 963 if (FAILED(hr)) { | |
| 964 if (bytes_written) { | |
| 965 *bytes_written = total_wrote; | |
| 966 } | |
| 967 return hr; | |
| 968 } | |
| 969 | |
| 970 total_wrote += wrote; | |
| 971 to_go--; | |
| 972 } | |
| 973 | |
| 974 if (bytes_written) { | |
| 975 *bytes_written = total_wrote; | |
| 976 } | |
| 977 return hr; | |
| 978 } | |
| 979 | |
| 980 // returns number of bytes written | |
| 981 HRESULT File::Write(const byte* buf, const uint32 len, uint32* bytes_written) { | |
| 982 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 983 ASSERT1(!read_only_); | |
| 984 ASSERT1(buf); | |
| 985 ASSERT1(len); // writing 0 bytes is not valid (differs from CRT API) | |
| 986 | |
| 987 byte* b = const_cast<byte*>(buf); | |
| 988 | |
| 989 scoped_array<byte> encrypt_buf; | |
| 990 | |
| 991 DWORD wrote = 0; | |
| 992 if (!::WriteFile(handle_, b, len, &wrote, NULL)) { | |
| 993 HRESULT hr = HRESULTFromLastError(); | |
| 994 UTIL_LOG(LEVEL_ERROR, (_T("[File::Write]") | |
| 995 _T("[WriteFile failed][%s][0x%x]"), | |
| 996 file_name_, hr)); | |
| 997 return hr; | |
| 998 } | |
| 999 | |
| 1000 if (bytes_written) { | |
| 1001 *bytes_written = wrote; | |
| 1002 } | |
| 1003 pos_ += wrote; | |
| 1004 | |
| 1005 return (wrote == len) ? S_OK : E_FAIL; | |
| 1006 } | |
| 1007 | |
| 1008 HRESULT File::ClearAt(const uint32 offset, | |
| 1009 const uint32 len, | |
| 1010 uint32* bytes_written) { | |
| 1011 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1012 ASSERT1(!read_only_); | |
| 1013 ASSERT1(len); | |
| 1014 | |
| 1015 byte zero[kZeroSize] = {0}; | |
| 1016 uint32 to_go = len; | |
| 1017 uint32 written = 0; | |
| 1018 uint32 pos = offset; | |
| 1019 | |
| 1020 while (to_go) { | |
| 1021 uint32 wrote = 0; | |
| 1022 uint32 write_len = std::min(to_go, kZeroSize); | |
| 1023 RET_IF_FAILED(WriteAt(pos, zero, write_len, 0, &wrote)); | |
| 1024 | |
| 1025 if (wrote != write_len) { | |
| 1026 return E_FAIL; | |
| 1027 } | |
| 1028 pos += wrote; | |
| 1029 written += wrote; | |
| 1030 to_go -= write_len; | |
| 1031 } | |
| 1032 | |
| 1033 if (bytes_written) { | |
| 1034 *bytes_written = written; | |
| 1035 } | |
| 1036 return S_OK; | |
| 1037 } | |
| 1038 | |
| 1039 // returns true on failure | |
| 1040 // zeros new data if zero_data == true | |
| 1041 HRESULT File::SetLength(const uint32 n, bool zero_data) { | |
| 1042 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1043 ASSERT1(!read_only_); | |
| 1044 ASSERT1(n <= kMaxFileSize); | |
| 1045 | |
| 1046 HRESULT hr = S_OK; | |
| 1047 | |
| 1048 uint32 len = 0; | |
| 1049 VERIFY1(SUCCEEDED(GetLength(&len))); | |
| 1050 | |
| 1051 if (len == n) { | |
| 1052 return S_OK; | |
| 1053 } | |
| 1054 | |
| 1055 // according to the documentation, the | |
| 1056 // new space will not be initialized | |
| 1057 if (n > len) { | |
| 1058 if (zero_data) { | |
| 1059 uint32 bytes_written = 0; | |
| 1060 RET_IF_FAILED(ClearAt(len, n - len, &bytes_written)); | |
| 1061 if (bytes_written != n - len) { | |
| 1062 return E_FAIL; | |
| 1063 } | |
| 1064 } else { | |
| 1065 byte zero = 0; | |
| 1066 uint32 bytes_written = 0; | |
| 1067 RET_IF_FAILED(WriteAt(n - 1, &zero, 1, 0, &bytes_written)); | |
| 1068 if (bytes_written != 1) { | |
| 1069 return E_FAIL; | |
| 1070 } | |
| 1071 } | |
| 1072 } else { | |
| 1073 SeekFromBegin(n); | |
| 1074 SetEndOfFile(handle_); | |
| 1075 } | |
| 1076 | |
| 1077 ASSERT1(SUCCEEDED(GetLength(&len)) && len == n); | |
| 1078 | |
| 1079 return S_OK; | |
| 1080 } | |
| 1081 | |
| 1082 HRESULT File::ExtendInBlocks(const uint32 block_size, uint32 size_needed, | |
| 1083 uint32* new_size, bool clear_new_space) { | |
| 1084 ASSERT1(new_size); | |
| 1085 | |
| 1086 *new_size = size_needed; | |
| 1087 | |
| 1088 if (*new_size % block_size) { | |
| 1089 *new_size += block_size - (*new_size % block_size); | |
| 1090 } | |
| 1091 | |
| 1092 // is zero_data needed? may reduce fragmentation by causing the block to | |
| 1093 // be written | |
| 1094 return SetLength(*new_size, clear_new_space); | |
| 1095 } | |
| 1096 | |
| 1097 // returns S_OK on success | |
| 1098 HRESULT File::GetLength(uint32* length) { | |
| 1099 ASSERT1(length); | |
| 1100 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1101 | |
| 1102 DWORD len = GetFileSize(handle_, NULL); | |
| 1103 if (len == INVALID_FILE_SIZE) { | |
| 1104 ASSERT(false, (_T("cannot get file length"))); | |
| 1105 return E_FAIL; | |
| 1106 } | |
| 1107 *length = len; | |
| 1108 return S_OK; | |
| 1109 } | |
| 1110 | |
| 1111 HRESULT File::Touch() { | |
| 1112 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1113 | |
| 1114 FILETIME file_time; | |
| 1115 SetZero(file_time); | |
| 1116 | |
| 1117 ::GetSystemTimeAsFileTime(&file_time); | |
| 1118 | |
| 1119 if (!::SetFileTime(handle_, NULL, NULL, &file_time)) { | |
| 1120 return HRESULTFromLastError(); | |
| 1121 } | |
| 1122 return S_OK; | |
| 1123 } | |
| 1124 | |
| 1125 HRESULT File::Close() { | |
| 1126 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1127 | |
| 1128 HRESULT hr = S_OK; | |
| 1129 if (!::CloseHandle(handle_)) { | |
| 1130 hr = HRESULTFromLastError(); | |
| 1131 } | |
| 1132 | |
| 1133 handle_ = INVALID_HANDLE_VALUE; | |
| 1134 | |
| 1135 return hr; | |
| 1136 } | |
| 1137 | |
| 1138 // this is just for consistency with other classes; does not do anything | |
| 1139 HRESULT File::Reload(uint32* number_errors) { | |
| 1140 ASSERT1(number_errors); | |
| 1141 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1142 | |
| 1143 *number_errors = 0; | |
| 1144 return S_OK; | |
| 1145 } | |
| 1146 | |
| 1147 // this is just for consistency with other classes; does not do anything | |
| 1148 HRESULT File::Verify(uint32* number_errors) { | |
| 1149 ASSERT1(number_errors); | |
| 1150 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1151 | |
| 1152 *number_errors = 0; | |
| 1153 return S_OK; | |
| 1154 } | |
| 1155 | |
| 1156 // this is just for consistency with other classes; does not do anything | |
| 1157 HRESULT File::Dump() { | |
| 1158 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1159 return S_OK; | |
| 1160 } | |
| 1161 | |
| 1162 // for consistency with other classes | |
| 1163 HRESULT File::GetSizeOnDisk(uint64* size_on_disk) { | |
| 1164 ASSERT1(size_on_disk); | |
| 1165 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1166 | |
| 1167 uint32 len = 0; | |
| 1168 RET_IF_FAILED(GetLength(&len)); | |
| 1169 | |
| 1170 *size_on_disk = len; | |
| 1171 return S_OK; | |
| 1172 } | |
| 1173 | |
| 1174 // for consistency with other classes | |
| 1175 HRESULT File::GetReloadDiskSpaceNeeded(uint64* bytes_needed) { | |
| 1176 ASSERT1(bytes_needed); | |
| 1177 ASSERT1(handle_ != INVALID_HANDLE_VALUE); | |
| 1178 | |
| 1179 uint32 len = 0; | |
| 1180 RET_IF_FAILED(GetLength(&len)); | |
| 1181 | |
| 1182 *bytes_needed = len; | |
| 1183 return S_OK; | |
| 1184 } | |
| 1185 | |
| 1186 // Get the file size | |
| 1187 HRESULT File::GetFileSizeUnopen(const TCHAR* filename, uint32* out_size) { | |
| 1188 ASSERT1(filename); | |
| 1189 ASSERT1(out_size); | |
| 1190 | |
| 1191 WIN32_FILE_ATTRIBUTE_DATA data; | |
| 1192 SetZero(data); | |
| 1193 | |
| 1194 if (!::GetFileAttributesEx(filename, ::GetFileExInfoStandard, &data)) { | |
| 1195 return HRESULTFromLastError(); | |
| 1196 } | |
| 1197 | |
| 1198 *out_size = data.nFileSizeLow; | |
| 1199 | |
| 1200 return S_OK; | |
| 1201 } | |
| 1202 | |
| 1203 // Get the last time with a file was written to, and the size | |
| 1204 HRESULT File::GetLastWriteTimeAndSize(const TCHAR* file_path, | |
| 1205 SYSTEMTIME* out_time, | |
| 1206 unsigned int* out_size) { | |
| 1207 ASSERT1(file_path); | |
| 1208 | |
| 1209 WIN32_FIND_DATA wfd; | |
| 1210 SetZero(wfd); | |
| 1211 | |
| 1212 HANDLE find = ::FindFirstFile(file_path, &wfd); | |
| 1213 if (find == INVALID_HANDLE_VALUE) { | |
| 1214 return HRESULTFromLastError(); | |
| 1215 } | |
| 1216 | |
| 1217 ::FindClose(find); | |
| 1218 | |
| 1219 if (out_size) { | |
| 1220 *out_size = wfd.nFileSizeLow; | |
| 1221 } | |
| 1222 | |
| 1223 if (out_time) { | |
| 1224 // If created time is newer than write time, then use that instead | |
| 1225 // [it tends to be more relevant when copying files around] | |
| 1226 FILETIME* latest_time = NULL; | |
| 1227 if (::CompareFileTime(&wfd.ftCreationTime, &wfd.ftLastWriteTime) > 0) { | |
| 1228 latest_time = &wfd.ftCreationTime; | |
| 1229 } else { | |
| 1230 latest_time = &wfd.ftLastWriteTime; | |
| 1231 } | |
| 1232 | |
| 1233 if (!::FileTimeToSystemTime(latest_time, out_time)) { | |
| 1234 return HRESULTFromLastError(); | |
| 1235 } | |
| 1236 } | |
| 1237 | |
| 1238 return S_OK; | |
| 1239 } | |
| 1240 | |
| 1241 bool File::AreFilesIdentical(const TCHAR* filename1, const TCHAR* filename2) { | |
| 1242 UTIL_LOG(L4, (_T("[File::AreFilesIdentical][%s][%s]"), filename1, filename2)); | |
| 1243 | |
| 1244 uint32 file_size1 = 0; | |
| 1245 HRESULT hr = File::GetFileSizeUnopen(filename1, &file_size1); | |
| 1246 if (FAILED(hr)) { | |
| 1247 UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size1][0x%x]"), hr)); | |
| 1248 return false; | |
| 1249 } | |
| 1250 | |
| 1251 uint32 file_size2 = 0; | |
| 1252 hr = File::GetFileSizeUnopen(filename2, &file_size2); | |
| 1253 if (FAILED(hr)) { | |
| 1254 UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size2][0x%x]"), hr)); | |
| 1255 return false; | |
| 1256 } | |
| 1257 | |
| 1258 if (file_size1 != file_size2) { | |
| 1259 UTIL_LOG(L3, (_T("[file_size1 != file_size2][%d][%d]"), | |
| 1260 file_size1, file_size2)); | |
| 1261 return false; | |
| 1262 } | |
| 1263 | |
| 1264 File file1; | |
| 1265 hr = file1.OpenShareMode(filename1, false, false, FILE_SHARE_READ); | |
| 1266 if (FAILED(hr)) { | |
| 1267 UTIL_LOG(LE, (_T("[file1.OpenShareMode failed][0x%x]"), hr)); | |
| 1268 return false; | |
| 1269 } | |
| 1270 | |
| 1271 File file2; | |
| 1272 hr = file2.OpenShareMode(filename2, false, false, FILE_SHARE_READ); | |
| 1273 if (FAILED(hr)) { | |
| 1274 UTIL_LOG(LE, (_T("[file2.OpenShareMode failed][0x%x]"), hr)); | |
| 1275 return false; | |
| 1276 } | |
| 1277 | |
| 1278 static const uint32 kBufferSize = 0x10000; | |
| 1279 std::vector<uint8> buffer1(kBufferSize); | |
| 1280 std::vector<uint8> buffer2(kBufferSize); | |
| 1281 uint32 bytes_left = file_size1; | |
| 1282 | |
| 1283 while (bytes_left > 0) { | |
| 1284 uint32 bytes_to_read = std::min(bytes_left, kBufferSize); | |
| 1285 uint32 bytes_read1 = 0; | |
| 1286 uint32 bytes_read2 = 0; | |
| 1287 | |
| 1288 hr = file1.Read(bytes_to_read, &buffer1.front(), &bytes_read1); | |
| 1289 if (FAILED(hr)) { | |
| 1290 UTIL_LOG(LE, (_T("[file1.Read failed][%d][%d][0x%x]"), | |
| 1291 bytes_left, bytes_to_read, hr)); | |
| 1292 return false; | |
| 1293 } | |
| 1294 | |
| 1295 hr = file2.Read(bytes_to_read, &buffer2.front(), &bytes_read2); | |
| 1296 if (FAILED(hr)) { | |
| 1297 UTIL_LOG(LE, (_T("[file2.Read failed][%d][%d][0x%x]"), | |
| 1298 bytes_left, bytes_to_read, hr)); | |
| 1299 return false; | |
| 1300 } | |
| 1301 | |
| 1302 if (bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2) { | |
| 1303 UTIL_LOG(LE, | |
| 1304 (_T("[bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2]") | |
| 1305 _T("[%d][%d][%d]"), bytes_to_read, bytes_read1, bytes_read2)); | |
| 1306 return false; | |
| 1307 } | |
| 1308 | |
| 1309 if (memcmp(&buffer1.front(), &buffer2.front(), bytes_read1) != 0) { | |
| 1310 UTIL_LOG(L3, (_T("[memcmp failed][%d][%d]"), bytes_left, bytes_read1)); | |
| 1311 return false; | |
| 1312 } | |
| 1313 | |
| 1314 if (bytes_left < bytes_to_read) { | |
| 1315 UTIL_LOG(LE, (_T("[bytes_left < bytes_to_read][%d][%d]"), | |
| 1316 bytes_left, bytes_to_read)); | |
| 1317 return false; | |
| 1318 } | |
| 1319 | |
| 1320 bytes_left -= bytes_to_read; | |
| 1321 } | |
| 1322 | |
| 1323 return true; | |
| 1324 } | |
| 1325 | |
| 1326 FileLock::FileLock() { | |
| 1327 } | |
| 1328 | |
| 1329 FileLock::~FileLock() { | |
| 1330 Unlock(); | |
| 1331 } | |
| 1332 | |
| 1333 HRESULT FileLock::Lock(const TCHAR* file) { | |
| 1334 std::vector<CString> files; | |
| 1335 files.push_back(file); | |
| 1336 return Lock(files); | |
| 1337 } | |
| 1338 | |
| 1339 HRESULT FileLock::Lock(const std::vector<CString>& files) { | |
| 1340 ASSERT1(!files.empty()); | |
| 1341 | |
| 1342 // Try to lock all files | |
| 1343 size_t curr_size = handles_.size(); | |
| 1344 for (size_t i = 0; i < files.size(); ++i) { | |
| 1345 scoped_hfile handle(::CreateFile(files[i], | |
| 1346 GENERIC_READ, | |
| 1347 FILE_SHARE_READ, | |
| 1348 NULL, | |
| 1349 OPEN_EXISTING, | |
| 1350 FILE_ATTRIBUTE_NORMAL, | |
| 1351 NULL)); | |
| 1352 if (!handle) { | |
| 1353 UTIL_LOG(LEVEL_ERROR, | |
| 1354 (_T("[FileLock::Lock - failed to lock file][%s][0x%x]"), | |
| 1355 files[i], HRESULTFromLastError())); | |
| 1356 break; | |
| 1357 } | |
| 1358 handles_.push_back(release(handle)); | |
| 1359 } | |
| 1360 | |
| 1361 // Cleanup if we fail to lock all the files | |
| 1362 if (curr_size + files.size() < handles_.size()) { | |
| 1363 for (size_t i = handles_.size() - 1; i >= curr_size; --i) { | |
| 1364 VERIFY(::CloseHandle(handles_[i]), (_T(""))); | |
| 1365 handles_.pop_back(); | |
| 1366 } | |
| 1367 return E_FAIL; | |
| 1368 } | |
| 1369 | |
| 1370 return S_OK; | |
| 1371 } | |
| 1372 | |
| 1373 HRESULT FileLock::Unlock() { | |
| 1374 for (size_t i = 0; i < handles_.size(); ++i) { | |
| 1375 VERIFY(::CloseHandle(handles_[i]), (_T(""))); | |
| 1376 } | |
| 1377 handles_.clear(); | |
| 1378 return S_OK; | |
| 1379 } | |
| 1380 | |
| 1381 | |
| 1382 // path_name: the directory to watch | |
| 1383 // watch_subtree: watch all subdirectory changes or | |
| 1384 // only immediate child values | |
| 1385 // notify_filter: See the documentation for FindFirstChangeNotification | |
| 1386 FileWatcher::FileWatcher(const TCHAR* path_name, bool watch_subtree, | |
| 1387 DWORD notify_filter) | |
| 1388 : path_name_(path_name), | |
| 1389 watch_subtree_(watch_subtree), | |
| 1390 notify_filter_(notify_filter) { | |
| 1391 ASSERT1(path_name && *path_name); | |
| 1392 UTIL_LOG(L3, (_T("[FileWatcher::FileWatcher][%s]"), path_name)); | |
| 1393 } | |
| 1394 | |
| 1395 // Get the event that is signaled on store changes. | |
| 1396 HANDLE FileWatcher::change_event() const { | |
| 1397 ASSERT(valid(change_event_), (_T("call FileWatcher::SetupEvent first"))); | |
| 1398 return get(change_event_); | |
| 1399 } | |
| 1400 | |
| 1401 | |
| 1402 // Called to create/reset the event that gets signaled | |
| 1403 // any time the store changes. Access the created | |
| 1404 // event using change_event(). | |
| 1405 HRESULT FileWatcher::EnsureEventSetup() { | |
| 1406 UTIL_LOG(L3, (_T("[FileWatcher::EnsureEventSetup]"))); | |
| 1407 if (!valid(change_event_)) { | |
| 1408 reset(change_event_, ::FindFirstChangeNotification(path_name_, | |
| 1409 watch_subtree_, | |
| 1410 notify_filter_)); | |
| 1411 if (!valid(change_event_)) { | |
| 1412 ASSERT(false, (_T("unable to get file change notification"))); | |
| 1413 return E_FAIL; | |
| 1414 } | |
| 1415 // path name was only needed to set-up the event and now that is done.... | |
| 1416 path_name_.Empty(); | |
| 1417 return S_OK; | |
| 1418 } | |
| 1419 | |
| 1420 // if the event is set-up and no changes have occurred, | |
| 1421 // then there is no need to re-setup the event. | |
| 1422 if (valid(change_event_) && !HasChangeOccurred()) { | |
| 1423 return NOERROR; | |
| 1424 } | |
| 1425 | |
| 1426 return ::FindNextChangeNotification(get(change_event_)) ? S_OK : E_FAIL; | |
| 1427 } | |
| 1428 | |
| 1429 } // namespace omaha | |
| 1430 | |
| OLD | NEW |