Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3754)

Unified Diff: base/utils.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/utils.h ('k') | base/utils_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « base/utils.h ('k') | base/utils_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698