Index: base/file.cc |
diff --git a/base/file.cc b/base/file.cc |
deleted file mode 100644 |
index 8317af16d881450ff06f2e0d7a7a6468062f5bad..0000000000000000000000000000000000000000 |
--- a/base/file.cc |
+++ /dev/null |
@@ -1,1430 +0,0 @@ |
-// Copyright 2003-2009 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
-// ======================================================================== |
-// File handling routines |
-// |
-// Possible performance improvement: make a subclass or alternate class |
-// that reads an entire file into memory and fulfills read/write requests |
-// in memory (or equivalently, use a memory mapped file). This can greatly |
-// improve performance if there are files we do a lot of read/write requests on. |
-// |
-// We generally are dealing with files that can be very large and want to |
-// minimize memory usage, so this is not high priority |
-// |
-// this has the beginnings of asynchronous access support |
-// |
-// Unfortunately, doing asynchronous reads with FILE_FLAG_OVERLAPPED buys us |
-// nothing because the system cache manager enforces serial requests. |
-// |
-// Hence, we need to also use FILE_FLAG_NO_BUFFERING. this has a number of |
-// constraints: |
-// - file read/write position must be aligned on multiples of the disk sector |
-// size |
-// - file read/write length must be a multiple of the sector size |
-// - read/write buffer must be aligned on multiples of the disk sector size |
-// |
-// In particular, this means that we cannot write 8 bytes, for example, because |
-// we have to write an entire sector. |
-// |
-// Currently, the implementation only supports enough to do some simple read |
-// tests |
-// |
-// The general idea is code that wants to to a sequence of asynchronous actions |
-// will look like the following, for an example of reading multiple event |
-// records asynchronously: |
-// |
-// uint32 async_id = File::GetNextAsyncId() |
-// while (!done) { |
-// for (everything_to_do, e.g., for each event to read) { |
-// call File::Read to read items needed; |
-// returns TR_E_FILE_ASYNC_PENDING if queued; or returns data if done |
-// process the item (e.g., event) if desired |
-// } |
-// call some routine to process pending completions; initiate delayed action |
-// } |
-// call some cleanup routine |
- |
-#include "omaha/base/file.h" |
-#include <algorithm> |
-#include "base/scoped_ptr.h" |
-#include "omaha/base/app_util.h" |
-#include "omaha/base/const_config.h" |
-#include "omaha/base/debug.h" |
-#include "omaha/base/error.h" |
-#include "omaha/base/logging.h" |
-#include "omaha/base/path.h" |
-#include "omaha/base/reg_key.h" |
-#include "omaha/base/scoped_any.h" |
-#include "omaha/base/scoped_ptr_address.h" |
-#include "omaha/base/string.h" |
-#include "omaha/base/system.h" |
-#include "omaha/base/timer.h" |
-#include "omaha/base/utils.h" |
- |
-namespace omaha { |
- |
-// Constants |
-const uint32 kZeroSize = 4096; // Buffer size used for clearing data in a file. |
- |
-// The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the |
-// registry. |
-static const TCHAR* kSessionManagerKey = |
- _T("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager"); |
-static const TCHAR* kPendingFileRenameOps = _T("PendingFileRenameOperations"); |
- |
-File::File() |
- : handle_(INVALID_HANDLE_VALUE), read_only_(false), sequence_id_(0) { |
-} |
- |
-File::~File() { |
- if (handle_ != INVALID_HANDLE_VALUE) { |
- VERIFY1(SUCCEEDED(Close())); |
- } |
-} |
- |
-// open for reading only if write == false, otherwise both reading and writing |
-// allow asynchronous operations if async == true. Use this function when you |
-// need exclusive access to the file. |
-HRESULT File::Open(const TCHAR* file_name, bool write, bool async) { |
- return OpenShareMode(file_name, write, async, 0); |
-} |
- |
-// Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise, |
-// this is identical to File::Open(). |
-HRESULT File::OpenShareMode(const TCHAR* file_name, |
- bool write, |
- bool async, |
- DWORD share_mode) { |
- ASSERT1(file_name && *file_name); |
- ASSERT1(handle_ == INVALID_HANDLE_VALUE); |
- VERIFY1(!async); |
- |
- file_name_ = file_name; |
- |
- // there are restrictions on what we can do if using FILE_FLAG_NO_BUFFERING |
- // if (!buffer) { flags |= FILE_FLAG_NO_BUFFERING; } |
- // FILE_FLAG_WRITE_THROUGH |
- // how efficient is NTFS encryption? FILE_ATTRIBUTE_ENCRYPTED |
- // FILE_ATTRIBUTE_TEMPORARY |
- // FILE_FLAG_RANDOM_ACCESS |
- // FILE_FLAG_SEQUENTIAL_SCAN |
- |
- handle_ = ::CreateFile(file_name, |
- write ? (FILE_WRITE_DATA | |
- FILE_WRITE_ATTRIBUTES | |
- FILE_READ_DATA) : FILE_READ_DATA, |
- share_mode, |
- NULL, |
- write ? OPEN_ALWAYS : OPEN_EXISTING, |
- FILE_FLAG_RANDOM_ACCESS, |
- NULL); |
- |
- if (handle_ == INVALID_HANDLE_VALUE) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::OpenShareMode - CreateFile failed][%s][%d][%d][0x%x]"), |
- file_name, write, async, hr)); |
- return hr; |
- } |
- |
- // This attribute is not supported directly by the CreateFile function. |
- if (write && |
- !::SetFileAttributes(file_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::OpenShareMode - SetFileAttributes failed][0x%x]"), hr)); |
- return hr; |
- } |
- |
- read_only_ = !write; |
- pos_ = 0; |
- return S_OK; |
-} |
- |
-// The path must not be enclosed in quotes. This is the Windows standard. |
-// ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths. |
-bool File::Exists(const TCHAR* file_name) { |
- ASSERT1(file_name && *file_name); |
- ASSERT1(lstrlen(file_name) > 0); |
- |
- // NOTE: This is the fastest implementation I found. The results were: |
- // CreateFile 1783739 avg ticks/call |
- // FindFirstFile 634148 avg ticks/call |
- // GetFileAttributes 428714 avg ticks/call |
- // GetFileAttributesEx 396324 avg ticks/call |
- WIN32_FILE_ATTRIBUTE_DATA attrs = {0}; |
- return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs); |
-} |
- |
-bool File::IsDirectory(const TCHAR* file_name) { |
- ASSERT1(file_name && *file_name); |
- |
- WIN32_FILE_ATTRIBUTE_DATA attrs; |
- SetZero(attrs); |
- if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::IsDirectory - GetFileAttributesEx failed][%s][0x%x]"), |
- file_name, HRESULTFromLastError())); |
- return false; |
- } |
- |
- return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; |
-} |
- |
-HRESULT File::GetWildcards(const TCHAR* dir, |
- const TCHAR* wildcard, |
- std::vector<CString>* matching_paths) { |
- ASSERT1(dir && *dir); |
- ASSERT1(wildcard && *wildcard); |
- ASSERT1(matching_paths); |
- |
- matching_paths->clear(); |
- |
- // Make sure directory name ends with "\" |
- CString directory = String_MakeEndWith(dir, _T("\\"), false); |
- |
- WIN32_FIND_DATA find_data; |
- SetZero(find_data); |
- scoped_hfind hfind(::FindFirstFile(directory + wildcard, &find_data)); |
- if (!hfind) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr)); |
- return hr; |
- } |
- do { |
- if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || |
- !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
- CString to_file(directory + find_data.cFileName); |
- matching_paths->push_back(to_file); |
- } |
- } while (::FindNextFile(get(hfind), &find_data)); |
- |
- HRESULT hr = HRESULTFromLastError(); |
- if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::GetWildcards - FindNextFile failed][0x%x]"), hr)); |
- return hr; |
- } |
- return S_OK; |
-} |
- |
-// returns error if cannot remove |
-// returns success if removed or already removed |
-HRESULT File::Remove(const TCHAR* file_name) { |
- ASSERT1(file_name && *file_name); |
- |
- if (!Exists(file_name)) { |
- return S_OK; |
- } |
- |
- if (!::DeleteFile(file_name)) { |
- return HRESULTFromLastError(); |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT File::CopyWildcards(const TCHAR* from_dir, |
- const TCHAR* to_dir, |
- const TCHAR* wildcard, |
- bool replace_existing_files) { |
- ASSERT1(from_dir && *from_dir); |
- ASSERT1(to_dir && *to_dir); |
- ASSERT1(wildcard && *wildcard); |
- |
- // Make sure dir names end with a "\" |
- CString from_directory = String_MakeEndWith(from_dir, _T("\\"), false); |
- CString to_directory = String_MakeEndWith(to_dir, _T("\\"), false); |
- |
- // Get full path to source files (which is a wildcard) |
- CString from_files(from_directory + wildcard); |
- |
- // Run over all files that match wildcard |
- WIN32_FIND_DATA find_data; |
- SetZero(find_data); |
- |
- scoped_hfind hfind(::FindFirstFile(from_files, &find_data)); |
- if (!hfind) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::CopyWildcards - FindFirstFile failed][0x%x]"), hr)); |
- return hr; |
- } |
- do { |
- // Copy files |
- if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || |
- !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
- CString from_file(from_directory + find_data.cFileName); |
- CString to_file(to_directory + find_data.cFileName); |
- |
- if (!replace_existing_files && Exists(to_file)) { |
- // Continue, since the caller has explicitly asked us to not replace an |
- // existing file |
- continue; |
- } |
- |
- RET_IF_FAILED(Copy(from_file, to_file, replace_existing_files)); |
- } |
- } while (::FindNextFile(get(hfind), &find_data)); |
- |
- HRESULT hr = HRESULTFromLastError(); |
- if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::CopyWildcards - FindNextFile failed][0x%x]"), hr)); |
- return hr; |
- } |
- return S_OK; |
-} |
- |
-HRESULT File::CopyTree(const TCHAR* from_dir, |
- const TCHAR* to_dir, |
- bool replace_existing_files) { |
- ASSERT1(from_dir && *from_dir); |
- ASSERT1(to_dir && *to_dir); |
- |
- UTIL_LOG(L3, (L"[File::CopyTree][from_dir %s][to_dir %s][replace %d]", |
- from_dir, to_dir, replace_existing_files)); |
- |
- // Make sure dir names end with a "\" |
- CString from_directory(String_MakeEndWith(from_dir, L"\\", false)); |
- CString to_directory(String_MakeEndWith(to_dir, L"\\", false)); |
- |
- RET_IF_FAILED(CreateDir(to_directory, NULL)); |
- RET_IF_FAILED(CopyWildcards(from_directory, |
- to_directory, |
- L"*.*", |
- replace_existing_files)); |
- |
- // Run over all directories |
- WIN32_FIND_DATA find_data; |
- SetZero(find_data); |
- |
- CString from_files(from_directory); |
- from_files += _T("*.*"); |
- |
- scoped_hfind hfind(::FindFirstFile(from_files, &find_data)); |
- if (!hfind) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::CopyTree - FindFirstFile failed][0x%x]"), hr)); |
- return hr; |
- } |
- do { |
- // Copy files |
- if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && |
- String_StrNCmp(find_data.cFileName, L"..", 2, false) && |
- String_StrNCmp(find_data.cFileName, L".", 2, false)) { |
- CString from_subdir(from_directory + find_data.cFileName); |
- CString to_subdir(to_directory + find_data.cFileName); |
- RET_IF_FAILED(CopyTree(from_subdir, to_subdir, replace_existing_files)); |
- } |
- } while (::FindNextFile(get(hfind), &find_data)); |
- |
- HRESULT hr = HRESULTFromLastError(); |
- if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::CopyTree - FindNextFile failed][0x%x]"), hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT File::Copy(const TCHAR* from, |
- const TCHAR* to, |
- bool replace_existing_file) { |
- ASSERT1(from && *from); |
- ASSERT1(to && *to); |
- |
- if (!replace_existing_file && Exists(to)) { |
- // Return success, since the caller has explicitly asked us to not replace |
- // an existing file |
- return S_OK; |
- } |
- |
- if (!::CopyFile(from, to, !replace_existing_file)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::Copy - CopyFile failed]") |
- _T("[from=%s][to=%s][replace=%u][0x%x]"), |
- from, to, replace_existing_file, hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-// TODO(omaha): Combine common code in Move/MoveAfterReboot |
-HRESULT File::Move(const TCHAR* from, |
- const TCHAR* to, |
- bool replace_existing_file) { |
- ASSERT1(from && *from); |
- ASSERT1(to && *to); |
- |
- DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH; |
- if (replace_existing_file) { |
- flags |= MOVEFILE_REPLACE_EXISTING; |
- } |
- |
- if (!::MoveFileEx(from, to, flags)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[File::Move - MoveFileEx failed]") |
- _T("[from=%s][to=%s][replace=%u][0x%x]"), |
- from, to, replace_existing_file, hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-// DeleteAfterReboot tries to delete the files by either moving them to the TEMP |
-// directory and deleting them on reboot, or if that fails, by trying to delete |
-// them in-place on reboot |
-HRESULT File::DeleteAfterReboot(const TCHAR* from) { |
- ASSERT1(from && *from); |
- |
- if (File::Exists(from)) { |
- HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); |
- CString from_temp; |
- |
- // No point in moving into TEMP if we're already there |
- if (!String_StartsWith(from, app_util::GetTempDir(), true)) { |
- // Try to move to the TEMP directory first |
- CString temp_dir(String_MakeEndWith(app_util::GetTempDir(), |
- _T("\\"), |
- false)); |
- // Of the form "C:\\Windows\\Temp\\FROM.EXE1f4c0b7f" |
- from_temp.Format(_T("%s%s%x"), |
- temp_dir, |
- GetFileFromPath(from), |
- ::GetTickCount()); |
- |
- hr = File::Move(from, from_temp, true); |
- UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - move %s to %s][0x%x]"), |
- from, from_temp, hr)); |
- } |
- |
- if (SUCCEEDED(hr)) { |
- UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - delete %s after reboot]"), |
- from_temp)); |
- // Move temp file after reboot |
- if (FAILED(hr = File::MoveAfterReboot(from_temp, NULL))) { |
- UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") |
- _T("[failed to delete after reboot %s][0x%x]"), |
- from_temp, hr)); |
- } |
- } else { |
- // Move original file after reboot |
- if (FAILED(hr = File::MoveAfterReboot(from, NULL))) { |
- UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") |
- _T("[failed to delete after reboot %s][0x%x]"), |
- from, hr)); |
- } |
- } |
- |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
- |
-HRESULT File::MoveAfterReboot(const TCHAR* from, const TCHAR* to) { |
- ASSERT1(from && *from); |
- |
- if (!File::Exists(from)) { |
- // File/directory doesn't exist, should this return failure or success? |
- // Decision: Failure. Because the caller can decide if it is really |
- // failure or not in his specific case. |
- UTIL_LOG(LEVEL_WARNING, (_T("[File::MoveAfterReboot]") |
- _T("[file doesn't exist][from %s]"), from)); |
- return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); |
- } |
- |
- DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT; |
- if (!File::IsDirectory(from)) { |
- // This flag valid only for files |
- flags |= MOVEFILE_REPLACE_EXISTING; |
- } |
- |
- if (!::MoveFileEx(from, to, flags)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::MoveAfterReboot]") |
- _T("[failed to MoveFileEx from '%s' to '%s'][0x%x]"), |
- from, to, hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-// See if we have any moves pending a reboot. Return SUCCESS if we do |
-// not encounter errors (not finding a move is not an error). We need to |
-// also check the value of *found_ptr for whether we actually found a move. |
-// On return, *value_multisz_ptr is the value within |
-// "PendingFileRenameOperations", but with any moves for in_directory removed |
-// from it. |
-// The prefix_match boolean controls whether we do an exact match on |
-// in_directory, or remove all entries with the in_directory prefix. |
-// NOTE: If the only values found were our own keys, the whole |
-// PendingFileRenameOperations MULTISZ needs to be deleted. |
-// This is signified by a returned *value_size_chars_ptr of 0. |
-HRESULT File::GetPendingRenamesValueMinusDir(const TCHAR* in_directory, |
- bool prefix_match, |
- TCHAR** value_multisz_ptr, |
- DWORD* value_size_chars_ptr, |
- bool* found_ptr) { |
- ASSERT1(in_directory && *in_directory); |
- |
- // Convert to references for easier-to-read-code: |
- TCHAR*& value_multisz = *value_multisz_ptr; |
- DWORD& value_size_chars = *value_size_chars_ptr; |
- bool& found = *found_ptr; |
- |
- // Initialize [out] parameters |
- value_multisz = NULL; |
- value_size_chars = 0; |
- found = false; |
- |
- // Locals mirroring the [out] parameters. |
- // We will only set the corresponding [out] parameters when we have something |
- // meaningful to return to the caller |
- scoped_array<TCHAR> value_multisz_local; |
- DWORD value_size_chars_local = 0; |
- |
- DWORD value_size_bytes = 0; |
- // Get the current value of the key |
- // If the Key is missing, that's totally acceptable. |
- RET_IF_FALSE( |
- RegKey::HasValue(kSessionManagerKey, kPendingFileRenameOps) && |
- SUCCEEDED(RegKey::GetValue(kSessionManagerKey, |
- kPendingFileRenameOps, |
- reinterpret_cast<byte**>(&value_multisz_local), |
- &value_size_bytes)), |
- S_OK); |
- |
- ASSERT1(value_multisz_local.get() || value_size_bytes == 0); |
- UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") |
- _T("[read multisz %d bytes]"), |
- value_size_bytes)); |
- RET_IF_FALSE(value_size_bytes > 0, S_OK); |
- |
- // The size should always be aligned to a TCHAR boundary, otherwise the key |
- // is corrupted. |
- ASSERT1((value_size_bytes % sizeof(TCHAR)) == 0); |
- RET_IF_FALSE((value_size_bytes % sizeof(TCHAR)) == 0, |
- HRESULT_FROM_WIN32(ERROR_BADKEY)); |
- // Valid size, so convert to TCHARs: |
- value_size_chars_local = value_size_bytes / sizeof(TCHAR); |
- |
- // Buffer must terminate with two nulls |
- ASSERT(value_size_chars_local >= 2 && |
- !value_multisz_local[value_size_chars_local - 1] && |
- !value_multisz_local[value_size_chars_local - 2], |
- (_T("buffer must terminate with two nulls"))); |
- RET_IF_FALSE(value_size_chars_local >= 2 && |
- !value_multisz_local[value_size_chars_local - 1] && |
- !value_multisz_local[value_size_chars_local - 2], |
- HRESULT_FROM_WIN32(ERROR_BADKEY)); |
- // Mark the end of the string. |
- // multisz_end will point at the character past end of buffer: |
- TCHAR* multisz_end = value_multisz_local.get() + value_size_chars_local; |
- |
- // We're looking for \??\C:\... The \??\ was |
- // added by the OS to the directory name we specified. |
- CString from_dir(_T("\\??\\")); |
- from_dir += in_directory; |
- DWORD from_dir_len = from_dir.GetLength(); |
- |
- // A MULTISZ is a list of null terminated strings, terminated by a double |
- // null. We keep two pointers marching along the string in parallel. |
- TCHAR* str_read = value_multisz_local.get(); |
- TCHAR* str_write = str_read; |
- |
- while ((str_read < multisz_end) && *str_read) { |
- size_t str_len = ::lstrlen(str_read); |
- // A FALSE here indicates a corrupt PendingFileRenameOperations |
- RET_IF_FALSE((str_read + str_len + 1) < multisz_end, |
- HRESULT_FROM_WIN32(ERROR_BADKEY)); |
- if (0 == String_StrNCmp(str_read, |
- from_dir, |
- from_dir_len + (prefix_match ? 0 : 1), |
- true)) { |
- // String matches, we want to remove this string, so advance only the |
- // read pointer - past this string and the replacement string. |
- UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") |
- _T("[skips past match '%s']"), |
- str_read)); |
- str_read += str_len + 1; |
- str_read += ::lstrlen(str_read) + 1; |
- continue; |
- } |
- // String doesn't match, we want to keep it. |
- if (str_read != str_write) { |
- // Here we're not in sync in the buffer, we've got to move two |
- // strings down. |
- UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") |
- _T("[copying some other deletion][%s][%s]"), |
- str_read, str_read + ::lstrlen(str_read) + 1)); |
- ASSERT1(str_write < str_read); |
- String_StrNCpy(str_write, str_read, str_len+1); |
- str_read += str_len + 1; |
- str_write += str_len + 1; |
- str_len = ::lstrlen(str_read); |
- String_StrNCpy(str_write, str_read, str_len+1); |
- str_read += str_len + 1; |
- str_write += str_len + 1; |
- } else { |
- // We're in sync in the buffer, advance both pointers past two strings |
- UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]") |
- _T("[skipping past some other deletion][%s][%s]"), |
- str_read, str_read + ::lstrlen(str_read) + 1)); |
- str_read += str_len + 1; |
- str_read += ::lstrlen(str_read) + 1; |
- str_write = str_read; |
- } |
- } |
- |
- // A FALSE here indicates a corrupt PendingFileRenameOperations |
- RET_IF_FALSE(str_read < multisz_end, |
- HRESULT_FROM_WIN32(ERROR_BADKEY)); |
- |
- if (str_read != str_write) { |
- // We found some values |
- found = true; |
- |
- if (str_write == value_multisz_local.get()) { |
- // The only values were our own keys, |
- // and the whole PendingFileRenameOperations |
- // value needs to be deleted. We do not populate |
- // value_size_chars or value_multisz in this case. |
- ASSERT1(!value_size_chars); |
- ASSERT1(!value_multisz); |
- } else { |
- // The last string should have a NULL terminator: |
- ASSERT1(str_write[-1] == '\0'); |
- RET_IF_FALSE(str_write[-1] == '\0', |
- HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); |
- // a REG_MULTI_SZ needs to be terminated with an extra NULL. |
- *str_write = '\0'; |
- ++str_write; |
- |
- // Populate value_size_chars and value_multisz in this case. |
- value_multisz = value_multisz_local.release(); |
- value_size_chars = str_write - value_multisz; |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-// Remove any moves pending a reboot from the PendingFileRenameOperations |
-// in the registry. |
-// The prefix_match boolean controls whether we do an exact match on |
-// in_directory, or remove all entries with the in_directory prefix. |
-HRESULT File::RemoveFromMovesPendingReboot(const TCHAR* in_directory, |
- bool prefix_match) { |
- ASSERT1(in_directory && *in_directory); |
- |
- bool found = false; |
- // scoped_array will free the value_multisz buffer on stack unwind: |
- scoped_array<TCHAR> value_multisz; |
- DWORD value_size_chars = 0; |
- HRESULT hr = GetPendingRenamesValueMinusDir(in_directory, |
- prefix_match, |
- address(value_multisz), |
- &value_size_chars, |
- &found); |
- if (SUCCEEDED(hr) && found) { |
- if (value_multisz.get() == NULL) { |
- // There's no point in writing an empty value_multisz. |
- // Let's delete the PendingFileRenameOperations value |
- UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]") |
- _T("[deleting PendingFileRenameOperations value]"))); |
- RET_IF_FAILED(RegKey::DeleteValue(kSessionManagerKey, |
- kPendingFileRenameOps)); |
- } else { |
- // Let's write the modified value_multisz into the |
- // PendingFileRenameOperations value |
- UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]") |
- _T("[rewriting multisz %d bytes]"), |
- value_size_chars * sizeof(TCHAR))); |
- RET_IF_FAILED(RegKey::SetValueMultiSZ( |
- kSessionManagerKey, |
- kPendingFileRenameOps, |
- reinterpret_cast<byte*>(value_multisz.get()), |
- value_size_chars * sizeof(TCHAR))); |
- } |
- } |
- |
- // Failure of GetPendingRenamesValueMinusDir() may indicate something |
- // seriously wrong with the system. Propogate error. |
- return hr; |
-} |
- |
-// Did the user try to uninstall a previous install of the same version, and |
-// we couldn't clean up without a reboot? |
-// We check if there are any moves pending a reboot from the |
-// PendingFileRenameOperations in the registry. |
-// The prefix_match boolean controls whether we do an exact match on |
-// in_directory, or check all entries with the in_directory prefix. |
-bool File::AreMovesPendingReboot(const TCHAR* in_directory, bool prefix_match) { |
- ASSERT1(in_directory && *in_directory); |
- |
- bool found = false; |
- // scoped_array will free the value_multisz buffer on stack unwind: |
- scoped_array<TCHAR> value_multisz; |
- DWORD value_size_chars = 0; |
- |
- if (SUCCEEDED(GetPendingRenamesValueMinusDir(in_directory, |
- prefix_match, |
- address(value_multisz), |
- &value_size_chars, |
- &found)) && found) { |
- return true; |
- } |
- |
- return false; |
-} |
- |
-HRESULT File::GetFileTime(const TCHAR* file_name, |
- FILETIME* created, |
- FILETIME* accessed, |
- FILETIME* modified) { |
- ASSERT1(file_name && *file_name); |
- |
- bool is_dir = IsDirectory(file_name); |
- // To obtain a handle to a directory, call the CreateFile function with |
- // the FILE_FLAG_BACKUP_SEMANTICS flag |
- scoped_hfile file_handle( |
- ::CreateFile(file_name, |
- FILE_READ_DATA, |
- FILE_SHARE_READ, |
- NULL, |
- OPEN_EXISTING, |
- is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL, |
- NULL)); |
- HRESULT hr = S_OK; |
- |
- if (!file_handle) { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[File::GetFileTime]") |
- _T("[failed to open file][%s][0x%x]"), file_name, hr)); |
- } else { |
- if (!::GetFileTime(get(file_handle), created, accessed, modified)) { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]") |
- _T("[failed to get file time][%s][0x%x]"), |
- file_name, hr)); |
- } |
- } |
- |
- return hr; |
-} |
- |
-HRESULT File::SetFileTime(const TCHAR* file_name, |
- const FILETIME* created, |
- const FILETIME* accessed, |
- const FILETIME* modified) { |
- ASSERT1(file_name && *file_name); |
- |
- bool is_dir = IsDirectory(file_name); |
- // To obtain a handle to a directory, call the CreateFile function with |
- // the FILE_FLAG_BACKUP_SEMANTICS flag |
- scoped_hfile file_handle( |
- ::CreateFile(file_name, |
- FILE_WRITE_ATTRIBUTES, |
- FILE_SHARE_WRITE, |
- NULL, |
- OPEN_EXISTING, |
- is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL, |
- NULL)); |
- HRESULT hr = S_OK; |
- |
- if (!file_handle) { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]") |
- _T("[failed to open file][%s][0x%x]"), |
- file_name, hr)); |
- } else { |
- BOOL res = ::SetFileTime(get(file_handle), created, accessed, modified); |
- if (!res) { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::SetFileTime]") |
- _T("[failed to set file time][%s][0x%x]"), |
- file_name, hr)); |
- } |
- } |
- |
- return hr; |
-} |
- |
-HRESULT File::Sync() { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- if (!::FlushFileBuffers(handle_)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::Sync]") |
- _T("[FlushFileBuffers failed][%s][0x%x]"), |
- file_name_, hr)); |
- return hr; |
- } |
- return S_OK; |
-} |
- |
-HRESULT File::SeekToBegin() { |
- return SeekFromBegin(0); |
-} |
- |
-HRESULT File::SeekFromBegin(uint32 n) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- if (::SetFilePointer(handle_, n, NULL, FILE_BEGIN) == |
- INVALID_SET_FILE_POINTER) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::SeekFromBegin]") |
- _T("[SetFilePointer failed][%s][0x%x]"), |
- file_name_, hr)); |
- return hr; |
- } |
- pos_ = n; |
- return S_OK; |
-} |
- |
-// read nLen bytes starting at position n |
-// returns number of bytes read |
-// |
-// async operations: |
-// |
-// async_id - identifier of a sequence of async operations, 0 for synchronous |
-// |
-// if the async operation has not been initiated, we initiate it |
-// if it is in progress we do nothing |
-// if it has been completed we return the data |
-// does not delete async data entry |
-HRESULT File::ReadAt(const uint32 offset, byte* buf, const uint32 len, |
- const uint32, uint32* bytes_read) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(buf); |
- ASSERT1(len); // reading 0 bytes is not valid (differs from CRT API) |
- |
- RET_IF_FAILED(SeekFromBegin(offset)); |
- |
- DWORD read = 0; |
- if (!::ReadFile(handle_, buf, len, &read, NULL)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]") |
- _T("[ReadFile failed][%s][0x%x]"), file_name_, hr)); |
- return hr; |
- } |
- |
- if (bytes_read) { |
- *bytes_read = read; |
- } |
- |
- return (read == len) ? S_OK : E_FAIL; |
-} |
- |
- |
-// reads up to max_len bytes from the start of the file |
-// not considered an error if there are less than max_len bytes read |
-// returns number of bytes read |
-HRESULT File::ReadFromStartOfFile(const uint32 max_len, |
- byte* buf, |
- uint32* bytes_read) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(buf); |
- ASSERT1(max_len); |
- |
- RET_IF_FAILED(SeekFromBegin(0)); |
- |
- uint32 file_len = 0; |
- RET_IF_FAILED(GetLength(&file_len)); |
- |
- if (!file_len) { |
- if (bytes_read) { |
- *bytes_read = 0; |
- } |
- return S_OK; |
- } |
- |
- uint32 len = max_len; |
- if (len > file_len) { |
- len = static_cast<uint32>(file_len); |
- } |
- |
- return Read(len, buf, bytes_read); |
-} |
- |
-// this function handles lines terminated with LF or CRLF |
-// all CR characters are removed from each line, and LF is assumed |
-// to be the end of line and is removed |
-HRESULT File::ReadLineAnsi(uint32 max_len, char* line, uint32* len) { |
- ASSERT1(line); |
- ASSERT1(max_len); |
- |
- char c = 0; |
- uint32 len_read = 0; |
- uint32 total_len = 0; |
- |
- while (SUCCEEDED(Read(1, reinterpret_cast<byte *>(&c), &len_read)) && |
- len_read && |
- c != '\n') { |
- if (total_len < max_len - 1 && c != '\r') { |
- line[total_len++] = c; |
- } |
- } |
- |
- ASSERT1(total_len < max_len); |
- line[total_len] = '\0'; |
- |
- if (len) { |
- *len = total_len; |
- } |
- |
- return (len_read || total_len) ? S_OK : E_FAIL; |
-} |
- |
-// used by ReadFromStartOfFile and ReadLineAnsi; not reading all requested bytes |
-// is not considered fatal |
-HRESULT File::Read(const uint32 len, byte* buf, uint32* bytes_read) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(buf); |
- ASSERT1(len); |
- |
- DWORD read = 0; |
- if (!::ReadFile(handle_, buf, len, &read, NULL)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]") |
- _T("[ReadFile failed][%s][0x%x]"), |
- file_name_, hr)); |
- return hr; |
- } |
- |
- if (bytes_read) { |
- *bytes_read = read; |
- } |
- |
- return S_OK; |
-} |
- |
- |
-// returns number of bytes written |
-HRESULT File::WriteAt(const uint32 offset, |
- const byte* buf, |
- const uint32 len, |
- uint32, |
- uint32* bytes_written) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(!read_only_); |
- ASSERT1(buf); |
- ASSERT1(len); |
- |
- RET_IF_FAILED(SeekFromBegin(offset)); |
- |
- return Write(buf, len, bytes_written); |
-} |
- |
- |
-// write buffer n times |
-HRESULT File::WriteN(const byte* buf, |
- const uint32 len, |
- const uint32 n, |
- uint32* bytes_written) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(!read_only_); |
- ASSERT1(buf); |
- ASSERT1(len); |
- ASSERT1(n); |
- |
- HRESULT hr = S_OK; |
- |
- uint32 total_wrote = 0; |
- |
- byte* temp_buf = const_cast<byte*>(buf); |
- |
- scoped_array<byte> encrypt_buf; |
- |
- uint32 to_go = n; |
- while (to_go) { |
- uint32 wrote = 0; |
- hr = Write(temp_buf, len, &wrote); |
- if (FAILED(hr)) { |
- if (bytes_written) { |
- *bytes_written = total_wrote; |
- } |
- return hr; |
- } |
- |
- total_wrote += wrote; |
- to_go--; |
- } |
- |
- if (bytes_written) { |
- *bytes_written = total_wrote; |
- } |
- return hr; |
-} |
- |
-// returns number of bytes written |
-HRESULT File::Write(const byte* buf, const uint32 len, uint32* bytes_written) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(!read_only_); |
- ASSERT1(buf); |
- ASSERT1(len); // writing 0 bytes is not valid (differs from CRT API) |
- |
- byte* b = const_cast<byte*>(buf); |
- |
- scoped_array<byte> encrypt_buf; |
- |
- DWORD wrote = 0; |
- if (!::WriteFile(handle_, b, len, &wrote, NULL)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[File::Write]") |
- _T("[WriteFile failed][%s][0x%x]"), |
- file_name_, hr)); |
- return hr; |
- } |
- |
- if (bytes_written) { |
- *bytes_written = wrote; |
- } |
- pos_ += wrote; |
- |
- return (wrote == len) ? S_OK : E_FAIL; |
-} |
- |
-HRESULT File::ClearAt(const uint32 offset, |
- const uint32 len, |
- uint32* bytes_written) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(!read_only_); |
- ASSERT1(len); |
- |
- byte zero[kZeroSize] = {0}; |
- uint32 to_go = len; |
- uint32 written = 0; |
- uint32 pos = offset; |
- |
- while (to_go) { |
- uint32 wrote = 0; |
- uint32 write_len = std::min(to_go, kZeroSize); |
- RET_IF_FAILED(WriteAt(pos, zero, write_len, 0, &wrote)); |
- |
- if (wrote != write_len) { |
- return E_FAIL; |
- } |
- pos += wrote; |
- written += wrote; |
- to_go -= write_len; |
- } |
- |
- if (bytes_written) { |
- *bytes_written = written; |
- } |
- return S_OK; |
-} |
- |
-// returns true on failure |
-// zeros new data if zero_data == true |
-HRESULT File::SetLength(const uint32 n, bool zero_data) { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- ASSERT1(!read_only_); |
- ASSERT1(n <= kMaxFileSize); |
- |
- HRESULT hr = S_OK; |
- |
- uint32 len = 0; |
- VERIFY1(SUCCEEDED(GetLength(&len))); |
- |
- if (len == n) { |
- return S_OK; |
- } |
- |
- // according to the documentation, the |
- // new space will not be initialized |
- if (n > len) { |
- if (zero_data) { |
- uint32 bytes_written = 0; |
- RET_IF_FAILED(ClearAt(len, n - len, &bytes_written)); |
- if (bytes_written != n - len) { |
- return E_FAIL; |
- } |
- } else { |
- byte zero = 0; |
- uint32 bytes_written = 0; |
- RET_IF_FAILED(WriteAt(n - 1, &zero, 1, 0, &bytes_written)); |
- if (bytes_written != 1) { |
- return E_FAIL; |
- } |
- } |
- } else { |
- SeekFromBegin(n); |
- SetEndOfFile(handle_); |
- } |
- |
- ASSERT1(SUCCEEDED(GetLength(&len)) && len == n); |
- |
- return S_OK; |
-} |
- |
-HRESULT File::ExtendInBlocks(const uint32 block_size, uint32 size_needed, |
- uint32* new_size, bool clear_new_space) { |
- ASSERT1(new_size); |
- |
- *new_size = size_needed; |
- |
- if (*new_size % block_size) { |
- *new_size += block_size - (*new_size % block_size); |
- } |
- |
- // is zero_data needed? may reduce fragmentation by causing the block to |
- // be written |
- return SetLength(*new_size, clear_new_space); |
-} |
- |
-// returns S_OK on success |
-HRESULT File::GetLength(uint32* length) { |
- ASSERT1(length); |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- DWORD len = GetFileSize(handle_, NULL); |
- if (len == INVALID_FILE_SIZE) { |
- ASSERT(false, (_T("cannot get file length"))); |
- return E_FAIL; |
- } |
- *length = len; |
- return S_OK; |
-} |
- |
-HRESULT File::Touch() { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- FILETIME file_time; |
- SetZero(file_time); |
- |
- ::GetSystemTimeAsFileTime(&file_time); |
- |
- if (!::SetFileTime(handle_, NULL, NULL, &file_time)) { |
- return HRESULTFromLastError(); |
- } |
- return S_OK; |
-} |
- |
-HRESULT File::Close() { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- HRESULT hr = S_OK; |
- if (!::CloseHandle(handle_)) { |
- hr = HRESULTFromLastError(); |
- } |
- |
- handle_ = INVALID_HANDLE_VALUE; |
- |
- return hr; |
-} |
- |
-// this is just for consistency with other classes; does not do anything |
-HRESULT File::Reload(uint32* number_errors) { |
- ASSERT1(number_errors); |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- *number_errors = 0; |
- return S_OK; |
-} |
- |
-// this is just for consistency with other classes; does not do anything |
-HRESULT File::Verify(uint32* number_errors) { |
- ASSERT1(number_errors); |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- *number_errors = 0; |
- return S_OK; |
-} |
- |
-// this is just for consistency with other classes; does not do anything |
-HRESULT File::Dump() { |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- return S_OK; |
-} |
- |
-// for consistency with other classes |
-HRESULT File::GetSizeOnDisk(uint64* size_on_disk) { |
- ASSERT1(size_on_disk); |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- uint32 len = 0; |
- RET_IF_FAILED(GetLength(&len)); |
- |
- *size_on_disk = len; |
- return S_OK; |
-} |
- |
-// for consistency with other classes |
-HRESULT File::GetReloadDiskSpaceNeeded(uint64* bytes_needed) { |
- ASSERT1(bytes_needed); |
- ASSERT1(handle_ != INVALID_HANDLE_VALUE); |
- |
- uint32 len = 0; |
- RET_IF_FAILED(GetLength(&len)); |
- |
- *bytes_needed = len; |
- return S_OK; |
-} |
- |
-// Get the file size |
-HRESULT File::GetFileSizeUnopen(const TCHAR* filename, uint32* out_size) { |
- ASSERT1(filename); |
- ASSERT1(out_size); |
- |
- WIN32_FILE_ATTRIBUTE_DATA data; |
- SetZero(data); |
- |
- if (!::GetFileAttributesEx(filename, ::GetFileExInfoStandard, &data)) { |
- return HRESULTFromLastError(); |
- } |
- |
- *out_size = data.nFileSizeLow; |
- |
- return S_OK; |
-} |
- |
-// Get the last time with a file was written to, and the size |
-HRESULT File::GetLastWriteTimeAndSize(const TCHAR* file_path, |
- SYSTEMTIME* out_time, |
- unsigned int* out_size) { |
- ASSERT1(file_path); |
- |
- WIN32_FIND_DATA wfd; |
- SetZero(wfd); |
- |
- HANDLE find = ::FindFirstFile(file_path, &wfd); |
- if (find == INVALID_HANDLE_VALUE) { |
- return HRESULTFromLastError(); |
- } |
- |
- ::FindClose(find); |
- |
- if (out_size) { |
- *out_size = wfd.nFileSizeLow; |
- } |
- |
- if (out_time) { |
- // If created time is newer than write time, then use that instead |
- // [it tends to be more relevant when copying files around] |
- FILETIME* latest_time = NULL; |
- if (::CompareFileTime(&wfd.ftCreationTime, &wfd.ftLastWriteTime) > 0) { |
- latest_time = &wfd.ftCreationTime; |
- } else { |
- latest_time = &wfd.ftLastWriteTime; |
- } |
- |
- if (!::FileTimeToSystemTime(latest_time, out_time)) { |
- return HRESULTFromLastError(); |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-bool File::AreFilesIdentical(const TCHAR* filename1, const TCHAR* filename2) { |
- UTIL_LOG(L4, (_T("[File::AreFilesIdentical][%s][%s]"), filename1, filename2)); |
- |
- uint32 file_size1 = 0; |
- HRESULT hr = File::GetFileSizeUnopen(filename1, &file_size1); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size1][0x%x]"), hr)); |
- return false; |
- } |
- |
- uint32 file_size2 = 0; |
- hr = File::GetFileSizeUnopen(filename2, &file_size2); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size2][0x%x]"), hr)); |
- return false; |
- } |
- |
- if (file_size1 != file_size2) { |
- UTIL_LOG(L3, (_T("[file_size1 != file_size2][%d][%d]"), |
- file_size1, file_size2)); |
- return false; |
- } |
- |
- File file1; |
- hr = file1.OpenShareMode(filename1, false, false, FILE_SHARE_READ); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[file1.OpenShareMode failed][0x%x]"), hr)); |
- return false; |
- } |
- |
- File file2; |
- hr = file2.OpenShareMode(filename2, false, false, FILE_SHARE_READ); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[file2.OpenShareMode failed][0x%x]"), hr)); |
- return false; |
- } |
- |
- static const uint32 kBufferSize = 0x10000; |
- std::vector<uint8> buffer1(kBufferSize); |
- std::vector<uint8> buffer2(kBufferSize); |
- uint32 bytes_left = file_size1; |
- |
- while (bytes_left > 0) { |
- uint32 bytes_to_read = std::min(bytes_left, kBufferSize); |
- uint32 bytes_read1 = 0; |
- uint32 bytes_read2 = 0; |
- |
- hr = file1.Read(bytes_to_read, &buffer1.front(), &bytes_read1); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[file1.Read failed][%d][%d][0x%x]"), |
- bytes_left, bytes_to_read, hr)); |
- return false; |
- } |
- |
- hr = file2.Read(bytes_to_read, &buffer2.front(), &bytes_read2); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[file2.Read failed][%d][%d][0x%x]"), |
- bytes_left, bytes_to_read, hr)); |
- return false; |
- } |
- |
- if (bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2) { |
- UTIL_LOG(LE, |
- (_T("[bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2]") |
- _T("[%d][%d][%d]"), bytes_to_read, bytes_read1, bytes_read2)); |
- return false; |
- } |
- |
- if (memcmp(&buffer1.front(), &buffer2.front(), bytes_read1) != 0) { |
- UTIL_LOG(L3, (_T("[memcmp failed][%d][%d]"), bytes_left, bytes_read1)); |
- return false; |
- } |
- |
- if (bytes_left < bytes_to_read) { |
- UTIL_LOG(LE, (_T("[bytes_left < bytes_to_read][%d][%d]"), |
- bytes_left, bytes_to_read)); |
- return false; |
- } |
- |
- bytes_left -= bytes_to_read; |
- } |
- |
- return true; |
-} |
- |
-FileLock::FileLock() { |
-} |
- |
-FileLock::~FileLock() { |
- Unlock(); |
-} |
- |
-HRESULT FileLock::Lock(const TCHAR* file) { |
- std::vector<CString> files; |
- files.push_back(file); |
- return Lock(files); |
-} |
- |
-HRESULT FileLock::Lock(const std::vector<CString>& files) { |
- ASSERT1(!files.empty()); |
- |
- // Try to lock all files |
- size_t curr_size = handles_.size(); |
- for (size_t i = 0; i < files.size(); ++i) { |
- scoped_hfile handle(::CreateFile(files[i], |
- GENERIC_READ, |
- FILE_SHARE_READ, |
- NULL, |
- OPEN_EXISTING, |
- FILE_ATTRIBUTE_NORMAL, |
- NULL)); |
- if (!handle) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[FileLock::Lock - failed to lock file][%s][0x%x]"), |
- files[i], HRESULTFromLastError())); |
- break; |
- } |
- handles_.push_back(release(handle)); |
- } |
- |
- // Cleanup if we fail to lock all the files |
- if (curr_size + files.size() < handles_.size()) { |
- for (size_t i = handles_.size() - 1; i >= curr_size; --i) { |
- VERIFY(::CloseHandle(handles_[i]), (_T(""))); |
- handles_.pop_back(); |
- } |
- return E_FAIL; |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT FileLock::Unlock() { |
- for (size_t i = 0; i < handles_.size(); ++i) { |
- VERIFY(::CloseHandle(handles_[i]), (_T(""))); |
- } |
- handles_.clear(); |
- return S_OK; |
-} |
- |
- |
-// path_name: the directory to watch |
-// watch_subtree: watch all subdirectory changes or |
-// only immediate child values |
-// notify_filter: See the documentation for FindFirstChangeNotification |
-FileWatcher::FileWatcher(const TCHAR* path_name, bool watch_subtree, |
- DWORD notify_filter) |
- : path_name_(path_name), |
- watch_subtree_(watch_subtree), |
- notify_filter_(notify_filter) { |
- ASSERT1(path_name && *path_name); |
- UTIL_LOG(L3, (_T("[FileWatcher::FileWatcher][%s]"), path_name)); |
-} |
- |
-// Get the event that is signaled on store changes. |
-HANDLE FileWatcher::change_event() const { |
- ASSERT(valid(change_event_), (_T("call FileWatcher::SetupEvent first"))); |
- return get(change_event_); |
-} |
- |
- |
-// Called to create/reset the event that gets signaled |
-// any time the store changes. Access the created |
-// event using change_event(). |
-HRESULT FileWatcher::EnsureEventSetup() { |
- UTIL_LOG(L3, (_T("[FileWatcher::EnsureEventSetup]"))); |
- if (!valid(change_event_)) { |
- reset(change_event_, ::FindFirstChangeNotification(path_name_, |
- watch_subtree_, |
- notify_filter_)); |
- if (!valid(change_event_)) { |
- ASSERT(false, (_T("unable to get file change notification"))); |
- return E_FAIL; |
- } |
- // path name was only needed to set-up the event and now that is done.... |
- path_name_.Empty(); |
- return S_OK; |
- } |
- |
- // if the event is set-up and no changes have occurred, |
- // then there is no need to re-setup the event. |
- if (valid(change_event_) && !HasChangeOccurred()) { |
- return NOERROR; |
- } |
- |
- return ::FindNextChangeNotification(get(change_event_)) ? S_OK : E_FAIL; |
-} |
- |
-} // namespace omaha |
- |