Index: base/utils.cc |
diff --git a/base/utils.cc b/base/utils.cc |
deleted file mode 100644 |
index fbd0f0a8696756c2d175c85a0d609b056e3991c7..0000000000000000000000000000000000000000 |
--- a/base/utils.cc |
+++ /dev/null |
@@ -1,2044 +0,0 @@ |
-// Copyright 2003-2010 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. |
-// ======================================================================== |
- |
-#include "omaha/base/utils.h" |
- |
-#include <ras.h> |
-#include <regstr.h> |
-#include <urlmon.h> |
-#include <wincrypt.h> |
-#include <ATLComTime.h> |
-#include <atlpath.h> |
-#include <map> |
-#include <vector> |
-#include "base/basictypes.h" |
-#include "base/scoped_ptr.h" |
-#include "omaha/base/app_util.h" |
-#include "omaha/base/const_addresses.h" |
-#include "omaha/base/const_config.h" |
-#include "omaha/base/const_timeouts.h" |
-#include "omaha/base/const_object_names.h" |
-#include "omaha/base/debug.h" |
-#include "omaha/base/error.h" |
-#include "omaha/base/file.h" |
-#include "omaha/base/logging.h" |
-#include "omaha/base/process.h" |
-#include "omaha/base/reg_key.h" |
-#include "omaha/base/safe_format.h" |
-#include "omaha/base/scope_guard.h" |
-#include "omaha/base/shell.h" |
-#include "omaha/base/scoped_any.h" |
-#include "omaha/base/string.h" |
-#include "omaha/base/system.h" |
-#include "omaha/base/system_info.h" |
-#include "omaha/base/time.h" |
-#include "omaha/base/user_info.h" |
-#include "omaha/base/user_rights.h" |
-#include "omaha/base/vistautil.h" |
- |
-namespace omaha { |
- |
-namespace { |
- |
-// Private object namespaces for Vista processes. |
-const TCHAR* const kGoopdateBoundaryDescriptor = _T("GoogleUpdate_BD"); |
-const TCHAR* const kGoopdatePrivateNamespace = _T("GoogleUpdate"); |
-const TCHAR* const kGoopdatePrivateNamespacePrefix = _T("GoogleUpdate\\"); |
- |
-// Helper for IsPrivateNamespaceAvailable(). |
-// For simplicity, the handles opened here are leaked. We need these until |
-// process exit, at which point they will be cleaned up automatically by the OS. |
-bool EnsurePrivateNamespaceAvailable() { |
- HANDLE boundary_descriptor = |
- CreateBoundaryDescriptorWWrap(kGoopdateBoundaryDescriptor, 0); |
- if (NULL == boundary_descriptor) { |
- DWORD last_error(::GetLastError()); |
- UTIL_LOG(LE, (_T("CreateBoundaryDescriptor failed[%d]"), last_error)); |
- return false; |
- } |
- |
- char sid[SECURITY_MAX_SID_SIZE] = {0}; |
- DWORD size = sizeof(sid); |
- // Mark the boundary descriptor with the Admins Group SID. Consequently, all |
- // admins, including SYSTEM, will create objects in the same private |
- // namespace. |
- if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, sid, &size)) { |
- UTIL_LOG(LE, (_T("[::CreateWellKnownSid failed][%d]"), ::GetLastError())); |
- return false; |
- } |
- if (!AddSIDToBoundaryDescriptorWrap(&boundary_descriptor, sid)) { |
- UTIL_LOG(LE, (_T("[AddSIDToBoundaryDescriptor failed][%d]"), |
- ::GetLastError())); |
- return false; |
- } |
- |
- NamedObjectAttributes attr; |
- GetAdminDaclSecurityAttributes(&attr.sa, GENERIC_ALL); |
- // The private namespace created here will be used to create objects of the |
- // form "GoogleUpdate\xyz". As the article "Object Namespaces" on MSDN |
- // explains, these kernel objects are safe from squatting attacks from lower |
- // integrity processes. |
- HANDLE namespace_handle = |
- CreatePrivateNamespaceWWrap(&attr.sa, |
- boundary_descriptor, |
- kGoopdatePrivateNamespace); |
- if (namespace_handle) { |
- return true; |
- } |
- ASSERT(ERROR_ALREADY_EXISTS == ::GetLastError(), |
- (_T("CreatePrivateNamespaceW failed: %d"), ::GetLastError())); |
- |
- // Another process has already created the namespace. Attempt to open. |
- namespace_handle = OpenPrivateNamespaceWWrap(boundary_descriptor, |
- kGoopdatePrivateNamespace); |
- if (namespace_handle || ::GetLastError() == ERROR_DUP_NAME) { |
- // ERROR_DUP_NAME indicates that we have called CreatePrivateNamespaceWWrap |
- // or OpenPrivateNamespaceWWrap before in the same process. Either way, we |
- // can now create objects prefixed with our private namespace. |
- return true; |
- } |
- |
- ASSERT(namespace_handle, (_T("[Could not open private namespace][%d]"), |
- ::GetLastError())); |
- return false; |
-} |
- |
-} // namespace |
- |
-// Returns 0 if an error occurs. |
-ULONGLONG VersionFromString(const CString& s) { |
- int pos(0); |
- unsigned int quad[4] = {0, 0, 0, 0}; |
- |
- for (int i = 0; i < 4; ++i) { |
- CString q = s.Tokenize(_T("."), pos); |
- if (pos == -1) { |
- return 0; |
- } |
- |
- int quad_value(0); |
- if (!String_StringToDecimalIntChecked(q, &quad_value)) { |
- return 0; |
- } |
- |
- quad[i] = static_cast<unsigned int>(quad_value); |
- |
- if (kuint16max < quad[i]) { |
- return 0; |
- } |
- } |
- |
- if (s.GetLength() + 1 != pos) { |
- return 0; |
- } |
- |
- return MAKEDLLVERULL(quad[0], quad[1], quad[2], quad[3]); |
-} |
- |
-CString StringFromVersion(ULONGLONG version) { |
- const WORD version_major = HIWORD(version >> 32); |
- const WORD version_minor = LOWORD(version >> 32); |
- const WORD version_build = HIWORD(version); |
- const WORD version_patch = LOWORD(version); |
- |
- CString version_string; |
- version_string.Format((_T("%u.%u.%u.%u")), |
- version_major, |
- version_minor, |
- version_build, |
- version_patch); |
- return version_string; |
-} |
- |
-CString GetCurrentDir() { |
- TCHAR cur_dir[MAX_PATH] = {0}; |
- if (!::GetCurrentDirectory(MAX_PATH, cur_dir)) { |
- return CString(_T('.')); |
- } |
- return CString(cur_dir); |
-} |
- |
-HRESULT GetNewFileNameInDirectory(const CString& dir, CString* file_name) { |
- ASSERT1(file_name); |
- |
- GUID guid = {0}; |
- HRESULT hr = ::CoCreateGuid(&guid); |
- if (FAILED(hr)) { |
- CORE_LOG(LEVEL_WARNING, (_T("[CoCreateGuid failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- CString guid_file_name = GuidToString(guid); |
- CPath file_path(dir); |
- file_path.Append(guid_file_name); |
- |
- *file_name = static_cast<const TCHAR*>(file_path); |
- return S_OK; |
-} |
- |
-// determines if a time is in the distant past, present, or future |
-TimeCategory GetTimeCategory(const time64 system_time) { |
- time64 now = GetCurrent100NSTime(); |
- |
- // Times more than a few days in the future are wrong [I will allow a little |
- // leeway, since it could be set in another future time zone, or a program |
- // that likes UNC]] |
- if (system_time > (now + kDaysTo100ns * 5)) { |
- return FUTURE; |
- } |
- |
- // times more than 40 years ago are wrong |
- if (system_time < (now - kDaysTo100ns * 365 * 40)) { |
- return PAST; |
- } |
- |
- return PRESENT; |
-} |
- |
-// Determine if a given time is probably valid |
-bool IsValidTime(const time64 t) { |
- return (GetTimeCategory(t) == PRESENT); |
-} |
- |
-LARGE_INTEGER MSto100NSRelative(DWORD ms) { |
- const __int64 convert_ms_to_100ns_units = 1000 /*ms/us*/ * 10 /*us/100ns*/; |
- __int64 timeout_100ns = static_cast<__int64>(ms) * convert_ms_to_100ns_units; |
- LARGE_INTEGER timeout = {0}; |
- timeout.QuadPart = -timeout_100ns; |
- return timeout; |
-} |
- |
-// Local System and admins get admin_access_mask. Authenticated non-admins get |
-// non_admin_access_mask access. |
-void GetEveryoneDaclSecurityDescriptor(CSecurityDesc* sd, |
- ACCESS_MASK admin_access_mask, |
- ACCESS_MASK non_admin_access_mask) { |
- ASSERT1(sd); |
- |
- CDacl dacl; |
- dacl.AddAllowedAce(Sids::System(), admin_access_mask); |
- dacl.AddAllowedAce(Sids::Admins(), admin_access_mask); |
- dacl.AddAllowedAce(Sids::Interactive(), non_admin_access_mask); |
- |
- sd->SetDacl(dacl); |
- sd->MakeAbsolute(); |
-} |
- |
-void GetAdminDaclSecurityDescriptor(CSecurityDesc* sd, ACCESS_MASK accessmask) { |
- ASSERT1(sd); |
- |
- CDacl dacl; |
- dacl.AddAllowedAce(Sids::System(), accessmask); |
- dacl.AddAllowedAce(Sids::Admins(), accessmask); |
- |
- sd->SetOwner(Sids::Admins()); |
- sd->SetGroup(Sids::Admins()); |
- sd->SetDacl(dacl); |
- sd->MakeAbsolute(); |
-} |
- |
-void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr, |
- ACCESS_MASK accessmask) { |
- ASSERT1(sec_attr); |
- CSecurityDesc sd; |
- GetAdminDaclSecurityDescriptor(&sd, accessmask); |
- sec_attr->Set(sd); |
-} |
- |
-HRESULT InitializeClientSecurity() { |
- return ::CoInitializeSecurity( |
- NULL, |
- -1, |
- NULL, // Let COM choose what authentication services to register. |
- NULL, |
- RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Data integrity and encryption. |
- RPC_C_IMP_LEVEL_IMPERSONATE, // Allow server to impersonate. |
- NULL, |
- EOAC_DYNAMIC_CLOAKING, |
- NULL); |
-} |
- |
-HRESULT InitializeServerSecurity(bool allow_calls_from_medium) { |
- CSecurityDesc sd; |
- DWORD eole_auth_capabilities = EOAC_DYNAMIC_CLOAKING; |
- if (allow_calls_from_medium) { |
- GetEveryoneDaclSecurityDescriptor(&sd, |
- COM_RIGHTS_EXECUTE, |
- COM_RIGHTS_EXECUTE); |
- sd.SetOwner(Sids::Admins()); |
- sd.SetGroup(Sids::Admins()); |
- } else if (user_info::IsRunningAsSystem()) { |
- GetAdminDaclSecurityDescriptor(&sd, COM_RIGHTS_EXECUTE); |
- } |
- |
- HRESULT hr = ::CoInitializeSecurity( |
- const_cast<SECURITY_DESCRIPTOR*>(sd.GetPSECURITY_DESCRIPTOR()), |
- -1, |
- NULL, |
- NULL, |
- RPC_C_AUTHN_LEVEL_PKT_PRIVACY, |
- RPC_C_IMP_LEVEL_IDENTIFY, |
- NULL, |
- eole_auth_capabilities, |
- NULL); |
- ASSERT(SUCCEEDED(hr), (_T("[InitializeServerSecurity failed][0x%x]"), hr)); |
- return hr; |
-} |
- |
-// The IGlobalOptions interface is supported from Vista onwards. Callers |
-// should probably ignore the HRESULT returned, since it is not a critical error |
-// if turning off exception handling fails. |
-HRESULT DisableCOMExceptionHandling() { |
- CComPtr<IGlobalOptions> options; |
- HRESULT hr = options.CoCreateInstance(CLSID_GlobalOptions); |
- if (SUCCEEDED(hr)) { |
- hr = options->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE); |
- } |
- |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[DisableCOMExceptionHandling failed][0x%x]"), hr)); |
- } |
- |
- return hr; |
-} |
- |
-// This function is not thread-safe. |
-bool IsPrivateNamespaceAvailable(bool is_machine) { |
- static bool is_initialized = false; |
- static bool is_available = false; |
- |
- if (!is_machine) { |
- // TODO(Omaha): From a security viewpoint, private namespaces do not add |
- // much value for the User Omaha. But from a uniformity perspective, makes |
- // sense to use for both. |
- return false; |
- } |
- |
- if (is_initialized) { |
- return is_available; |
- } |
- |
- if (!SystemInfo::IsRunningOnVistaOrLater()) { |
- is_available = false; |
- is_initialized = true; |
- return false; |
- } |
- |
- is_available = EnsurePrivateNamespaceAvailable(); |
- is_initialized = true; |
- return is_available; |
-} |
- |
- |
-void GetNamedObjectAttributes(const TCHAR* base_name, |
- bool is_machine, |
- NamedObjectAttributes* attr) { |
- ASSERT1(base_name); |
- ASSERT1(attr); |
- |
- // TODO(Omaha): Enable this code after we have a better understanding of |
- // Private Object Namespaces. |
-#if 0 |
- if (IsPrivateNamespaceAvailable(is_machine)) { |
- attr->name = kGoopdatePrivateNamespacePrefix; |
- } else { |
- ASSERT1(!SystemInfo::IsRunningOnVistaOrLater()); |
-#endif |
- |
- attr->name = omaha::kGlobalPrefix; |
- |
- if (!is_machine) { |
- CString user_sid; |
- VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid))); |
- attr->name += user_sid; |
- } else { |
- // Grant access to administrators and system. |
- GetAdminDaclSecurityAttributes(&attr->sa, GENERIC_ALL); |
- } |
- |
- attr->name += base_name; |
- UTIL_LOG(L1, (_T("[GetNamedObjectAttributes][named_object=%s]"), attr->name)); |
-} |
- |
-// For now, required_ace_flags is only supported for SE_REGISTRY_KEY objects. |
-// INHERITED_ACE may be added to the read ACE flags, so it is excluded from |
-// the comparison with required_ace_flags. |
-HRESULT AddAllowedAce(const TCHAR* object_name, |
- SE_OBJECT_TYPE object_type, |
- const CSid& sid, |
- ACCESS_MASK required_permissions, |
- uint8 required_ace_flags) { |
- ASSERT1(SE_REGISTRY_KEY == object_type || !required_ace_flags); |
- ASSERT1(0 == (required_ace_flags & INHERITED_ACE)); |
- |
- CDacl dacl; |
- if (!AtlGetDacl(object_name, object_type, &dacl)) { |
- return HRESULTFromLastError(); |
- } |
- |
- int ace_count = dacl.GetAceCount(); |
- for (int i = 0; i < ace_count; ++i) { |
- CSid sid_entry; |
- ACCESS_MASK existing_permissions = 0; |
- BYTE existing_ace_flags = 0; |
- dacl.GetAclEntry(i, |
- &sid_entry, |
- &existing_permissions, |
- NULL, |
- &existing_ace_flags); |
- if (sid_entry == sid && |
- required_permissions == (existing_permissions & required_permissions) && |
- required_ace_flags == (existing_ace_flags & ~INHERITED_ACE)) { |
- return S_OK; |
- } |
- } |
- |
- if (!dacl.AddAllowedAce(sid, required_permissions, required_ace_flags) || |
- !AtlSetDacl(object_name, object_type, dacl)) { |
- return HRESULTFromLastError(); |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT CreateDir(const TCHAR* in_dir, |
- LPSECURITY_ATTRIBUTES security_attr) { |
- ASSERT1(in_dir); |
- CString path; |
- if (!PathCanonicalize(CStrBuf(path, MAX_PATH), in_dir)) { |
- return E_FAIL; |
- } |
- // Standardize path on backslash so Find works. |
- path.Replace(_T('/'), _T('\\')); |
- int next_slash = path.Find(_T('\\')); |
- while (true) { |
- int len = 0; |
- if (next_slash == -1) { |
- len = path.GetLength(); |
- } else { |
- len = next_slash; |
- } |
- CString dir(path.Left(len)); |
- // The check for File::Exists should not be needed. However in certain |
- // cases, i.e. when the program is run from a n/w drive or from the |
- // root drive location, the first CreateDirectory fails with an |
- // E_ACCESSDENIED instead of a ALREADY_EXISTS. Hence we protect the call |
- // with the exists. |
- if (!File::Exists(dir)) { |
- if (!::CreateDirectory(dir, security_attr)) { |
- DWORD error = ::GetLastError(); |
- if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) { |
- return HRESULT_FROM_WIN32(error); |
- } |
- } |
- } |
- if (next_slash == -1) { |
- break; |
- } |
- next_slash = path.Find(_T('\\'), next_slash + 1); |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT GetFolderPath(int csidl, CString* path) { |
- if (!path) { |
- return E_INVALIDARG; |
- } |
- |
- TCHAR buffer[MAX_PATH] = {0}; |
- HRESULT hr = ::SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buffer); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- *path = buffer; |
- return S_OK; |
-} |
- |
-// Delete directory files. If failed, try to schedule deletion at next reboot |
-HRESULT DeleteDirectoryFiles(const TCHAR* dir_name) { |
- ASSERT1(dir_name); |
- return DeleteWildcardFiles(dir_name, _T("*")); |
-} |
- |
-// Delete a set of wildcards within dir_name. |
-// If unable to delete immediately, try to schedule deletion at next reboot |
-HRESULT DeleteWildcardFiles(const TCHAR* dir_name, const TCHAR* wildcard_name) { |
- ASSERT1(dir_name); |
- ASSERT1(wildcard_name); |
- |
- HRESULT hr = S_OK; |
- |
- WIN32_FIND_DATA find_data; |
- SetZero(find_data); |
- |
- CString find_file(dir_name); |
- find_file += _T('\\'); |
- find_file += wildcard_name; |
- |
- scoped_hfind hfind(::FindFirstFile(find_file, &find_data)); |
- if (!hfind) { |
- if (::GetLastError() == ERROR_NO_MORE_FILES) { |
- return S_OK; |
- } else { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") |
- _T("[failed to get first file][0x%08x]"), hr)); |
- return hr; |
- } |
- } |
- |
- do { |
- if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
- CString specific_file_name(dir_name); |
- specific_file_name += _T('\\'); |
- specific_file_name += find_data.cFileName; |
- if (!::DeleteFile(specific_file_name)) { |
- if (!SUCCEEDED(hr = File::DeleteAfterReboot(specific_file_name))) { |
- UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") |
- _T("[failed to delete after reboot]") |
- _T("[%s][0x%08x]"), specific_file_name, hr)); |
- } |
- } |
- } |
- } while (::FindNextFile(get(hfind), &find_data)); |
- |
- if (::GetLastError() != ERROR_NO_MORE_FILES) { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") |
- _T("[failed to get next file][0x%08x]"), hr)); |
- } |
- |
- return hr; |
-} |
- |
-// Delete directory and files within. If failed, try to schedule deletion at |
-// next reboot |
-// TODO(Omaha) - the code to delete the directory is complicated, |
-// especially the way the result code is built from hr and hr1. I wonder if we |
-// could simplify this by reimplementing it on top of SHFileOperation and |
-// also save a few tens of bytes in the process. |
-HRESULT DeleteDirectory(const TCHAR* dir_name) { |
- ASSERT1(dir_name); |
- |
- if (!SafeDirectoryNameForDeletion(dir_name)) { |
- return E_FAIL; |
- } |
- |
- // Make sure the directory exists (it is ok if it doesn't) |
- DWORD dir_attributes = ::GetFileAttributes(dir_name); |
- if (dir_attributes == INVALID_FILE_ATTRIBUTES) { |
- if (::GetLastError() == ERROR_FILE_NOT_FOUND) |
- return S_OK; // Ok if directory is missing |
- else |
- return HRESULTFromLastError(); |
- } |
- // Confirm it is a directory |
- if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) { |
- return E_FAIL; |
- } |
- |
- // Try to delete all files at best effort |
- // Return the first HRESULT error encountered |
- |
- // First delete all the normal files |
- HRESULT hr = DeleteDirectoryFiles(dir_name); |
- |
- // Recursively delete any subdirectories |
- |
- WIN32_FIND_DATA find_data = {0}; |
- |
- CString find_file(dir_name); |
- find_file += _T("\\*"); |
- |
- // Note that the follows are enclosed in a block because we need to close the |
- // find handle before deleting the directorty itself |
- { |
- scoped_hfind hfind(::FindFirstFile(find_file, &find_data)); |
- if (!hfind) { |
- if (::GetLastError() == ERROR_NO_MORE_FILES) { |
- return hr; |
- } else { |
- HRESULT hr1 = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[DeleteDirectory]") |
- _T("[failed to get first file][0x%08x]"), hr1)); |
- return SUCCEEDED(hr) ? hr1 : hr; |
- } |
- } |
- |
- do { |
- if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { |
- if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 || |
- String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) { |
- continue; |
- } |
- |
- CString sub_dir(dir_name); |
- sub_dir += _T("\\"); |
- sub_dir += find_data.cFileName; |
- HRESULT hr1 = DeleteDirectory(sub_dir); |
- if (SUCCEEDED(hr) && FAILED(hr1)) { |
- hr = hr1; |
- } |
- } |
- } |
- while (::FindNextFile(get(hfind), &find_data)); |
- } |
- |
- // Delete the empty directory itself |
- if (!::RemoveDirectory(dir_name)) { |
- HRESULT hr1 = E_FAIL; |
- if (FAILED(hr1 = File::DeleteAfterReboot(dir_name))) { |
- UTIL_LOG(LE, (_T("[DeleteDirectory][failed to delete after reboot]") |
- _T("[%s][0x%08x]"), dir_name, hr1)); |
- } |
- |
- if (SUCCEEDED(hr) && FAILED(hr1)) { |
- hr = hr1; |
- } |
- } |
- |
- return hr; |
-} |
- |
-// Returns true if this directory name is 'safe' for deletion (doesn't contain |
-// "..", doesn't specify a drive root) |
-bool SafeDirectoryNameForDeletion(const TCHAR* dir_name) { |
- ASSERT1(dir_name); |
- |
- // empty name isn't allowed |
- if (!(dir_name && *dir_name)) { |
- return false; |
- } |
- |
- // require a character other than \/:. after the last : |
- // disallow anything with ".." |
- bool ok = false; |
- for (const TCHAR* s = dir_name; *s; ++s) { |
- if (*s != _T('\\') && *s != _T('/') && *s != _T(':') && *s != _T('.')) { |
- ok = true; |
- } |
- if (*s == _T('.') && s > dir_name && *(s-1) == _T('.')) { |
- return false; |
- } |
- if (*s == _T(':')) { |
- ok = false; |
- } |
- } |
- return ok; |
-} |
- |
-// Utility function that deletes either a file or directory, |
-// before or after reboot |
-HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname) { |
- if (!File::Exists(targetname)) { |
- return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); |
- } |
- |
- HRESULT hr = E_FAIL; |
- if (File::IsDirectory(targetname)) { |
- // DeleteDirectory will schedule deletion at next reboot if it cannot delete |
- // immediately. |
- hr = DeleteDirectory(targetname); |
- } else { |
- hr = File::Remove(targetname); |
- // If failed, schedule deletion at next reboot |
- if (FAILED(hr)) { |
- UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]") |
- _T("[trying to delete after reboot][%s]"), targetname)); |
- hr = File::DeleteAfterReboot(targetname); |
- } |
- } |
- |
- if (FAILED(hr)) { |
- UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]") |
- _T("[failed to delete][%s][0x%08x]"), targetname, hr)); |
- } |
- |
- return hr; |
-} |
- |
- |
-// Internal implementation of the safe version of getting size of all files in |
-// a directory. It is able to abort the counting if one of the maximum criteria |
-// is reached. |
-HRESULT InternalSafeGetDirectorySize(const TCHAR* dir_name, |
- uint64* size, |
- HANDLE shutdown_event, |
- uint64 max_size, |
- int curr_file_count, |
- int max_file_count, |
- int curr_depth, |
- int max_depth, |
- DWORD end_time_ms) { |
- ASSERT1(dir_name && *dir_name); |
- ASSERT1(size); |
- |
- CString dir_find_name = String_MakeEndWith(dir_name, _T("\\"), false); |
- dir_find_name += _T("*"); |
- WIN32_FIND_DATA find_data = {0}; |
- scoped_hfind hfind(::FindFirstFile(dir_find_name, &find_data)); |
- if (!hfind) { |
- return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK : |
- HRESULTFromLastError(); |
- } |
- |
- do { |
- // Bail out if shutting down |
- if (shutdown_event && IsHandleSignaled(shutdown_event)) { |
- return E_ABORT; |
- } |
- |
- // Bail out if reaching maximum running time |
- if (end_time_ms && ::GetTickCount() >= end_time_ms) { |
- UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") |
- _T("[reaching max running time][%s][%u]"), |
- dir_name, end_time_ms)); |
- return E_ABORT; |
- } |
- |
- // Skip reparse point since it might be a hard link which could cause an |
- // infinite recursive directory loop. |
- if (find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { |
- continue; |
- } |
- |
- if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { |
- // Skip . and .. |
- if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 || |
- String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) { |
- continue; |
- } |
- |
- // Bail out if reaching maximum depth |
- if (max_depth && curr_depth + 1 >= max_depth) { |
- UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") |
- _T("[reaching max depth][%s][%u]"), dir_name, max_depth)); |
- return E_ABORT; |
- } |
- |
- // Walk over sub-directory |
- CString sub_dir_name = String_MakeEndWith(dir_name, _T("\\"), false); |
- sub_dir_name += find_data.cFileName; |
- RET_IF_FAILED(InternalSafeGetDirectorySize(sub_dir_name, |
- size, |
- shutdown_event, |
- max_size, |
- curr_file_count, |
- max_file_count, |
- curr_depth + 1, |
- max_depth, |
- end_time_ms)); |
- } else { |
- // Bail out if reaching maximum number of files |
- ++curr_file_count; |
- if (max_file_count && curr_file_count >= max_file_count) { |
- UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") |
- _T("[reaching max file count][%s][%u]"), |
- dir_name, max_file_count)); |
- return E_ABORT; |
- } |
- |
- // Count the file size |
- uint64 file_size = |
- ((static_cast<uint64>((find_data.nFileSizeHigh)) << 32)) + |
- static_cast<uint64>(find_data.nFileSizeLow); |
- *size += file_size; |
- |
- // Bail out if reaching maximum size |
- if (max_size && *size >= max_size) { |
- UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") |
- _T("[reaching max size][%s][%u]"), dir_name, max_size)); |
- return E_ABORT; |
- } |
- } |
- } while (::FindNextFile(get(hfind), &find_data)); |
- |
- return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK : |
- HRESULTFromLastError(); |
-} |
- |
-// The safe version of getting size of all files in a directory |
-// It is able to abort the counting if one of the maximum criteria is reached |
-HRESULT SafeGetDirectorySize(const TCHAR* dir_name, |
- uint64* size, |
- HANDLE shutdown_event, |
- uint64 max_size, |
- int max_file_count, |
- int max_depth, |
- int max_running_time_ms) { |
- ASSERT1(dir_name && *dir_name); |
- ASSERT1(size); |
- |
- *size = 0; |
- |
- DWORD end_time = 0; |
- if (max_running_time_ms > 0) { |
- end_time = ::GetTickCount() + max_running_time_ms; |
- } |
- return InternalSafeGetDirectorySize(dir_name, |
- size, |
- shutdown_event, |
- max_size, |
- 0, |
- max_file_count, |
- 0, |
- max_depth, |
- end_time); |
-} |
- |
-// Get size of all files in a directory |
-HRESULT GetDirectorySize(const TCHAR* dir_name, uint64* size) { |
- ASSERT1(dir_name && *dir_name); |
- ASSERT1(size); |
- return SafeGetDirectorySize(dir_name, size, NULL, 0, 0, 0, 0); |
-} |
- |
-// Handles the logic to determine the handle that was signaled |
-// as a result of calling *WaitForMultipleObjects. |
-HRESULT GetSignaledObjectPosition(uint32 cnt, DWORD res, uint32* pos) { |
- ASSERT1(pos); |
- |
- if (res == WAIT_FAILED) { |
- return S_FALSE; |
- } |
- |
-#pragma warning(disable : 4296) |
- // C4296: '>=' : expression is always true |
- if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cnt)) { |
- *pos = res - WAIT_OBJECT_0; |
- return S_OK; |
- } |
-#pragma warning(default : 4296) |
- |
- if ((res >= WAIT_ABANDONED_0) && (res < WAIT_ABANDONED_0 + cnt)) { |
- *pos = res - WAIT_ABANDONED_0; |
- return S_OK; |
- } |
- return E_INVALIDARG; |
-} |
- |
-// Supports all of the other WaitWithMessage* functions. |
-// |
-// Returns: |
-// S_OK when the message loop should continue. |
-// S_FALSE when it receives something that indicates the |
-// loop should quit. |
-// E_* only when GetMessage failed |
-// |
-// This function is not exposed outside of this file. Only |
-// friendly wrappers of it are. |
-HRESULT WaitWithMessageLoopAnyInternal( |
- const HANDLE* phandles, |
- uint32 cnt, |
- uint32* pos, |
- MessageHandlerInternalInterface* message_handler) { |
- ASSERT1(pos && message_handler); |
- // cnt and phandles are either both zero or both not zero. |
- ASSERT1(!cnt == !phandles); |
- |
- // Loop until an error happens or the wait is satisfied by a signaled |
- // object or an abandoned mutex. |
- for (;;) { |
- MSG msg = {0}; |
- |
- // Process the messages in the input queue. |
- while (::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) { |
- BOOL ret = false; |
- if ((ret = ::GetMessage(&msg, NULL, 0, 0)) != 0) { |
- if (ret == -1) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[WaitWithMessageLoopAnyInternal]") |
- _T("[GetMessage failed][0x%08x]"), hr)); |
- return hr; |
- } |
- message_handler->Process(&msg, &phandles, &cnt); |
- } else { |
- // We need to re-post the quit message we retrieved so that it could |
- // propagate to the outer layer. Otherwise, the program will seem to |
- // "get stuck" in its shutdown code. |
- ::PostQuitMessage(msg.wParam); |
- return S_FALSE; |
- } |
- |
- // WaitForMultipleObjects fails if cnt == 0. |
- if (cnt) { |
- // Briefly check the state of the handle array to see if something |
- // has signaled as we processed a message. |
- ASSERT1(phandles); |
- DWORD res = ::WaitForMultipleObjects(cnt, phandles, false, 0); |
- ASSERT1(res != WAIT_FAILED); |
- HRESULT hr = GetSignaledObjectPosition(cnt, res, pos); |
- if (SUCCEEDED(hr)) { |
- return hr; |
- } |
- } |
- } |
- |
- // The wait with message. It is satisfied by either the objects getting |
- // signaled or when messages enter the message queue. |
- // TODO(omaha): implementing timeout is a little bit tricky since we |
- // want the timeout on the handles only and the native API does not |
- // have this semantic. |
- // |
- // TODO(omaha): use a waitable timer to implement the timeout. |
- // |
- // When cnt is zero then the execution flow waits here until messages |
- // arrive in the input queue. Unlike WaitForMultipleObjects, |
- // MsgWaitForMultipleObjects does not error out when cnt == 0. |
- const DWORD timeout = INFINITE; |
- DWORD res(::MsgWaitForMultipleObjects(cnt, phandles, false, timeout, |
- QS_ALLINPUT)); |
- ASSERT((res != WAIT_FAILED), |
- (_T("[MsgWaitForMultipleObjects returned WAIT_FAILED][%u]"), |
- ::GetLastError())); |
- |
- ASSERT1(res != WAIT_TIMEOUT); |
- |
- HRESULT hr = GetSignaledObjectPosition(cnt, res, pos); |
- if (SUCCEEDED(hr)) { |
- return hr; |
- } |
- } |
-} |
- |
-// The simplest implementation of a message processor |
-void BasicMessageHandler::Process(MSG* msg) { |
- ASSERT1(msg); |
- ::TranslateMessage(msg); |
- ::DispatchMessage(msg); |
-} |
- |
-class BasicMessageHandlerInternal : public BasicMessageHandler, |
- public MessageHandlerInternalInterface { |
- public: |
- BasicMessageHandlerInternal() {} |
- virtual void Process(MSG* msg, const HANDLE**, uint32*) { |
- BasicMessageHandler::Process(msg); |
- } |
- private: |
- DISALLOW_EVIL_CONSTRUCTORS(BasicMessageHandlerInternal); |
-}; |
- |
- |
-bool WaitWithMessageLoopAny(const std::vector<HANDLE>& handles, uint32* pos) { |
- BasicMessageHandlerInternal msg_handler; |
- return WaitWithMessageLoopAnyInternal(&handles.front(), handles.size(), pos, |
- &msg_handler) != S_FALSE; |
-} |
- |
-bool WaitWithMessageLoopAll(const std::vector<HANDLE>& handles) { |
- // make a copy of the vector, as objects must be removed from the |
- // wait array as they get signaled. |
- std::vector<HANDLE> h(handles); |
- |
- // The function is mainly implemented in terms of WaitWithMessageLoopAny |
- |
- // loop until all objects are signaled. |
- while (!h.empty()) { |
- uint32 pos(static_cast<uint32>(-1)); |
- if (!WaitWithMessageLoopAny(h, &pos)) return false; |
- ASSERT1(pos < h.size()); |
- h.erase(h.begin() + pos); // remove the signaled object and loop |
- } |
- |
- return true; |
-} |
- |
-bool WaitWithMessageLoop(HANDLE h) { |
- BasicMessageHandlerInternal msg_handler; |
- uint32 pos(static_cast<uint32>(-1)); |
- bool res = |
- WaitWithMessageLoopAnyInternal(&h, 1, &pos, &msg_handler) != S_FALSE; |
- if (res) { |
- // It's the first and the only handle that it is signaled. |
- ASSERT1(pos == 0); |
- } |
- return res; |
-} |
- |
-// Wait with message loop for a certain period of time |
-bool WaitWithMessageLoopTimed(DWORD ms) { |
- scoped_timer timer(::CreateWaitableTimer(NULL, |
- true, // manual reset |
- NULL)); |
- ASSERT1(get(timer)); |
- LARGE_INTEGER timeout = MSto100NSRelative(ms); |
- BOOL timer_ok = ::SetWaitableTimer(get(timer), |
- &timeout, |
- 0, |
- NULL, |
- NULL, |
- false); |
- ASSERT1(timer_ok); |
- return WaitWithMessageLoop(get(timer)); |
-} |
- |
-MessageLoopWithWait::MessageLoopWithWait() : message_handler_(NULL) { |
-} |
- |
-void MessageLoopWithWait::set_message_handler( |
- MessageHandlerInterface* message_handler) { |
- message_handler_ = message_handler; |
-} |
- |
-// The message loop and handle callback routine. |
-HRESULT MessageLoopWithWait::Process() { |
- while (true) { |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
- |
- // The implementation allows for an empty array of handles. Taking the |
- // address of elements in an empty container is not allowed so we must |
- // deal with this case here. |
- size_t pos(0); |
- HRESULT hr = WaitWithMessageLoopAnyInternal( |
- callback_handles_.empty() ? NULL : &callback_handles_.front(), |
- callback_handles_.size(), |
- &pos, |
- this); |
- |
- // In addition to E_*, S_FALSE should cause a return to happen here. |
- if (hr != S_OK) { |
- return hr; |
- } |
- |
- ASSERT1(pos < callback_handles_.size()); |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
- |
- HANDLE signaled_handle = callback_handles_[pos]; |
- WaitCallbackInterface* callback_interface = callbacks_[pos]; |
- RemoveHandleAt(pos); |
- |
- if (!callback_interface->HandleSignaled(signaled_handle)) { |
- return S_OK; |
- } |
- } |
-} |
- |
-// Handles one messgae and adjust the handles and cnt as appropriate after |
-// handling the message. |
-void MessageLoopWithWait::Process(MSG* msg, const HANDLE** handles, |
- uint32* cnt) { |
- ASSERT1(msg && handles && cnt); |
- |
- if (message_handler_) { |
- message_handler_->Process(msg); |
- } |
- |
- // Set the handles and count again because they may have changed |
- // while processing the message. |
- *handles = callback_handles_.empty() ? NULL : &callback_handles_.front(); |
- *cnt = callback_handles_.size(); |
-} |
-// Starts waiting on the given handle |
-bool MessageLoopWithWait::RegisterWaitForSingleObject( |
- HANDLE handle, WaitCallbackInterface* callback) { |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
- ASSERT1(callback != NULL); |
- |
- if (callback_handles_.size() >= MAXIMUM_WAIT_OBJECTS - 1) { |
- return false; |
- } |
- |
- // In case the user is registering a handle, that they previous added |
- // remove the previous one before adding it back into the array. |
- UnregisterWait(handle); |
- callback_handles_.push_back(handle); |
- callbacks_.push_back(callback); |
- |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
- return true; |
-} |
- |
-// Finds the given handle and stops waiting on it |
-bool MessageLoopWithWait::UnregisterWait(HANDLE handle) { |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
- |
- for (uint32 index = 0; index < callback_handles_.size() ; index++) { |
- if (callback_handles_[index] == handle) { |
- RemoveHandleAt(index); |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-// Removes the wait handle at the given position |
-void MessageLoopWithWait::RemoveHandleAt(uint32 pos) { |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
- ASSERT1(pos < callback_handles_.size()); |
- |
- callback_handles_.erase(callback_handles_.begin() + pos); |
- callbacks_.erase(callbacks_.begin() + pos); |
- |
- ASSERT1(callback_handles_.size() == callbacks_.size()); |
-} |
- |
-HRESULT CallEntryPoint0(const TCHAR* dll_path, |
- const char* function_name, |
- HRESULT* result) { |
- ASSERT1(dll_path); |
- ASSERT1(::lstrlen(dll_path) > 0); |
- ASSERT1(function_name); |
- ASSERT1(::strlen(function_name) > 0); |
- ASSERT1(result); |
- |
- scoped_library dll(::LoadLibrary(dll_path)); |
- if (!dll) { |
- return HRESULTFromLastError(); |
- } |
- |
- HRESULT (*proc)() = reinterpret_cast<HRESULT (*)()>( |
- ::GetProcAddress(get(dll), function_name)); |
- if (!proc) { |
- return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); |
- } |
- |
- *result = (proc)(); |
- return S_OK; |
-} |
- |
-// Register a DLL |
-HRESULT RegisterDll(const TCHAR* dll_path) { |
- HRESULT hr = S_OK; |
- HRESULT hr_call = CallEntryPoint0(dll_path, "DllRegisterServer", &hr); |
- if (SUCCEEDED(hr_call)) { |
- return hr; |
- } |
- return hr_call; |
-} |
- |
-// Unregister a DLL |
-HRESULT UnregisterDll(const TCHAR* dll_path) { |
- HRESULT hr = S_OK; |
- HRESULT hr_call = CallEntryPoint0(dll_path, "DllUnregisterServer", &hr); |
- if (SUCCEEDED(hr_call)) { |
- return hr; |
- } |
- return hr_call; |
-} |
- |
-// Register/unregister an EXE |
-HRESULT RegisterOrUnregisterExe(const TCHAR* exe_path, const TCHAR* cmd_line) { |
- ASSERT1(exe_path); |
- ASSERT1(cmd_line); |
- |
- // cmd_line parameter really contains the arguments to be passed |
- // on the process creation command line. |
- PROCESS_INFORMATION pi = {0}; |
- HRESULT hr = System::StartProcessWithArgsAndInfo(exe_path, cmd_line, &pi); |
- if (FAILED(hr)) { |
- UTIL_LOG(LEVEL_WARNING, (_T("[RegisterOrUnregisterExe]") |
- _T("[failed to start process]") |
- _T("[%s][%s][0x%08x]"), exe_path, cmd_line, hr)); |
- return hr; |
- } |
- // Take ownership of the handles for clean up. |
- scoped_thread thread(pi.hThread); |
- scoped_process process(pi.hProcess); |
- |
- // ATL COM servers return an HRESULT on exit. There is a case in which they |
- // return -1 which seems like a bug in ATL. It appears there is no |
- // documented convention on what a local server would return for errors. |
- // There is a possibility that a server would return Windows errors. |
- |
- // Wait on the process to exit and return the exit code of the process. |
- DWORD result(::WaitForSingleObject(get(process), INFINITE)); |
- DWORD exit_code(0); |
- if (result == WAIT_OBJECT_0 && |
- ::GetExitCodeProcess(get(process), &exit_code)) { |
- return static_cast<HRESULT>(exit_code); |
- } else { |
- return HRESULT_FROM_WIN32(ERROR_TIMEOUT); |
- } |
-} |
- |
-// Register a COM Local Server |
-HRESULT RegisterServer(const TCHAR* exe_path) { |
- return RegisterOrUnregisterExe(exe_path, _T("/RegServer")); |
-} |
- |
-// Unregister a COM Local Server |
-HRESULT UnregisterServer(const TCHAR* exe_path) { |
- return RegisterOrUnregisterExe(exe_path, _T("/UnregServer")); |
-} |
- |
-// Register a Service |
-HRESULT RegisterService(const TCHAR* exe_path) { |
- return RegisterOrUnregisterExe(exe_path, _T("/Service")); |
-} |
- |
-// Unregister a Service |
-HRESULT UnregisterService(const TCHAR* exe_path) { |
- // Unregistering a service is via UnregServer |
- return RegisterOrUnregisterExe(exe_path, _T("/UnregServer")); |
-} |
- |
-// Adapted from gds installer/install/work_list.cpp: InstallServiceExecutable |
-HRESULT RunService(const TCHAR* service_name) { |
- scoped_service manager(::OpenSCManager(NULL, // local machine |
- NULL, // ServicesActive database |
- STANDARD_RIGHTS_READ)); |
- ASSERT1(get(manager)); |
- if (!get(manager)) { |
- return HRESULTFromLastError(); |
- } |
- |
- scoped_service service(::OpenService(get(manager), service_name, |
- SERVICE_START)); |
- ASSERT1(get(service)); |
- if (!get(service)) { |
- return HRESULTFromLastError(); |
- } |
- |
- UTIL_LOG(L2, (_T("start service"))); |
- if (!::StartService(get(service), 0, NULL)) { |
- return HRESULTFromLastError(); |
- } |
- return S_OK; |
-} |
- |
- |
-HRESULT ReadEntireFile(const TCHAR* filepath, |
- uint32 max_len, |
- std::vector<byte>* buffer_out) { |
- return ReadEntireFileShareMode(filepath, max_len, 0, buffer_out); |
-} |
- |
-HRESULT ReadEntireFileShareMode(const TCHAR* filepath, |
- uint32 max_len, |
- DWORD share_mode, |
- std::vector<byte>* buffer_out) { |
- ASSERT1(filepath); |
- ASSERT1(buffer_out); |
- |
- File file; |
- HRESULT hr = file.OpenShareMode(filepath, false, false, share_mode); |
- if (FAILED(hr)) { |
- // File missing. |
- return hr; |
- } |
- |
- ON_SCOPE_EXIT_OBJ(file, &File::Close); |
- |
- uint32 file_len = 0; |
- hr = file.GetLength(&file_len); |
- if (FAILED(hr)) { |
- // Should never happen |
- return hr; |
- } |
- |
- if (max_len != 0 && file_len > max_len) { |
- // Too large to consider |
- return MEM_E_INVALID_SIZE; |
- } |
- |
- if (file_len == 0) { |
- buffer_out->clear(); |
- return S_OK; |
- } |
- |
- int old_size = buffer_out->size(); |
- buffer_out->resize(old_size + file_len); |
- |
- uint32 bytes_read = 0; |
- hr = file.ReadFromStartOfFile(file_len, |
- &(*buffer_out)[old_size], |
- &bytes_read); |
- if (FAILED(hr)) { |
- // I/O error of some kind |
- return hr; |
- } |
- |
- if (bytes_read != file_len) { |
- // Unexpected length. This could happen when reading a file someone else |
- // is writing to such as log files. |
- ASSERT1(false); |
- return E_UNEXPECTED; |
- } |
- |
- // All's well that ends well |
- return S_OK; |
-} |
- |
-HRESULT WriteEntireFile(const TCHAR * filepath, |
- const std::vector<byte>& buffer_in) { |
- ASSERT1(filepath); |
- |
- // File::WriteAt doesn't implement clear-on-open-for-write semantics, |
- // so just delete the file if it exists instead of writing into it. |
- |
- if (File::Exists(filepath)) { |
- HRESULT hr = File::Remove(filepath); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- } |
- |
- File file; |
- HRESULT hr = file.Open(filepath, true, false); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- ON_SCOPE_EXIT_OBJ(file, &File::Close); |
- |
- uint32 bytes_written = 0; |
- hr = file.WriteAt(0, &buffer_in.front(), buffer_in.size(), 0, &bytes_written); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- if (bytes_written != buffer_in.size()) { |
- // This shouldn't happen, caller needs to investigate what's up. |
- ASSERT1(false); |
- return E_UNEXPECTED; |
- } |
- |
- return S_OK; |
-} |
- |
-// Conversions between a byte stream and a std::string |
-HRESULT BufferToString(const std::vector<byte>& buffer_in, CStringA* str_out) { |
- ASSERT1(str_out); |
- str_out->Append(reinterpret_cast<const char*>(&buffer_in.front()), |
- buffer_in.size()); |
- return S_OK; |
-} |
- |
-HRESULT StringToBuffer(const CStringA& str_in, std::vector<byte>* buffer_out) { |
- ASSERT1(buffer_out); |
- buffer_out->assign(str_in.GetString(), |
- str_in.GetString() + str_in.GetLength()); |
- return S_OK; |
-} |
- |
-HRESULT BufferToString(const std::vector<byte>& buffer_in, CString* str_out) { |
- ASSERT1(str_out); |
- |
- size_t len2 = buffer_in.size(); |
- ASSERT1(len2 % 2 == 0); |
- size_t len = len2 / 2; |
- |
- str_out->Append(reinterpret_cast<const TCHAR*>(&buffer_in.front()), len); |
- |
- return S_OK; |
-} |
- |
-HRESULT StringToBuffer(const CString& str_in, std::vector<byte>* buffer_out) { |
- ASSERT1(buffer_out); |
- |
- size_t len = str_in.GetLength(); |
- size_t len2 = len * 2; |
- |
- buffer_out->resize(len2); |
- ::memcpy(&buffer_out->front(), str_in.GetString(), len2); |
- |
- return S_OK; |
-} |
- |
-HRESULT RegSplitKeyvalueName(const CString& keyvalue_name, |
- CString* key_name, |
- CString* value_name) { |
- ASSERT1(key_name); |
- ASSERT1(value_name); |
- |
- const TCHAR kDefault[] = _T("\\(default)"); |
- |
- if (String_EndsWith(keyvalue_name, _T("\\"), false)) { |
- key_name->SetString(keyvalue_name, keyvalue_name.GetLength() - 1); |
- value_name->Empty(); |
- } else if (String_EndsWith(keyvalue_name, kDefault, true)) { |
- key_name->SetString(keyvalue_name, |
- keyvalue_name.GetLength() - TSTR_SIZE(kDefault)); |
- value_name->Empty(); |
- } else { |
- int last_slash = String_ReverseFindChar(keyvalue_name, _T('\\')); |
- if (last_slash == -1) { |
- // No slash found - bizzare and wrong |
- return E_FAIL; |
- } |
- key_name->SetString(keyvalue_name, last_slash); |
- value_name->SetString(keyvalue_name.GetString() + last_slash + 1, |
- keyvalue_name.GetLength() - last_slash - 1); |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT ExpandEnvLikeStrings(const TCHAR* src, |
- const std::map<CString, CString>& keywords, |
- CString* dest) { |
- ASSERT1(src); |
- ASSERT1(dest); |
- |
- const TCHAR kMarker = _T('%'); |
- |
- dest->Empty(); |
- |
- // Loop while finding the marker in the string |
- HRESULT hr = S_OK; |
- int pos = 0; |
- int marker_pos1 = -1; |
- while ((marker_pos1 = String_FindChar(src, kMarker, pos)) != -1) { |
- // Try to find the right marker |
- int marker_pos2 = -1; |
- const TCHAR* s = src + marker_pos1 + 1; |
- for (; *s; ++s) { |
- if (*s == kMarker) { |
- marker_pos2 = s - src; |
- break; |
- } |
- if (!String_IsIdentifierChar(*s)) { |
- break; |
- } |
- } |
- if (marker_pos2 == -1) { |
- // Unmatched marker found, skip |
- dest->Append(src + pos, marker_pos1 - pos + 1); |
- pos = marker_pos1 + 1; |
- continue; |
- } |
- |
- // Get the name - without the % markers on each end |
- CString name(src + marker_pos1 + 1, marker_pos2 - marker_pos1 - 1); |
- |
- bool found = false; |
- for (std::map<CString, CString>::const_iterator it(keywords.begin()); |
- it != keywords.end(); |
- ++it) { |
- if (_tcsicmp(it->first, name) == 0) { |
- dest->Append(src + pos, marker_pos1 - pos); |
- dest->Append(it->second); |
- found = true; |
- break; |
- } |
- } |
- if (!found) { |
- // No mapping found |
- UTIL_LOG(LE, (_T("[ExpandEnvLikeStrings]") |
- _T("[no mapping found for '%s' in '%s']"), name, src)); |
- dest->Append(src + pos, marker_pos2 - pos + 1); |
- hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
- } |
- |
- pos = marker_pos2 + 1; |
- } |
- |
- int len = _tcslen(src); |
- if (pos < len) { |
- dest->Append(src + pos, len - pos); |
- } |
- |
- return hr; |
-} |
- |
-bool IsRegistryPath(const TCHAR* path) { |
- return String_StartsWith(path, _T("HKLM\\"), false) || |
- String_StartsWith(path, _T("HKCU\\"), false) || |
- String_StartsWith(path, _T("HKCR\\"), false) || |
- String_StartsWith(path, _T("HKEY_LOCAL_MACHINE\\"), false) || |
- String_StartsWith(path, _T("HKEY_CURRENT_USER\\"), false) || |
- String_StartsWith(path, _T("HKEY_CLASSES_ROOT\\"), false); |
-} |
- |
-bool IsUrl(const TCHAR* path) { |
- // Currently we only check for "http://" and "https://" |
- return String_StartsWith(path, kHttpProto, true) || |
- String_StartsWith(path, kHttpsProto, true); |
-} |
- |
- |
-CString GuidToString(const GUID& guid) { |
- TCHAR guid_str[40] = {0}; |
- VERIFY1(::StringFromGUID2(guid, guid_str, arraysize(guid_str))); |
- String_ToUpper(guid_str); |
- return guid_str; |
-} |
- |
-HRESULT StringToGuidSafe(const CString& str, GUID* guid) { |
- ASSERT1(guid); |
- TCHAR* s = const_cast<TCHAR*>(str.GetString()); |
- return ::IIDFromString(s, guid); |
-} |
- |
-// Helper function to convert a variant containing a list of strings |
-void VariantToStringList(VARIANT var, std::vector<CString>* list) { |
- ASSERT1(list); |
- |
- list->clear(); |
- |
- ASSERT1(V_VT(&var) == VT_DISPATCH); |
- CComPtr<IDispatch> obj = V_DISPATCH(&var); |
- ASSERT1(obj); |
- |
- CComVariant var_length; |
- VERIFY1(SUCCEEDED(obj.GetPropertyByName(_T("length"), &var_length))); |
- ASSERT1(V_VT(&var_length) == VT_I4); |
- int length = V_I4(&var_length); |
- |
- for (int i = 0; i < length; ++i) { |
- CComVariant value; |
- VERIFY1(SUCCEEDED(obj.GetPropertyByName(itostr(i), &value))); |
- if (V_VT(&value) == VT_BSTR) { |
- list->push_back(V_BSTR(&value)); |
- } else { |
- ASSERT1(false); |
- } |
- } |
-} |
- |
- |
-HRESULT GetCurrentProcessHandle(HANDLE* handle) { |
- ASSERT1(handle); |
- scoped_process real_handle; |
- HANDLE pseudo_handle = ::GetCurrentProcess(); |
- bool res = ::DuplicateHandle( |
- pseudo_handle, // this process pseudo-handle |
- pseudo_handle, // handle to duplicate |
- pseudo_handle, // the process receiving the handle |
- address(real_handle), // this process real handle |
- 0, // ignored |
- false, // don't inherit this handle |
- DUPLICATE_SAME_ACCESS) != 0; |
- |
- *handle = NULL; |
- if (!res) { |
- return HRESULTFromLastError(); |
- } |
- *handle = release(real_handle); |
- return S_OK; |
-} |
- |
-HRESULT DuplicateTokenIntoCurrentProcess(HANDLE source_process, |
- HANDLE token_to_duplicate, |
- CAccessToken* duplicated_token) { |
- ASSERT1(source_process); |
- ASSERT1(token_to_duplicate); |
- ASSERT1(duplicated_token); |
- |
- scoped_handle alt_token; |
- bool res = ::DuplicateHandle( |
- source_process, // Process whose handle needs duplicating. |
- token_to_duplicate, // Handle to duplicate. |
- ::GetCurrentProcess(), // Current process receives the handle. |
- address(alt_token), // Duplicated handle. |
- TOKEN_ALL_ACCESS, // Access requested for the new handle. |
- false, // Do not inherit the new handle. |
- DUPLICATE_SAME_ACCESS) != 0; // Same access as token_to_duplicate. |
- |
- if (!res) { |
- HRESULT hr = HRESULTFromLastError(); |
- CORE_LOG(LE, (_T("[DuplicateTokenIntoCurrentProcess failed][0x%x]"), hr)); |
- return hr; |
- } |
- |
- duplicated_token->Attach(release(alt_token)); |
- return S_OK; |
-} |
- |
- |
-// get a time64 value |
-// NOTE: If the value is greater than the |
-// max value, then SetValue will be called using the max_value. |
-HRESULT GetLimitedTimeValue(const TCHAR* full_key_name, const TCHAR* value_name, |
- time64 max_time, time64* value, |
- bool* limited_value) { |
- ASSERT1(full_key_name); |
- ASSERT1(value); |
- STATIC_ASSERT(sizeof(time64) == sizeof(DWORD64)); |
- |
- if (limited_value) { |
- *limited_value = false; |
- } |
- HRESULT hr = RegKey::GetValue(full_key_name, value_name, value); |
- if (SUCCEEDED(hr) && *value > max_time) { |
- *value = max_time; |
- |
- // Use a different hr for the setting of the value b/c |
- // the returned hr should reflect the success/failure of reading the key |
- HRESULT set_value_hr = RegKey::SetValue(full_key_name, value_name, *value); |
- ASSERT(SUCCEEDED(set_value_hr), |
- (_T("GetLimitedTimeValue - failed when setting a value: 0x%08x]"), |
- set_value_hr)); |
- if (SUCCEEDED(set_value_hr) && limited_value) { |
- *limited_value = true; |
- } |
- } |
- return hr; |
-} |
- |
-// get a time64 value trying reg keys successively if there is a |
-// failure in getting a value. |
-HRESULT GetLimitedTimeValues(const TCHAR* full_key_names[], |
- int key_names_length, |
- const TCHAR* value_name, |
- time64 max_time, |
- time64* value, |
- bool* limited_value) { |
- ASSERT1(full_key_names); |
- ASSERT1(value); |
- ASSERT1(key_names_length > 0); |
- |
- HRESULT hr = E_FAIL; |
- for (int i = 0; i < key_names_length; ++i) { |
- hr = GetLimitedTimeValue(full_key_names[i], value_name, max_time, value, |
- limited_value); |
- if (SUCCEEDED(hr)) { |
- return hr; |
- } |
- } |
- return hr; |
-} |
- |
-// Wininet.dll (and especially the version that comes with IE7, with 01/12/07 |
-// timestamp) incorrectly initializes Rasman.dll. As a result, there is a race |
-// condition that causes double-free on a memory from process heap. |
-// This causes memory corruption in the heap that may later produce a variety |
-// of ill effects, most frequently a crash with a callstack that contains |
-// wininet and rasman, or ntdll!RtlAllocHeap. The root cause is that |
-// Rasapi32!LoadRasmanDllAndInit is not thread safe and can start very involved |
-// process of initialization on 2 threads at the same time. It's a bug. |
-// Solution: in the begining of the program, trigger synchronous load of |
-// rasman dll. The easy way is to call a public ras api that does synchronous |
-// initialization, which is what we do here. |
-void EnsureRasmanLoaded() { |
- RASENTRYNAME ras_entry_name = {0}; |
- DWORD size_bytes = sizeof(ras_entry_name); |
- DWORD number_of_entries = 0; |
- ras_entry_name.dwSize = size_bytes; |
- // we don't really need results of this method, |
- // it simply triggers RASAPI32!LoadRasmanDllAndInit() internally. |
- ::RasEnumEntries(NULL, |
- NULL, |
- &ras_entry_name, |
- &size_bytes, |
- &number_of_entries); |
-} |
- |
-// Appends two reg keys. Handles the situation where there are traling |
-// back slashes in one and leading back slashes in two. |
-CString AppendRegKeyPath(const CString& one, const CString& two) { |
- CString leftpart(one); |
- int length = leftpart.GetLength(); |
- int i = 0; |
- for (i = length - 1; i >= 0; --i) { |
- if (leftpart[i] != _T('\\')) { |
- break; |
- } |
- } |
- leftpart = leftpart.Left(i+1); |
- |
- CString rightpart(two); |
- int lengthr = rightpart.GetLength(); |
- for (i = 0; i < lengthr; ++i) { |
- if (rightpart[i] != _T('\\')) { |
- break; |
- } |
- } |
- rightpart = rightpart.Right(lengthr - i); |
- |
- CString result; |
- SafeCStringFormat(&result, _T("%s\\%s"), leftpart, rightpart); |
- return result; |
-} |
- |
-CString AppendRegKeyPath(const CString& one, const CString& two, |
- const CString& three) { |
- CString result = AppendRegKeyPath(one, two); |
- result = AppendRegKeyPath(result, three); |
- return result; |
-} |
- |
- |
-HRESULT GetUserKeysFromHkeyUsers(std::vector<CString>* key_names) { |
- ASSERT1(key_names); |
- CORE_LOG(L3, (_T("[GetUserKeysFromHkeyUsers]"))); |
- |
- TCHAR user_key_name[MAX_PATH] = {0}; |
- int i = 0; |
- while (::RegEnumKey(HKEY_USERS, i++, user_key_name, MAX_PATH) != |
- ERROR_NO_MORE_ITEMS) { |
- byte sid_buffer[SECURITY_MAX_SID_SIZE] = {0}; |
- PSID sid = reinterpret_cast<PSID>(sid_buffer); |
- if (::ConvertStringSidToSid(user_key_name, &sid) != 0) { |
- // We could convert the string SID into a real SID. If not |
- // we just ignore. |
- DWORD size = MAX_PATH; |
- DWORD size_domain = MAX_PATH; |
- SID_NAME_USE sid_type = SidTypeComputer; |
- TCHAR user_name[MAX_PATH] = {0}; |
- TCHAR domain_name[MAX_PATH] = {0}; |
- |
- if (::LookupAccountSid(NULL, sid, user_name, &size, |
- domain_name, &size_domain, &sid_type) == 0) { |
- HRESULT hr = HRESULTFromLastError(); |
- CORE_LOG(LW, (_T("[GetUserKeysFromHkeyUsers LookupAccountSid failed]") |
- _T("[0x%08x]"), hr)); |
- continue; |
- } |
- |
- if (sid_type == SidTypeUser) { |
- // Change the RunAs keys for the user goopdates to point to the |
- // machine install. |
- CString user_reg_key_name = AppendRegKeyPath(USERS_KEY, user_key_name); |
- key_names->push_back(user_reg_key_name); |
- } |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT IsSystemProcess(bool* is_system_process) { |
- CAccessToken current_process_token; |
- if (!current_process_token.GetProcessToken(TOKEN_QUERY, |
- ::GetCurrentProcess())) { |
- HRESULT hr = HRESULTFromLastError(); |
- ASSERT(false, (_T("CAccessToken::GetProcessToken failed: 0x%08x"), hr)); |
- return hr; |
- } |
- CSid logon_sid; |
- if (!current_process_token.GetUser(&logon_sid)) { |
- HRESULT hr = HRESULTFromLastError(); |
- ASSERT(false, (_T("CAccessToken::GetUser failed: 0x%08x"), hr)); |
- return hr; |
- } |
- *is_system_process = logon_sid == Sids::System(); |
- return S_OK; |
-} |
- |
-HRESULT IsUserLoggedOn(bool* is_logged_on) { |
- ASSERT1(is_logged_on); |
- bool is_local_system(false); |
- HRESULT hr = IsSystemProcess(&is_local_system); |
- if (SUCCEEDED(hr) && is_local_system) { |
- *is_logged_on = true; |
- return S_OK; |
- } |
- return UserRights::UserIsLoggedOnInteractively(is_logged_on); |
-} |
- |
-bool IsClickOnceDisabled() { |
- CComPtr<IInternetZoneManager> zone_mgr; |
- HRESULT hr = zone_mgr.CoCreateInstance(CLSID_InternetZoneManager); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[InternetZoneManager CreateInstance fail][0x%08x]"), hr)); |
- return true; |
- } |
- |
- DWORD policy = URLPOLICY_DISALLOW; |
- size_t policy_size = sizeof(policy); |
- hr = zone_mgr->GetZoneActionPolicy(URLZONE_INTERNET, |
- URLACTION_MANAGED_UNSIGNED, |
- reinterpret_cast<BYTE*>(&policy), |
- policy_size, |
- URLZONEREG_DEFAULT); |
- if (FAILED(hr)) { |
- UTIL_LOG(LE, (_T("[GetZoneActionPolicy failed][0x%08x]"), hr)); |
- return true; |
- } |
- |
- return policy == URLPOLICY_DISALLOW; |
-} |
- |
-// This function only uses kernel32, and it is safe to call from DllMain. |
-HRESULT PinModuleIntoProcess(const CString& module_name) { |
- ASSERT1(!module_name.IsEmpty()); |
- static HMODULE module_handle = NULL; |
- typedef BOOL (WINAPI *Fun)(DWORD flags, |
- LPCWSTR module_name, |
- HMODULE* module_handle); |
- |
- HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); |
- ASSERT1(kernel_instance); |
- Fun pfn = NULL; |
- if (GPA(kernel_instance, "GetModuleHandleExW", &pfn)) { |
- if ((*pfn)(GET_MODULE_HANDLE_EX_FLAG_PIN, module_name, &module_handle)) { |
- return S_OK; |
- } |
- ASSERT(false, (_T("GetModuleHandleExW() failed: %d"), ::GetLastError())); |
- } |
- |
- module_handle = ::LoadLibrary(module_name); |
- ASSERT(NULL != module_handle, (_T("LoadLibrary fail: %d"), ::GetLastError())); |
- if (NULL == module_handle) { |
- return HRESULTFromLastError(); |
- } |
- |
- return S_OK; |
-} |
- |
-bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info) { |
- UTIL_LOG(L3, (_T("[ShellExecuteExEnsureParent]"))); |
- |
- ASSERT1(shell_exec_info); |
- bool shell_exec_succeeded(false); |
- DWORD last_error(ERROR_SUCCESS); |
- |
- { |
- // hwnd_parent window is destroyed at the end of the scope when the |
- // destructor of scoped_window calls ::DestroyWindow. |
- scoped_window hwnd_parent; |
- |
- if (!shell_exec_info->hwnd && vista_util::IsVistaOrLater()) { |
- reset(hwnd_parent, CreateForegroundParentWindowForUAC()); |
- |
- if (!hwnd_parent) { |
- last_error = ::GetLastError(); |
- UTIL_LOG(LE, (_T("[CreateDummyOverlappedWindow failed]"))); |
- // Restore last error in case the logging reset it. |
- ::SetLastError(last_error); |
- return false; |
- } |
- |
- shell_exec_info->hwnd = get(hwnd_parent); |
- |
- // If elevation is required on Vista, call ::SetForegroundWindow(). This |
- // will make sure that the elevation prompt, as well as the elevated |
- // process window comes up in the foreground. It will also ensure that in |
- // the case where the elevation prompt is cancelled, the error dialog |
- // shown from this process comes up in the foreground. |
- if (shell_exec_info->lpVerb && |
- _tcsicmp(shell_exec_info->lpVerb, _T("runas")) == 0) { |
- if (!::SetForegroundWindow(get(hwnd_parent))) { |
- UTIL_LOG(LW, (_T("[SetForegroundWindow failed][%d]"), |
- ::GetLastError())); |
- } |
- } |
- } |
- |
- shell_exec_succeeded = !!::ShellExecuteEx(shell_exec_info); |
- |
- if (shell_exec_succeeded) { |
- if (shell_exec_info->hProcess) { |
- DWORD pid = Process::GetProcessIdFromHandle(shell_exec_info->hProcess); |
- OPT_LOG(L1, (_T("[Started process][%u]"), pid)); |
- if (!::AllowSetForegroundWindow(pid)) { |
- UTIL_LOG(LW, (_T("[AllowSetForegroundWindow failed][%d]"), |
- ::GetLastError())); |
- } |
- } else { |
- OPT_LOG(L1, (_T("[Started process][PID unknown]"))); |
- } |
- } else { |
- last_error = ::GetLastError(); |
- UTIL_LOG(LE, (_T("[ShellExecuteEx failed][%s][%s][0x%08x]"), |
- shell_exec_info->lpFile, shell_exec_info->lpParameters, |
- last_error)); |
- } |
- } |
- |
- // The implicit ::DestroyWindow call from the scoped_window could have reset |
- // the last error, so restore it. |
- ::SetLastError(last_error); |
- |
- return shell_exec_succeeded; |
-} |
- |
-// Loads and unloads advapi32.dll for every call. If performance is an issue |
-// consider keeping the dll always loaded and holding the pointer to the |
-// RtlGenRandom in a static variable. |
-// Use the function with care. While the function is documented, it may be |
-// altered or made unavailable in future versions of the operating system. |
-bool GenRandom(void* buffer, size_t buffer_length) { |
- ASSERT1(buffer); |
- scoped_library lib(::LoadLibrary(_T("ADVAPI32.DLL"))); |
- if (lib) { |
- typedef BOOLEAN (APIENTRY *RtlGenRandomType)(void*, ULONG); |
- RtlGenRandomType rtl_gen_random = reinterpret_cast<RtlGenRandomType>( |
- ::GetProcAddress(get(lib), "SystemFunction036")); |
- return rtl_gen_random && rtl_gen_random(buffer, buffer_length); |
- } |
- |
- // Use CAPI to generate randomness for systems which do not support |
- // RtlGenRandomType, for instance Windows 2000. |
- const uint32 kCspFlags = CRYPT_VERIFYCONTEXT | CRYPT_SILENT; |
- HCRYPTPROV csp = NULL; |
- if (::CryptAcquireContext(&csp, NULL, NULL, PROV_RSA_FULL, kCspFlags)) { |
- if (::CryptGenRandom(csp, buffer_length, static_cast<BYTE*>(buffer))) { |
- return true; |
- } |
- } |
- VERIFY1(::CryptReleaseContext(csp, 0)); |
- return false; |
-} |
- |
-// Assumes the path in command is properly enclosed if necessary. |
-HRESULT ConfigureRunAtStartup(const CString& root_key_name, |
- const CString& run_value_name, |
- const CString& command, |
- bool install) { |
- UTIL_LOG(L3, (_T("[ConfigureRunAtStartup]"))); |
- |
- const CString key_path = AppendRegKeyPath(root_key_name, REGSTR_PATH_RUN); |
- HRESULT hr(S_OK); |
- |
- if (install) { |
- hr = RegKey::SetValue(key_path, run_value_name, command); |
- } else { |
- hr = RegKey::DeleteValue(key_path, run_value_name); |
- } |
- |
- return hr; |
-} |
- |
-HRESULT GetExePathFromCommandLine(const TCHAR* command_line, |
- CString* exe_path) { |
- ASSERT1(exe_path); |
- CString command_line_str(command_line); |
- command_line_str.Trim(_T(' ')); |
- if (command_line_str.IsEmpty()) { |
- // ::CommandLineToArgvW parses the current process command line for blank |
- // strings. We do not want this behavior. |
- return E_INVALIDARG; |
- } |
- |
- int argc = 0; |
- wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc); |
- if (argc == 0 || !argv) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[::CommandLineToArgvW failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- *exe_path = argv[0]; |
- ::LocalFree(argv); |
- exe_path->Trim(_T(' ')); |
- ASSERT1(!exe_path->IsEmpty()); |
- return S_OK; |
-} |
- |
-// Tries to open the _MSIExecute mutex and tests its state. MSI sets the |
-// mutex when processing sequence tables. This indicates MSI is busy. |
-// The function returns S_OK if the mutex is not owned by MSI or the mutex has |
-// not been created. |
-HRESULT WaitForMSIExecute(int timeout_ms) { |
- const TCHAR* mutex_name = _T("Global\\_MSIExecute"); |
- scoped_mutex mutex(::OpenMutex(SYNCHRONIZE, false, mutex_name)); |
- if (!mutex) { |
- DWORD error = ::GetLastError(); |
- return (error == ERROR_FILE_NOT_FOUND) ? S_OK : HRESULT_FROM_WIN32(error); |
- } |
- UTIL_LOG(L3, (_T("[Wait for _MSIExecute]"))); |
- switch (::WaitForSingleObject(get(mutex), timeout_ms)) { |
- case WAIT_OBJECT_0: |
- case WAIT_ABANDONED: |
- VERIFY1(::ReleaseMutex(get(mutex))); |
- return S_OK; |
- case WAIT_TIMEOUT: |
- return HRESULT_FROM_WIN32(ERROR_TIMEOUT); |
- case WAIT_FAILED: |
- return HRESULTFromLastError(); |
- default: |
- return E_FAIL; |
- } |
-} |
- |
-CString GetEnvironmentVariableAsString(const TCHAR* name) { |
- CString value; |
- size_t value_length = ::GetEnvironmentVariable(name, NULL, 0); |
- if (value_length) { |
- VERIFY1(::GetEnvironmentVariable(name, |
- CStrBuf(value, value_length), |
- value_length)); |
- } |
- return value; |
-} |
- |
-// States are documented at |
-// http://technet.microsoft.com/en-us/library/cc721913.aspx. |
-bool IsWindowsInstalling() { |
- static const TCHAR kVistaSetupStateKey[] = |
- _T("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"); |
- static const TCHAR kImageStateValueName[] = _T("ImageState"); |
- static const TCHAR kImageStateUnuseableValue[] = |
- _T("IMAGE_STATE_UNDEPLOYABLE"); |
- static const TCHAR kImageStateGeneralAuditValue[] = |
- _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT"); |
- static const TCHAR kImageStateSpecialAuditValue[] = |
- _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT"); |
- |
- static const TCHAR kXPSetupStateKey[] = _T("System\\Setup"); |
- static const TCHAR kAuditFlagValueName[] = _T("AuditInProgress"); |
- |
- if (vista_util::IsVistaOrLater()) { |
- RegKey vista_setup_key; |
- HRESULT hr = |
- vista_setup_key.Open(HKEY_LOCAL_MACHINE, kVistaSetupStateKey, KEY_READ); |
- if (SUCCEEDED(hr)) { |
- CString state; |
- hr = vista_setup_key.GetValue(kImageStateValueName, &state); |
- if (SUCCEEDED(hr) && |
- !state.IsEmpty() && |
- (0 == state.CompareNoCase(kImageStateUnuseableValue) || |
- 0 == state.CompareNoCase(kImageStateGeneralAuditValue) || |
- 0 == state.CompareNoCase(kImageStateSpecialAuditValue))) |
- return true; // Vista is still installing. |
- } |
- } else { |
- RegKey xp_setup_key; |
- HRESULT hr = |
- xp_setup_key.Open(HKEY_LOCAL_MACHINE, kXPSetupStateKey, KEY_READ); |
- if (SUCCEEDED(hr)) { |
- DWORD audit_flag(0); |
- hr = xp_setup_key.GetValue(kAuditFlagValueName, &audit_flag); |
- if (SUCCEEDED(hr) && 0 != audit_flag) |
- return true; // XP is still installing. |
- } |
- } |
- return false; |
-} |
- |
-HRESULT GetGuid(CString* guid) { |
- GUID guid_local = {0}; |
- HRESULT hr = ::CoCreateGuid(&guid_local); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- *guid = GuidToString(guid_local); |
- return S_OK; |
-} |
- |
-CString GetMessageForSystemErrorCode(DWORD error_code) { |
- CORE_LOG(L3, (_T("[GetMessageForSystemErrorCode][%u]"), error_code)); |
- |
- TCHAR* system_allocated_buffer = NULL; |
- const DWORD kFormatOptions = FORMAT_MESSAGE_ALLOCATE_BUFFER | |
- FORMAT_MESSAGE_FROM_SYSTEM | |
- FORMAT_MESSAGE_IGNORE_INSERTS | |
- FORMAT_MESSAGE_MAX_WIDTH_MASK; |
- DWORD tchars_written = ::FormatMessage( |
- kFormatOptions, |
- NULL, |
- error_code, |
- 0, |
- reinterpret_cast<LPWSTR>(&system_allocated_buffer), |
- 0, |
- NULL); |
- |
- CString message; |
- if (tchars_written > 0) { |
- message = system_allocated_buffer; |
- } else { |
- UTIL_LOG(LW, (_T("[::FormatMessage failed][%u]"), ::GetLastError())); |
- } |
- |
- VERIFY1(!::LocalFree(system_allocated_buffer)); |
- |
- return message; |
-} |
- |
-} // namespace omaha |