| OLD | NEW |
| (Empty) |
| 1 // Copyright 2003-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/base/utils.h" | |
| 17 | |
| 18 #include <ras.h> | |
| 19 #include <regstr.h> | |
| 20 #include <urlmon.h> | |
| 21 #include <wincrypt.h> | |
| 22 #include <ATLComTime.h> | |
| 23 #include <atlpath.h> | |
| 24 #include <map> | |
| 25 #include <vector> | |
| 26 #include "base/basictypes.h" | |
| 27 #include "base/scoped_ptr.h" | |
| 28 #include "omaha/base/app_util.h" | |
| 29 #include "omaha/base/const_addresses.h" | |
| 30 #include "omaha/base/const_config.h" | |
| 31 #include "omaha/base/const_timeouts.h" | |
| 32 #include "omaha/base/const_object_names.h" | |
| 33 #include "omaha/base/debug.h" | |
| 34 #include "omaha/base/error.h" | |
| 35 #include "omaha/base/file.h" | |
| 36 #include "omaha/base/logging.h" | |
| 37 #include "omaha/base/process.h" | |
| 38 #include "omaha/base/reg_key.h" | |
| 39 #include "omaha/base/safe_format.h" | |
| 40 #include "omaha/base/scope_guard.h" | |
| 41 #include "omaha/base/shell.h" | |
| 42 #include "omaha/base/scoped_any.h" | |
| 43 #include "omaha/base/string.h" | |
| 44 #include "omaha/base/system.h" | |
| 45 #include "omaha/base/system_info.h" | |
| 46 #include "omaha/base/time.h" | |
| 47 #include "omaha/base/user_info.h" | |
| 48 #include "omaha/base/user_rights.h" | |
| 49 #include "omaha/base/vistautil.h" | |
| 50 | |
| 51 namespace omaha { | |
| 52 | |
| 53 namespace { | |
| 54 | |
| 55 // Private object namespaces for Vista processes. | |
| 56 const TCHAR* const kGoopdateBoundaryDescriptor = _T("GoogleUpdate_BD"); | |
| 57 const TCHAR* const kGoopdatePrivateNamespace = _T("GoogleUpdate"); | |
| 58 const TCHAR* const kGoopdatePrivateNamespacePrefix = _T("GoogleUpdate\\"); | |
| 59 | |
| 60 // Helper for IsPrivateNamespaceAvailable(). | |
| 61 // For simplicity, the handles opened here are leaked. We need these until | |
| 62 // process exit, at which point they will be cleaned up automatically by the OS. | |
| 63 bool EnsurePrivateNamespaceAvailable() { | |
| 64 HANDLE boundary_descriptor = | |
| 65 CreateBoundaryDescriptorWWrap(kGoopdateBoundaryDescriptor, 0); | |
| 66 if (NULL == boundary_descriptor) { | |
| 67 DWORD last_error(::GetLastError()); | |
| 68 UTIL_LOG(LE, (_T("CreateBoundaryDescriptor failed[%d]"), last_error)); | |
| 69 return false; | |
| 70 } | |
| 71 | |
| 72 char sid[SECURITY_MAX_SID_SIZE] = {0}; | |
| 73 DWORD size = sizeof(sid); | |
| 74 // Mark the boundary descriptor with the Admins Group SID. Consequently, all | |
| 75 // admins, including SYSTEM, will create objects in the same private | |
| 76 // namespace. | |
| 77 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, sid, &size)) { | |
| 78 UTIL_LOG(LE, (_T("[::CreateWellKnownSid failed][%d]"), ::GetLastError())); | |
| 79 return false; | |
| 80 } | |
| 81 if (!AddSIDToBoundaryDescriptorWrap(&boundary_descriptor, sid)) { | |
| 82 UTIL_LOG(LE, (_T("[AddSIDToBoundaryDescriptor failed][%d]"), | |
| 83 ::GetLastError())); | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 NamedObjectAttributes attr; | |
| 88 GetAdminDaclSecurityAttributes(&attr.sa, GENERIC_ALL); | |
| 89 // The private namespace created here will be used to create objects of the | |
| 90 // form "GoogleUpdate\xyz". As the article "Object Namespaces" on MSDN | |
| 91 // explains, these kernel objects are safe from squatting attacks from lower | |
| 92 // integrity processes. | |
| 93 HANDLE namespace_handle = | |
| 94 CreatePrivateNamespaceWWrap(&attr.sa, | |
| 95 boundary_descriptor, | |
| 96 kGoopdatePrivateNamespace); | |
| 97 if (namespace_handle) { | |
| 98 return true; | |
| 99 } | |
| 100 ASSERT(ERROR_ALREADY_EXISTS == ::GetLastError(), | |
| 101 (_T("CreatePrivateNamespaceW failed: %d"), ::GetLastError())); | |
| 102 | |
| 103 // Another process has already created the namespace. Attempt to open. | |
| 104 namespace_handle = OpenPrivateNamespaceWWrap(boundary_descriptor, | |
| 105 kGoopdatePrivateNamespace); | |
| 106 if (namespace_handle || ::GetLastError() == ERROR_DUP_NAME) { | |
| 107 // ERROR_DUP_NAME indicates that we have called CreatePrivateNamespaceWWrap | |
| 108 // or OpenPrivateNamespaceWWrap before in the same process. Either way, we | |
| 109 // can now create objects prefixed with our private namespace. | |
| 110 return true; | |
| 111 } | |
| 112 | |
| 113 ASSERT(namespace_handle, (_T("[Could not open private namespace][%d]"), | |
| 114 ::GetLastError())); | |
| 115 return false; | |
| 116 } | |
| 117 | |
| 118 } // namespace | |
| 119 | |
| 120 // Returns 0 if an error occurs. | |
| 121 ULONGLONG VersionFromString(const CString& s) { | |
| 122 int pos(0); | |
| 123 unsigned int quad[4] = {0, 0, 0, 0}; | |
| 124 | |
| 125 for (int i = 0; i < 4; ++i) { | |
| 126 CString q = s.Tokenize(_T("."), pos); | |
| 127 if (pos == -1) { | |
| 128 return 0; | |
| 129 } | |
| 130 | |
| 131 int quad_value(0); | |
| 132 if (!String_StringToDecimalIntChecked(q, &quad_value)) { | |
| 133 return 0; | |
| 134 } | |
| 135 | |
| 136 quad[i] = static_cast<unsigned int>(quad_value); | |
| 137 | |
| 138 if (kuint16max < quad[i]) { | |
| 139 return 0; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 if (s.GetLength() + 1 != pos) { | |
| 144 return 0; | |
| 145 } | |
| 146 | |
| 147 return MAKEDLLVERULL(quad[0], quad[1], quad[2], quad[3]); | |
| 148 } | |
| 149 | |
| 150 CString StringFromVersion(ULONGLONG version) { | |
| 151 const WORD version_major = HIWORD(version >> 32); | |
| 152 const WORD version_minor = LOWORD(version >> 32); | |
| 153 const WORD version_build = HIWORD(version); | |
| 154 const WORD version_patch = LOWORD(version); | |
| 155 | |
| 156 CString version_string; | |
| 157 version_string.Format((_T("%u.%u.%u.%u")), | |
| 158 version_major, | |
| 159 version_minor, | |
| 160 version_build, | |
| 161 version_patch); | |
| 162 return version_string; | |
| 163 } | |
| 164 | |
| 165 CString GetCurrentDir() { | |
| 166 TCHAR cur_dir[MAX_PATH] = {0}; | |
| 167 if (!::GetCurrentDirectory(MAX_PATH, cur_dir)) { | |
| 168 return CString(_T('.')); | |
| 169 } | |
| 170 return CString(cur_dir); | |
| 171 } | |
| 172 | |
| 173 HRESULT GetNewFileNameInDirectory(const CString& dir, CString* file_name) { | |
| 174 ASSERT1(file_name); | |
| 175 | |
| 176 GUID guid = {0}; | |
| 177 HRESULT hr = ::CoCreateGuid(&guid); | |
| 178 if (FAILED(hr)) { | |
| 179 CORE_LOG(LEVEL_WARNING, (_T("[CoCreateGuid failed][0x%08x]"), hr)); | |
| 180 return hr; | |
| 181 } | |
| 182 | |
| 183 CString guid_file_name = GuidToString(guid); | |
| 184 CPath file_path(dir); | |
| 185 file_path.Append(guid_file_name); | |
| 186 | |
| 187 *file_name = static_cast<const TCHAR*>(file_path); | |
| 188 return S_OK; | |
| 189 } | |
| 190 | |
| 191 // determines if a time is in the distant past, present, or future | |
| 192 TimeCategory GetTimeCategory(const time64 system_time) { | |
| 193 time64 now = GetCurrent100NSTime(); | |
| 194 | |
| 195 // Times more than a few days in the future are wrong [I will allow a little | |
| 196 // leeway, since it could be set in another future time zone, or a program | |
| 197 // that likes UNC]] | |
| 198 if (system_time > (now + kDaysTo100ns * 5)) { | |
| 199 return FUTURE; | |
| 200 } | |
| 201 | |
| 202 // times more than 40 years ago are wrong | |
| 203 if (system_time < (now - kDaysTo100ns * 365 * 40)) { | |
| 204 return PAST; | |
| 205 } | |
| 206 | |
| 207 return PRESENT; | |
| 208 } | |
| 209 | |
| 210 // Determine if a given time is probably valid | |
| 211 bool IsValidTime(const time64 t) { | |
| 212 return (GetTimeCategory(t) == PRESENT); | |
| 213 } | |
| 214 | |
| 215 LARGE_INTEGER MSto100NSRelative(DWORD ms) { | |
| 216 const __int64 convert_ms_to_100ns_units = 1000 /*ms/us*/ * 10 /*us/100ns*/; | |
| 217 __int64 timeout_100ns = static_cast<__int64>(ms) * convert_ms_to_100ns_units; | |
| 218 LARGE_INTEGER timeout = {0}; | |
| 219 timeout.QuadPart = -timeout_100ns; | |
| 220 return timeout; | |
| 221 } | |
| 222 | |
| 223 // Local System and admins get admin_access_mask. Authenticated non-admins get | |
| 224 // non_admin_access_mask access. | |
| 225 void GetEveryoneDaclSecurityDescriptor(CSecurityDesc* sd, | |
| 226 ACCESS_MASK admin_access_mask, | |
| 227 ACCESS_MASK non_admin_access_mask) { | |
| 228 ASSERT1(sd); | |
| 229 | |
| 230 CDacl dacl; | |
| 231 dacl.AddAllowedAce(Sids::System(), admin_access_mask); | |
| 232 dacl.AddAllowedAce(Sids::Admins(), admin_access_mask); | |
| 233 dacl.AddAllowedAce(Sids::Interactive(), non_admin_access_mask); | |
| 234 | |
| 235 sd->SetDacl(dacl); | |
| 236 sd->MakeAbsolute(); | |
| 237 } | |
| 238 | |
| 239 void GetAdminDaclSecurityDescriptor(CSecurityDesc* sd, ACCESS_MASK accessmask) { | |
| 240 ASSERT1(sd); | |
| 241 | |
| 242 CDacl dacl; | |
| 243 dacl.AddAllowedAce(Sids::System(), accessmask); | |
| 244 dacl.AddAllowedAce(Sids::Admins(), accessmask); | |
| 245 | |
| 246 sd->SetOwner(Sids::Admins()); | |
| 247 sd->SetGroup(Sids::Admins()); | |
| 248 sd->SetDacl(dacl); | |
| 249 sd->MakeAbsolute(); | |
| 250 } | |
| 251 | |
| 252 void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr, | |
| 253 ACCESS_MASK accessmask) { | |
| 254 ASSERT1(sec_attr); | |
| 255 CSecurityDesc sd; | |
| 256 GetAdminDaclSecurityDescriptor(&sd, accessmask); | |
| 257 sec_attr->Set(sd); | |
| 258 } | |
| 259 | |
| 260 HRESULT InitializeClientSecurity() { | |
| 261 return ::CoInitializeSecurity( | |
| 262 NULL, | |
| 263 -1, | |
| 264 NULL, // Let COM choose what authentication services to register. | |
| 265 NULL, | |
| 266 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Data integrity and encryption. | |
| 267 RPC_C_IMP_LEVEL_IMPERSONATE, // Allow server to impersonate. | |
| 268 NULL, | |
| 269 EOAC_DYNAMIC_CLOAKING, | |
| 270 NULL); | |
| 271 } | |
| 272 | |
| 273 HRESULT InitializeServerSecurity(bool allow_calls_from_medium) { | |
| 274 CSecurityDesc sd; | |
| 275 DWORD eole_auth_capabilities = EOAC_DYNAMIC_CLOAKING; | |
| 276 if (allow_calls_from_medium) { | |
| 277 GetEveryoneDaclSecurityDescriptor(&sd, | |
| 278 COM_RIGHTS_EXECUTE, | |
| 279 COM_RIGHTS_EXECUTE); | |
| 280 sd.SetOwner(Sids::Admins()); | |
| 281 sd.SetGroup(Sids::Admins()); | |
| 282 } else if (user_info::IsRunningAsSystem()) { | |
| 283 GetAdminDaclSecurityDescriptor(&sd, COM_RIGHTS_EXECUTE); | |
| 284 } | |
| 285 | |
| 286 HRESULT hr = ::CoInitializeSecurity( | |
| 287 const_cast<SECURITY_DESCRIPTOR*>(sd.GetPSECURITY_DESCRIPTOR()), | |
| 288 -1, | |
| 289 NULL, | |
| 290 NULL, | |
| 291 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, | |
| 292 RPC_C_IMP_LEVEL_IDENTIFY, | |
| 293 NULL, | |
| 294 eole_auth_capabilities, | |
| 295 NULL); | |
| 296 ASSERT(SUCCEEDED(hr), (_T("[InitializeServerSecurity failed][0x%x]"), hr)); | |
| 297 return hr; | |
| 298 } | |
| 299 | |
| 300 // The IGlobalOptions interface is supported from Vista onwards. Callers | |
| 301 // should probably ignore the HRESULT returned, since it is not a critical error | |
| 302 // if turning off exception handling fails. | |
| 303 HRESULT DisableCOMExceptionHandling() { | |
| 304 CComPtr<IGlobalOptions> options; | |
| 305 HRESULT hr = options.CoCreateInstance(CLSID_GlobalOptions); | |
| 306 if (SUCCEEDED(hr)) { | |
| 307 hr = options->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE); | |
| 308 } | |
| 309 | |
| 310 if (FAILED(hr)) { | |
| 311 UTIL_LOG(LE, (_T("[DisableCOMExceptionHandling failed][0x%x]"), hr)); | |
| 312 } | |
| 313 | |
| 314 return hr; | |
| 315 } | |
| 316 | |
| 317 // This function is not thread-safe. | |
| 318 bool IsPrivateNamespaceAvailable(bool is_machine) { | |
| 319 static bool is_initialized = false; | |
| 320 static bool is_available = false; | |
| 321 | |
| 322 if (!is_machine) { | |
| 323 // TODO(Omaha): From a security viewpoint, private namespaces do not add | |
| 324 // much value for the User Omaha. But from a uniformity perspective, makes | |
| 325 // sense to use for both. | |
| 326 return false; | |
| 327 } | |
| 328 | |
| 329 if (is_initialized) { | |
| 330 return is_available; | |
| 331 } | |
| 332 | |
| 333 if (!SystemInfo::IsRunningOnVistaOrLater()) { | |
| 334 is_available = false; | |
| 335 is_initialized = true; | |
| 336 return false; | |
| 337 } | |
| 338 | |
| 339 is_available = EnsurePrivateNamespaceAvailable(); | |
| 340 is_initialized = true; | |
| 341 return is_available; | |
| 342 } | |
| 343 | |
| 344 | |
| 345 void GetNamedObjectAttributes(const TCHAR* base_name, | |
| 346 bool is_machine, | |
| 347 NamedObjectAttributes* attr) { | |
| 348 ASSERT1(base_name); | |
| 349 ASSERT1(attr); | |
| 350 | |
| 351 // TODO(Omaha): Enable this code after we have a better understanding of | |
| 352 // Private Object Namespaces. | |
| 353 #if 0 | |
| 354 if (IsPrivateNamespaceAvailable(is_machine)) { | |
| 355 attr->name = kGoopdatePrivateNamespacePrefix; | |
| 356 } else { | |
| 357 ASSERT1(!SystemInfo::IsRunningOnVistaOrLater()); | |
| 358 #endif | |
| 359 | |
| 360 attr->name = omaha::kGlobalPrefix; | |
| 361 | |
| 362 if (!is_machine) { | |
| 363 CString user_sid; | |
| 364 VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid))); | |
| 365 attr->name += user_sid; | |
| 366 } else { | |
| 367 // Grant access to administrators and system. | |
| 368 GetAdminDaclSecurityAttributes(&attr->sa, GENERIC_ALL); | |
| 369 } | |
| 370 | |
| 371 attr->name += base_name; | |
| 372 UTIL_LOG(L1, (_T("[GetNamedObjectAttributes][named_object=%s]"), attr->name)); | |
| 373 } | |
| 374 | |
| 375 // For now, required_ace_flags is only supported for SE_REGISTRY_KEY objects. | |
| 376 // INHERITED_ACE may be added to the read ACE flags, so it is excluded from | |
| 377 // the comparison with required_ace_flags. | |
| 378 HRESULT AddAllowedAce(const TCHAR* object_name, | |
| 379 SE_OBJECT_TYPE object_type, | |
| 380 const CSid& sid, | |
| 381 ACCESS_MASK required_permissions, | |
| 382 uint8 required_ace_flags) { | |
| 383 ASSERT1(SE_REGISTRY_KEY == object_type || !required_ace_flags); | |
| 384 ASSERT1(0 == (required_ace_flags & INHERITED_ACE)); | |
| 385 | |
| 386 CDacl dacl; | |
| 387 if (!AtlGetDacl(object_name, object_type, &dacl)) { | |
| 388 return HRESULTFromLastError(); | |
| 389 } | |
| 390 | |
| 391 int ace_count = dacl.GetAceCount(); | |
| 392 for (int i = 0; i < ace_count; ++i) { | |
| 393 CSid sid_entry; | |
| 394 ACCESS_MASK existing_permissions = 0; | |
| 395 BYTE existing_ace_flags = 0; | |
| 396 dacl.GetAclEntry(i, | |
| 397 &sid_entry, | |
| 398 &existing_permissions, | |
| 399 NULL, | |
| 400 &existing_ace_flags); | |
| 401 if (sid_entry == sid && | |
| 402 required_permissions == (existing_permissions & required_permissions) && | |
| 403 required_ace_flags == (existing_ace_flags & ~INHERITED_ACE)) { | |
| 404 return S_OK; | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 if (!dacl.AddAllowedAce(sid, required_permissions, required_ace_flags) || | |
| 409 !AtlSetDacl(object_name, object_type, dacl)) { | |
| 410 return HRESULTFromLastError(); | |
| 411 } | |
| 412 | |
| 413 return S_OK; | |
| 414 } | |
| 415 | |
| 416 HRESULT CreateDir(const TCHAR* in_dir, | |
| 417 LPSECURITY_ATTRIBUTES security_attr) { | |
| 418 ASSERT1(in_dir); | |
| 419 CString path; | |
| 420 if (!PathCanonicalize(CStrBuf(path, MAX_PATH), in_dir)) { | |
| 421 return E_FAIL; | |
| 422 } | |
| 423 // Standardize path on backslash so Find works. | |
| 424 path.Replace(_T('/'), _T('\\')); | |
| 425 int next_slash = path.Find(_T('\\')); | |
| 426 while (true) { | |
| 427 int len = 0; | |
| 428 if (next_slash == -1) { | |
| 429 len = path.GetLength(); | |
| 430 } else { | |
| 431 len = next_slash; | |
| 432 } | |
| 433 CString dir(path.Left(len)); | |
| 434 // The check for File::Exists should not be needed. However in certain | |
| 435 // cases, i.e. when the program is run from a n/w drive or from the | |
| 436 // root drive location, the first CreateDirectory fails with an | |
| 437 // E_ACCESSDENIED instead of a ALREADY_EXISTS. Hence we protect the call | |
| 438 // with the exists. | |
| 439 if (!File::Exists(dir)) { | |
| 440 if (!::CreateDirectory(dir, security_attr)) { | |
| 441 DWORD error = ::GetLastError(); | |
| 442 if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) { | |
| 443 return HRESULT_FROM_WIN32(error); | |
| 444 } | |
| 445 } | |
| 446 } | |
| 447 if (next_slash == -1) { | |
| 448 break; | |
| 449 } | |
| 450 next_slash = path.Find(_T('\\'), next_slash + 1); | |
| 451 } | |
| 452 | |
| 453 return S_OK; | |
| 454 } | |
| 455 | |
| 456 HRESULT GetFolderPath(int csidl, CString* path) { | |
| 457 if (!path) { | |
| 458 return E_INVALIDARG; | |
| 459 } | |
| 460 | |
| 461 TCHAR buffer[MAX_PATH] = {0}; | |
| 462 HRESULT hr = ::SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buffer); | |
| 463 if (FAILED(hr)) { | |
| 464 return hr; | |
| 465 } | |
| 466 | |
| 467 *path = buffer; | |
| 468 return S_OK; | |
| 469 } | |
| 470 | |
| 471 // Delete directory files. If failed, try to schedule deletion at next reboot | |
| 472 HRESULT DeleteDirectoryFiles(const TCHAR* dir_name) { | |
| 473 ASSERT1(dir_name); | |
| 474 return DeleteWildcardFiles(dir_name, _T("*")); | |
| 475 } | |
| 476 | |
| 477 // Delete a set of wildcards within dir_name. | |
| 478 // If unable to delete immediately, try to schedule deletion at next reboot | |
| 479 HRESULT DeleteWildcardFiles(const TCHAR* dir_name, const TCHAR* wildcard_name) { | |
| 480 ASSERT1(dir_name); | |
| 481 ASSERT1(wildcard_name); | |
| 482 | |
| 483 HRESULT hr = S_OK; | |
| 484 | |
| 485 WIN32_FIND_DATA find_data; | |
| 486 SetZero(find_data); | |
| 487 | |
| 488 CString find_file(dir_name); | |
| 489 find_file += _T('\\'); | |
| 490 find_file += wildcard_name; | |
| 491 | |
| 492 scoped_hfind hfind(::FindFirstFile(find_file, &find_data)); | |
| 493 if (!hfind) { | |
| 494 if (::GetLastError() == ERROR_NO_MORE_FILES) { | |
| 495 return S_OK; | |
| 496 } else { | |
| 497 hr = HRESULTFromLastError(); | |
| 498 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") | |
| 499 _T("[failed to get first file][0x%08x]"), hr)); | |
| 500 return hr; | |
| 501 } | |
| 502 } | |
| 503 | |
| 504 do { | |
| 505 if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 506 CString specific_file_name(dir_name); | |
| 507 specific_file_name += _T('\\'); | |
| 508 specific_file_name += find_data.cFileName; | |
| 509 if (!::DeleteFile(specific_file_name)) { | |
| 510 if (!SUCCEEDED(hr = File::DeleteAfterReboot(specific_file_name))) { | |
| 511 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") | |
| 512 _T("[failed to delete after reboot]") | |
| 513 _T("[%s][0x%08x]"), specific_file_name, hr)); | |
| 514 } | |
| 515 } | |
| 516 } | |
| 517 } while (::FindNextFile(get(hfind), &find_data)); | |
| 518 | |
| 519 if (::GetLastError() != ERROR_NO_MORE_FILES) { | |
| 520 hr = HRESULTFromLastError(); | |
| 521 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]") | |
| 522 _T("[failed to get next file][0x%08x]"), hr)); | |
| 523 } | |
| 524 | |
| 525 return hr; | |
| 526 } | |
| 527 | |
| 528 // Delete directory and files within. If failed, try to schedule deletion at | |
| 529 // next reboot | |
| 530 // TODO(Omaha) - the code to delete the directory is complicated, | |
| 531 // especially the way the result code is built from hr and hr1. I wonder if we | |
| 532 // could simplify this by reimplementing it on top of SHFileOperation and | |
| 533 // also save a few tens of bytes in the process. | |
| 534 HRESULT DeleteDirectory(const TCHAR* dir_name) { | |
| 535 ASSERT1(dir_name); | |
| 536 | |
| 537 if (!SafeDirectoryNameForDeletion(dir_name)) { | |
| 538 return E_FAIL; | |
| 539 } | |
| 540 | |
| 541 // Make sure the directory exists (it is ok if it doesn't) | |
| 542 DWORD dir_attributes = ::GetFileAttributes(dir_name); | |
| 543 if (dir_attributes == INVALID_FILE_ATTRIBUTES) { | |
| 544 if (::GetLastError() == ERROR_FILE_NOT_FOUND) | |
| 545 return S_OK; // Ok if directory is missing | |
| 546 else | |
| 547 return HRESULTFromLastError(); | |
| 548 } | |
| 549 // Confirm it is a directory | |
| 550 if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 551 return E_FAIL; | |
| 552 } | |
| 553 | |
| 554 // Try to delete all files at best effort | |
| 555 // Return the first HRESULT error encountered | |
| 556 | |
| 557 // First delete all the normal files | |
| 558 HRESULT hr = DeleteDirectoryFiles(dir_name); | |
| 559 | |
| 560 // Recursively delete any subdirectories | |
| 561 | |
| 562 WIN32_FIND_DATA find_data = {0}; | |
| 563 | |
| 564 CString find_file(dir_name); | |
| 565 find_file += _T("\\*"); | |
| 566 | |
| 567 // Note that the follows are enclosed in a block because we need to close the | |
| 568 // find handle before deleting the directorty itself | |
| 569 { | |
| 570 scoped_hfind hfind(::FindFirstFile(find_file, &find_data)); | |
| 571 if (!hfind) { | |
| 572 if (::GetLastError() == ERROR_NO_MORE_FILES) { | |
| 573 return hr; | |
| 574 } else { | |
| 575 HRESULT hr1 = HRESULTFromLastError(); | |
| 576 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteDirectory]") | |
| 577 _T("[failed to get first file][0x%08x]"), hr1)); | |
| 578 return SUCCEEDED(hr) ? hr1 : hr; | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 do { | |
| 583 if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |
| 584 if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 || | |
| 585 String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) { | |
| 586 continue; | |
| 587 } | |
| 588 | |
| 589 CString sub_dir(dir_name); | |
| 590 sub_dir += _T("\\"); | |
| 591 sub_dir += find_data.cFileName; | |
| 592 HRESULT hr1 = DeleteDirectory(sub_dir); | |
| 593 if (SUCCEEDED(hr) && FAILED(hr1)) { | |
| 594 hr = hr1; | |
| 595 } | |
| 596 } | |
| 597 } | |
| 598 while (::FindNextFile(get(hfind), &find_data)); | |
| 599 } | |
| 600 | |
| 601 // Delete the empty directory itself | |
| 602 if (!::RemoveDirectory(dir_name)) { | |
| 603 HRESULT hr1 = E_FAIL; | |
| 604 if (FAILED(hr1 = File::DeleteAfterReboot(dir_name))) { | |
| 605 UTIL_LOG(LE, (_T("[DeleteDirectory][failed to delete after reboot]") | |
| 606 _T("[%s][0x%08x]"), dir_name, hr1)); | |
| 607 } | |
| 608 | |
| 609 if (SUCCEEDED(hr) && FAILED(hr1)) { | |
| 610 hr = hr1; | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 return hr; | |
| 615 } | |
| 616 | |
| 617 // Returns true if this directory name is 'safe' for deletion (doesn't contain | |
| 618 // "..", doesn't specify a drive root) | |
| 619 bool SafeDirectoryNameForDeletion(const TCHAR* dir_name) { | |
| 620 ASSERT1(dir_name); | |
| 621 | |
| 622 // empty name isn't allowed | |
| 623 if (!(dir_name && *dir_name)) { | |
| 624 return false; | |
| 625 } | |
| 626 | |
| 627 // require a character other than \/:. after the last : | |
| 628 // disallow anything with ".." | |
| 629 bool ok = false; | |
| 630 for (const TCHAR* s = dir_name; *s; ++s) { | |
| 631 if (*s != _T('\\') && *s != _T('/') && *s != _T(':') && *s != _T('.')) { | |
| 632 ok = true; | |
| 633 } | |
| 634 if (*s == _T('.') && s > dir_name && *(s-1) == _T('.')) { | |
| 635 return false; | |
| 636 } | |
| 637 if (*s == _T(':')) { | |
| 638 ok = false; | |
| 639 } | |
| 640 } | |
| 641 return ok; | |
| 642 } | |
| 643 | |
| 644 // Utility function that deletes either a file or directory, | |
| 645 // before or after reboot | |
| 646 HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname) { | |
| 647 if (!File::Exists(targetname)) { | |
| 648 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
| 649 } | |
| 650 | |
| 651 HRESULT hr = E_FAIL; | |
| 652 if (File::IsDirectory(targetname)) { | |
| 653 // DeleteDirectory will schedule deletion at next reboot if it cannot delete | |
| 654 // immediately. | |
| 655 hr = DeleteDirectory(targetname); | |
| 656 } else { | |
| 657 hr = File::Remove(targetname); | |
| 658 // If failed, schedule deletion at next reboot | |
| 659 if (FAILED(hr)) { | |
| 660 UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]") | |
| 661 _T("[trying to delete after reboot][%s]"), targetname)); | |
| 662 hr = File::DeleteAfterReboot(targetname); | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 if (FAILED(hr)) { | |
| 667 UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]") | |
| 668 _T("[failed to delete][%s][0x%08x]"), targetname, hr)); | |
| 669 } | |
| 670 | |
| 671 return hr; | |
| 672 } | |
| 673 | |
| 674 | |
| 675 // Internal implementation of the safe version of getting size of all files in | |
| 676 // a directory. It is able to abort the counting if one of the maximum criteria | |
| 677 // is reached. | |
| 678 HRESULT InternalSafeGetDirectorySize(const TCHAR* dir_name, | |
| 679 uint64* size, | |
| 680 HANDLE shutdown_event, | |
| 681 uint64 max_size, | |
| 682 int curr_file_count, | |
| 683 int max_file_count, | |
| 684 int curr_depth, | |
| 685 int max_depth, | |
| 686 DWORD end_time_ms) { | |
| 687 ASSERT1(dir_name && *dir_name); | |
| 688 ASSERT1(size); | |
| 689 | |
| 690 CString dir_find_name = String_MakeEndWith(dir_name, _T("\\"), false); | |
| 691 dir_find_name += _T("*"); | |
| 692 WIN32_FIND_DATA find_data = {0}; | |
| 693 scoped_hfind hfind(::FindFirstFile(dir_find_name, &find_data)); | |
| 694 if (!hfind) { | |
| 695 return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK : | |
| 696 HRESULTFromLastError(); | |
| 697 } | |
| 698 | |
| 699 do { | |
| 700 // Bail out if shutting down | |
| 701 if (shutdown_event && IsHandleSignaled(shutdown_event)) { | |
| 702 return E_ABORT; | |
| 703 } | |
| 704 | |
| 705 // Bail out if reaching maximum running time | |
| 706 if (end_time_ms && ::GetTickCount() >= end_time_ms) { | |
| 707 UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") | |
| 708 _T("[reaching max running time][%s][%u]"), | |
| 709 dir_name, end_time_ms)); | |
| 710 return E_ABORT; | |
| 711 } | |
| 712 | |
| 713 // Skip reparse point since it might be a hard link which could cause an | |
| 714 // infinite recursive directory loop. | |
| 715 if (find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { | |
| 716 continue; | |
| 717 } | |
| 718 | |
| 719 if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |
| 720 // Skip . and .. | |
| 721 if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 || | |
| 722 String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) { | |
| 723 continue; | |
| 724 } | |
| 725 | |
| 726 // Bail out if reaching maximum depth | |
| 727 if (max_depth && curr_depth + 1 >= max_depth) { | |
| 728 UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") | |
| 729 _T("[reaching max depth][%s][%u]"), dir_name, max_depth)); | |
| 730 return E_ABORT; | |
| 731 } | |
| 732 | |
| 733 // Walk over sub-directory | |
| 734 CString sub_dir_name = String_MakeEndWith(dir_name, _T("\\"), false); | |
| 735 sub_dir_name += find_data.cFileName; | |
| 736 RET_IF_FAILED(InternalSafeGetDirectorySize(sub_dir_name, | |
| 737 size, | |
| 738 shutdown_event, | |
| 739 max_size, | |
| 740 curr_file_count, | |
| 741 max_file_count, | |
| 742 curr_depth + 1, | |
| 743 max_depth, | |
| 744 end_time_ms)); | |
| 745 } else { | |
| 746 // Bail out if reaching maximum number of files | |
| 747 ++curr_file_count; | |
| 748 if (max_file_count && curr_file_count >= max_file_count) { | |
| 749 UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") | |
| 750 _T("[reaching max file count][%s][%u]"), | |
| 751 dir_name, max_file_count)); | |
| 752 return E_ABORT; | |
| 753 } | |
| 754 | |
| 755 // Count the file size | |
| 756 uint64 file_size = | |
| 757 ((static_cast<uint64>((find_data.nFileSizeHigh)) << 32)) + | |
| 758 static_cast<uint64>(find_data.nFileSizeLow); | |
| 759 *size += file_size; | |
| 760 | |
| 761 // Bail out if reaching maximum size | |
| 762 if (max_size && *size >= max_size) { | |
| 763 UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]") | |
| 764 _T("[reaching max size][%s][%u]"), dir_name, max_size)); | |
| 765 return E_ABORT; | |
| 766 } | |
| 767 } | |
| 768 } while (::FindNextFile(get(hfind), &find_data)); | |
| 769 | |
| 770 return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK : | |
| 771 HRESULTFromLastError(); | |
| 772 } | |
| 773 | |
| 774 // The safe version of getting size of all files in a directory | |
| 775 // It is able to abort the counting if one of the maximum criteria is reached | |
| 776 HRESULT SafeGetDirectorySize(const TCHAR* dir_name, | |
| 777 uint64* size, | |
| 778 HANDLE shutdown_event, | |
| 779 uint64 max_size, | |
| 780 int max_file_count, | |
| 781 int max_depth, | |
| 782 int max_running_time_ms) { | |
| 783 ASSERT1(dir_name && *dir_name); | |
| 784 ASSERT1(size); | |
| 785 | |
| 786 *size = 0; | |
| 787 | |
| 788 DWORD end_time = 0; | |
| 789 if (max_running_time_ms > 0) { | |
| 790 end_time = ::GetTickCount() + max_running_time_ms; | |
| 791 } | |
| 792 return InternalSafeGetDirectorySize(dir_name, | |
| 793 size, | |
| 794 shutdown_event, | |
| 795 max_size, | |
| 796 0, | |
| 797 max_file_count, | |
| 798 0, | |
| 799 max_depth, | |
| 800 end_time); | |
| 801 } | |
| 802 | |
| 803 // Get size of all files in a directory | |
| 804 HRESULT GetDirectorySize(const TCHAR* dir_name, uint64* size) { | |
| 805 ASSERT1(dir_name && *dir_name); | |
| 806 ASSERT1(size); | |
| 807 return SafeGetDirectorySize(dir_name, size, NULL, 0, 0, 0, 0); | |
| 808 } | |
| 809 | |
| 810 // Handles the logic to determine the handle that was signaled | |
| 811 // as a result of calling *WaitForMultipleObjects. | |
| 812 HRESULT GetSignaledObjectPosition(uint32 cnt, DWORD res, uint32* pos) { | |
| 813 ASSERT1(pos); | |
| 814 | |
| 815 if (res == WAIT_FAILED) { | |
| 816 return S_FALSE; | |
| 817 } | |
| 818 | |
| 819 #pragma warning(disable : 4296) | |
| 820 // C4296: '>=' : expression is always true | |
| 821 if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cnt)) { | |
| 822 *pos = res - WAIT_OBJECT_0; | |
| 823 return S_OK; | |
| 824 } | |
| 825 #pragma warning(default : 4296) | |
| 826 | |
| 827 if ((res >= WAIT_ABANDONED_0) && (res < WAIT_ABANDONED_0 + cnt)) { | |
| 828 *pos = res - WAIT_ABANDONED_0; | |
| 829 return S_OK; | |
| 830 } | |
| 831 return E_INVALIDARG; | |
| 832 } | |
| 833 | |
| 834 // Supports all of the other WaitWithMessage* functions. | |
| 835 // | |
| 836 // Returns: | |
| 837 // S_OK when the message loop should continue. | |
| 838 // S_FALSE when it receives something that indicates the | |
| 839 // loop should quit. | |
| 840 // E_* only when GetMessage failed | |
| 841 // | |
| 842 // This function is not exposed outside of this file. Only | |
| 843 // friendly wrappers of it are. | |
| 844 HRESULT WaitWithMessageLoopAnyInternal( | |
| 845 const HANDLE* phandles, | |
| 846 uint32 cnt, | |
| 847 uint32* pos, | |
| 848 MessageHandlerInternalInterface* message_handler) { | |
| 849 ASSERT1(pos && message_handler); | |
| 850 // cnt and phandles are either both zero or both not zero. | |
| 851 ASSERT1(!cnt == !phandles); | |
| 852 | |
| 853 // Loop until an error happens or the wait is satisfied by a signaled | |
| 854 // object or an abandoned mutex. | |
| 855 for (;;) { | |
| 856 MSG msg = {0}; | |
| 857 | |
| 858 // Process the messages in the input queue. | |
| 859 while (::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) { | |
| 860 BOOL ret = false; | |
| 861 if ((ret = ::GetMessage(&msg, NULL, 0, 0)) != 0) { | |
| 862 if (ret == -1) { | |
| 863 HRESULT hr = HRESULTFromLastError(); | |
| 864 UTIL_LOG(LE, (_T("[WaitWithMessageLoopAnyInternal]") | |
| 865 _T("[GetMessage failed][0x%08x]"), hr)); | |
| 866 return hr; | |
| 867 } | |
| 868 message_handler->Process(&msg, &phandles, &cnt); | |
| 869 } else { | |
| 870 // We need to re-post the quit message we retrieved so that it could | |
| 871 // propagate to the outer layer. Otherwise, the program will seem to | |
| 872 // "get stuck" in its shutdown code. | |
| 873 ::PostQuitMessage(msg.wParam); | |
| 874 return S_FALSE; | |
| 875 } | |
| 876 | |
| 877 // WaitForMultipleObjects fails if cnt == 0. | |
| 878 if (cnt) { | |
| 879 // Briefly check the state of the handle array to see if something | |
| 880 // has signaled as we processed a message. | |
| 881 ASSERT1(phandles); | |
| 882 DWORD res = ::WaitForMultipleObjects(cnt, phandles, false, 0); | |
| 883 ASSERT1(res != WAIT_FAILED); | |
| 884 HRESULT hr = GetSignaledObjectPosition(cnt, res, pos); | |
| 885 if (SUCCEEDED(hr)) { | |
| 886 return hr; | |
| 887 } | |
| 888 } | |
| 889 } | |
| 890 | |
| 891 // The wait with message. It is satisfied by either the objects getting | |
| 892 // signaled or when messages enter the message queue. | |
| 893 // TODO(omaha): implementing timeout is a little bit tricky since we | |
| 894 // want the timeout on the handles only and the native API does not | |
| 895 // have this semantic. | |
| 896 // | |
| 897 // TODO(omaha): use a waitable timer to implement the timeout. | |
| 898 // | |
| 899 // When cnt is zero then the execution flow waits here until messages | |
| 900 // arrive in the input queue. Unlike WaitForMultipleObjects, | |
| 901 // MsgWaitForMultipleObjects does not error out when cnt == 0. | |
| 902 const DWORD timeout = INFINITE; | |
| 903 DWORD res(::MsgWaitForMultipleObjects(cnt, phandles, false, timeout, | |
| 904 QS_ALLINPUT)); | |
| 905 ASSERT((res != WAIT_FAILED), | |
| 906 (_T("[MsgWaitForMultipleObjects returned WAIT_FAILED][%u]"), | |
| 907 ::GetLastError())); | |
| 908 | |
| 909 ASSERT1(res != WAIT_TIMEOUT); | |
| 910 | |
| 911 HRESULT hr = GetSignaledObjectPosition(cnt, res, pos); | |
| 912 if (SUCCEEDED(hr)) { | |
| 913 return hr; | |
| 914 } | |
| 915 } | |
| 916 } | |
| 917 | |
| 918 // The simplest implementation of a message processor | |
| 919 void BasicMessageHandler::Process(MSG* msg) { | |
| 920 ASSERT1(msg); | |
| 921 ::TranslateMessage(msg); | |
| 922 ::DispatchMessage(msg); | |
| 923 } | |
| 924 | |
| 925 class BasicMessageHandlerInternal : public BasicMessageHandler, | |
| 926 public MessageHandlerInternalInterface { | |
| 927 public: | |
| 928 BasicMessageHandlerInternal() {} | |
| 929 virtual void Process(MSG* msg, const HANDLE**, uint32*) { | |
| 930 BasicMessageHandler::Process(msg); | |
| 931 } | |
| 932 private: | |
| 933 DISALLOW_EVIL_CONSTRUCTORS(BasicMessageHandlerInternal); | |
| 934 }; | |
| 935 | |
| 936 | |
| 937 bool WaitWithMessageLoopAny(const std::vector<HANDLE>& handles, uint32* pos) { | |
| 938 BasicMessageHandlerInternal msg_handler; | |
| 939 return WaitWithMessageLoopAnyInternal(&handles.front(), handles.size(), pos, | |
| 940 &msg_handler) != S_FALSE; | |
| 941 } | |
| 942 | |
| 943 bool WaitWithMessageLoopAll(const std::vector<HANDLE>& handles) { | |
| 944 // make a copy of the vector, as objects must be removed from the | |
| 945 // wait array as they get signaled. | |
| 946 std::vector<HANDLE> h(handles); | |
| 947 | |
| 948 // The function is mainly implemented in terms of WaitWithMessageLoopAny | |
| 949 | |
| 950 // loop until all objects are signaled. | |
| 951 while (!h.empty()) { | |
| 952 uint32 pos(static_cast<uint32>(-1)); | |
| 953 if (!WaitWithMessageLoopAny(h, &pos)) return false; | |
| 954 ASSERT1(pos < h.size()); | |
| 955 h.erase(h.begin() + pos); // remove the signaled object and loop | |
| 956 } | |
| 957 | |
| 958 return true; | |
| 959 } | |
| 960 | |
| 961 bool WaitWithMessageLoop(HANDLE h) { | |
| 962 BasicMessageHandlerInternal msg_handler; | |
| 963 uint32 pos(static_cast<uint32>(-1)); | |
| 964 bool res = | |
| 965 WaitWithMessageLoopAnyInternal(&h, 1, &pos, &msg_handler) != S_FALSE; | |
| 966 if (res) { | |
| 967 // It's the first and the only handle that it is signaled. | |
| 968 ASSERT1(pos == 0); | |
| 969 } | |
| 970 return res; | |
| 971 } | |
| 972 | |
| 973 // Wait with message loop for a certain period of time | |
| 974 bool WaitWithMessageLoopTimed(DWORD ms) { | |
| 975 scoped_timer timer(::CreateWaitableTimer(NULL, | |
| 976 true, // manual reset | |
| 977 NULL)); | |
| 978 ASSERT1(get(timer)); | |
| 979 LARGE_INTEGER timeout = MSto100NSRelative(ms); | |
| 980 BOOL timer_ok = ::SetWaitableTimer(get(timer), | |
| 981 &timeout, | |
| 982 0, | |
| 983 NULL, | |
| 984 NULL, | |
| 985 false); | |
| 986 ASSERT1(timer_ok); | |
| 987 return WaitWithMessageLoop(get(timer)); | |
| 988 } | |
| 989 | |
| 990 MessageLoopWithWait::MessageLoopWithWait() : message_handler_(NULL) { | |
| 991 } | |
| 992 | |
| 993 void MessageLoopWithWait::set_message_handler( | |
| 994 MessageHandlerInterface* message_handler) { | |
| 995 message_handler_ = message_handler; | |
| 996 } | |
| 997 | |
| 998 // The message loop and handle callback routine. | |
| 999 HRESULT MessageLoopWithWait::Process() { | |
| 1000 while (true) { | |
| 1001 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1002 | |
| 1003 // The implementation allows for an empty array of handles. Taking the | |
| 1004 // address of elements in an empty container is not allowed so we must | |
| 1005 // deal with this case here. | |
| 1006 size_t pos(0); | |
| 1007 HRESULT hr = WaitWithMessageLoopAnyInternal( | |
| 1008 callback_handles_.empty() ? NULL : &callback_handles_.front(), | |
| 1009 callback_handles_.size(), | |
| 1010 &pos, | |
| 1011 this); | |
| 1012 | |
| 1013 // In addition to E_*, S_FALSE should cause a return to happen here. | |
| 1014 if (hr != S_OK) { | |
| 1015 return hr; | |
| 1016 } | |
| 1017 | |
| 1018 ASSERT1(pos < callback_handles_.size()); | |
| 1019 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1020 | |
| 1021 HANDLE signaled_handle = callback_handles_[pos]; | |
| 1022 WaitCallbackInterface* callback_interface = callbacks_[pos]; | |
| 1023 RemoveHandleAt(pos); | |
| 1024 | |
| 1025 if (!callback_interface->HandleSignaled(signaled_handle)) { | |
| 1026 return S_OK; | |
| 1027 } | |
| 1028 } | |
| 1029 } | |
| 1030 | |
| 1031 // Handles one messgae and adjust the handles and cnt as appropriate after | |
| 1032 // handling the message. | |
| 1033 void MessageLoopWithWait::Process(MSG* msg, const HANDLE** handles, | |
| 1034 uint32* cnt) { | |
| 1035 ASSERT1(msg && handles && cnt); | |
| 1036 | |
| 1037 if (message_handler_) { | |
| 1038 message_handler_->Process(msg); | |
| 1039 } | |
| 1040 | |
| 1041 // Set the handles and count again because they may have changed | |
| 1042 // while processing the message. | |
| 1043 *handles = callback_handles_.empty() ? NULL : &callback_handles_.front(); | |
| 1044 *cnt = callback_handles_.size(); | |
| 1045 } | |
| 1046 // Starts waiting on the given handle | |
| 1047 bool MessageLoopWithWait::RegisterWaitForSingleObject( | |
| 1048 HANDLE handle, WaitCallbackInterface* callback) { | |
| 1049 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1050 ASSERT1(callback != NULL); | |
| 1051 | |
| 1052 if (callback_handles_.size() >= MAXIMUM_WAIT_OBJECTS - 1) { | |
| 1053 return false; | |
| 1054 } | |
| 1055 | |
| 1056 // In case the user is registering a handle, that they previous added | |
| 1057 // remove the previous one before adding it back into the array. | |
| 1058 UnregisterWait(handle); | |
| 1059 callback_handles_.push_back(handle); | |
| 1060 callbacks_.push_back(callback); | |
| 1061 | |
| 1062 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1063 return true; | |
| 1064 } | |
| 1065 | |
| 1066 // Finds the given handle and stops waiting on it | |
| 1067 bool MessageLoopWithWait::UnregisterWait(HANDLE handle) { | |
| 1068 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1069 | |
| 1070 for (uint32 index = 0; index < callback_handles_.size() ; index++) { | |
| 1071 if (callback_handles_[index] == handle) { | |
| 1072 RemoveHandleAt(index); | |
| 1073 return true; | |
| 1074 } | |
| 1075 } | |
| 1076 return false; | |
| 1077 } | |
| 1078 | |
| 1079 // Removes the wait handle at the given position | |
| 1080 void MessageLoopWithWait::RemoveHandleAt(uint32 pos) { | |
| 1081 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1082 ASSERT1(pos < callback_handles_.size()); | |
| 1083 | |
| 1084 callback_handles_.erase(callback_handles_.begin() + pos); | |
| 1085 callbacks_.erase(callbacks_.begin() + pos); | |
| 1086 | |
| 1087 ASSERT1(callback_handles_.size() == callbacks_.size()); | |
| 1088 } | |
| 1089 | |
| 1090 HRESULT CallEntryPoint0(const TCHAR* dll_path, | |
| 1091 const char* function_name, | |
| 1092 HRESULT* result) { | |
| 1093 ASSERT1(dll_path); | |
| 1094 ASSERT1(::lstrlen(dll_path) > 0); | |
| 1095 ASSERT1(function_name); | |
| 1096 ASSERT1(::strlen(function_name) > 0); | |
| 1097 ASSERT1(result); | |
| 1098 | |
| 1099 scoped_library dll(::LoadLibrary(dll_path)); | |
| 1100 if (!dll) { | |
| 1101 return HRESULTFromLastError(); | |
| 1102 } | |
| 1103 | |
| 1104 HRESULT (*proc)() = reinterpret_cast<HRESULT (*)()>( | |
| 1105 ::GetProcAddress(get(dll), function_name)); | |
| 1106 if (!proc) { | |
| 1107 return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); | |
| 1108 } | |
| 1109 | |
| 1110 *result = (proc)(); | |
| 1111 return S_OK; | |
| 1112 } | |
| 1113 | |
| 1114 // Register a DLL | |
| 1115 HRESULT RegisterDll(const TCHAR* dll_path) { | |
| 1116 HRESULT hr = S_OK; | |
| 1117 HRESULT hr_call = CallEntryPoint0(dll_path, "DllRegisterServer", &hr); | |
| 1118 if (SUCCEEDED(hr_call)) { | |
| 1119 return hr; | |
| 1120 } | |
| 1121 return hr_call; | |
| 1122 } | |
| 1123 | |
| 1124 // Unregister a DLL | |
| 1125 HRESULT UnregisterDll(const TCHAR* dll_path) { | |
| 1126 HRESULT hr = S_OK; | |
| 1127 HRESULT hr_call = CallEntryPoint0(dll_path, "DllUnregisterServer", &hr); | |
| 1128 if (SUCCEEDED(hr_call)) { | |
| 1129 return hr; | |
| 1130 } | |
| 1131 return hr_call; | |
| 1132 } | |
| 1133 | |
| 1134 // Register/unregister an EXE | |
| 1135 HRESULT RegisterOrUnregisterExe(const TCHAR* exe_path, const TCHAR* cmd_line) { | |
| 1136 ASSERT1(exe_path); | |
| 1137 ASSERT1(cmd_line); | |
| 1138 | |
| 1139 // cmd_line parameter really contains the arguments to be passed | |
| 1140 // on the process creation command line. | |
| 1141 PROCESS_INFORMATION pi = {0}; | |
| 1142 HRESULT hr = System::StartProcessWithArgsAndInfo(exe_path, cmd_line, &pi); | |
| 1143 if (FAILED(hr)) { | |
| 1144 UTIL_LOG(LEVEL_WARNING, (_T("[RegisterOrUnregisterExe]") | |
| 1145 _T("[failed to start process]") | |
| 1146 _T("[%s][%s][0x%08x]"), exe_path, cmd_line, hr)); | |
| 1147 return hr; | |
| 1148 } | |
| 1149 // Take ownership of the handles for clean up. | |
| 1150 scoped_thread thread(pi.hThread); | |
| 1151 scoped_process process(pi.hProcess); | |
| 1152 | |
| 1153 // ATL COM servers return an HRESULT on exit. There is a case in which they | |
| 1154 // return -1 which seems like a bug in ATL. It appears there is no | |
| 1155 // documented convention on what a local server would return for errors. | |
| 1156 // There is a possibility that a server would return Windows errors. | |
| 1157 | |
| 1158 // Wait on the process to exit and return the exit code of the process. | |
| 1159 DWORD result(::WaitForSingleObject(get(process), INFINITE)); | |
| 1160 DWORD exit_code(0); | |
| 1161 if (result == WAIT_OBJECT_0 && | |
| 1162 ::GetExitCodeProcess(get(process), &exit_code)) { | |
| 1163 return static_cast<HRESULT>(exit_code); | |
| 1164 } else { | |
| 1165 return HRESULT_FROM_WIN32(ERROR_TIMEOUT); | |
| 1166 } | |
| 1167 } | |
| 1168 | |
| 1169 // Register a COM Local Server | |
| 1170 HRESULT RegisterServer(const TCHAR* exe_path) { | |
| 1171 return RegisterOrUnregisterExe(exe_path, _T("/RegServer")); | |
| 1172 } | |
| 1173 | |
| 1174 // Unregister a COM Local Server | |
| 1175 HRESULT UnregisterServer(const TCHAR* exe_path) { | |
| 1176 return RegisterOrUnregisterExe(exe_path, _T("/UnregServer")); | |
| 1177 } | |
| 1178 | |
| 1179 // Register a Service | |
| 1180 HRESULT RegisterService(const TCHAR* exe_path) { | |
| 1181 return RegisterOrUnregisterExe(exe_path, _T("/Service")); | |
| 1182 } | |
| 1183 | |
| 1184 // Unregister a Service | |
| 1185 HRESULT UnregisterService(const TCHAR* exe_path) { | |
| 1186 // Unregistering a service is via UnregServer | |
| 1187 return RegisterOrUnregisterExe(exe_path, _T("/UnregServer")); | |
| 1188 } | |
| 1189 | |
| 1190 // Adapted from gds installer/install/work_list.cpp: InstallServiceExecutable | |
| 1191 HRESULT RunService(const TCHAR* service_name) { | |
| 1192 scoped_service manager(::OpenSCManager(NULL, // local machine | |
| 1193 NULL, // ServicesActive database | |
| 1194 STANDARD_RIGHTS_READ)); | |
| 1195 ASSERT1(get(manager)); | |
| 1196 if (!get(manager)) { | |
| 1197 return HRESULTFromLastError(); | |
| 1198 } | |
| 1199 | |
| 1200 scoped_service service(::OpenService(get(manager), service_name, | |
| 1201 SERVICE_START)); | |
| 1202 ASSERT1(get(service)); | |
| 1203 if (!get(service)) { | |
| 1204 return HRESULTFromLastError(); | |
| 1205 } | |
| 1206 | |
| 1207 UTIL_LOG(L2, (_T("start service"))); | |
| 1208 if (!::StartService(get(service), 0, NULL)) { | |
| 1209 return HRESULTFromLastError(); | |
| 1210 } | |
| 1211 return S_OK; | |
| 1212 } | |
| 1213 | |
| 1214 | |
| 1215 HRESULT ReadEntireFile(const TCHAR* filepath, | |
| 1216 uint32 max_len, | |
| 1217 std::vector<byte>* buffer_out) { | |
| 1218 return ReadEntireFileShareMode(filepath, max_len, 0, buffer_out); | |
| 1219 } | |
| 1220 | |
| 1221 HRESULT ReadEntireFileShareMode(const TCHAR* filepath, | |
| 1222 uint32 max_len, | |
| 1223 DWORD share_mode, | |
| 1224 std::vector<byte>* buffer_out) { | |
| 1225 ASSERT1(filepath); | |
| 1226 ASSERT1(buffer_out); | |
| 1227 | |
| 1228 File file; | |
| 1229 HRESULT hr = file.OpenShareMode(filepath, false, false, share_mode); | |
| 1230 if (FAILED(hr)) { | |
| 1231 // File missing. | |
| 1232 return hr; | |
| 1233 } | |
| 1234 | |
| 1235 ON_SCOPE_EXIT_OBJ(file, &File::Close); | |
| 1236 | |
| 1237 uint32 file_len = 0; | |
| 1238 hr = file.GetLength(&file_len); | |
| 1239 if (FAILED(hr)) { | |
| 1240 // Should never happen | |
| 1241 return hr; | |
| 1242 } | |
| 1243 | |
| 1244 if (max_len != 0 && file_len > max_len) { | |
| 1245 // Too large to consider | |
| 1246 return MEM_E_INVALID_SIZE; | |
| 1247 } | |
| 1248 | |
| 1249 if (file_len == 0) { | |
| 1250 buffer_out->clear(); | |
| 1251 return S_OK; | |
| 1252 } | |
| 1253 | |
| 1254 int old_size = buffer_out->size(); | |
| 1255 buffer_out->resize(old_size + file_len); | |
| 1256 | |
| 1257 uint32 bytes_read = 0; | |
| 1258 hr = file.ReadFromStartOfFile(file_len, | |
| 1259 &(*buffer_out)[old_size], | |
| 1260 &bytes_read); | |
| 1261 if (FAILED(hr)) { | |
| 1262 // I/O error of some kind | |
| 1263 return hr; | |
| 1264 } | |
| 1265 | |
| 1266 if (bytes_read != file_len) { | |
| 1267 // Unexpected length. This could happen when reading a file someone else | |
| 1268 // is writing to such as log files. | |
| 1269 ASSERT1(false); | |
| 1270 return E_UNEXPECTED; | |
| 1271 } | |
| 1272 | |
| 1273 // All's well that ends well | |
| 1274 return S_OK; | |
| 1275 } | |
| 1276 | |
| 1277 HRESULT WriteEntireFile(const TCHAR * filepath, | |
| 1278 const std::vector<byte>& buffer_in) { | |
| 1279 ASSERT1(filepath); | |
| 1280 | |
| 1281 // File::WriteAt doesn't implement clear-on-open-for-write semantics, | |
| 1282 // so just delete the file if it exists instead of writing into it. | |
| 1283 | |
| 1284 if (File::Exists(filepath)) { | |
| 1285 HRESULT hr = File::Remove(filepath); | |
| 1286 if (FAILED(hr)) { | |
| 1287 return hr; | |
| 1288 } | |
| 1289 } | |
| 1290 | |
| 1291 File file; | |
| 1292 HRESULT hr = file.Open(filepath, true, false); | |
| 1293 if (FAILED(hr)) { | |
| 1294 return hr; | |
| 1295 } | |
| 1296 | |
| 1297 ON_SCOPE_EXIT_OBJ(file, &File::Close); | |
| 1298 | |
| 1299 uint32 bytes_written = 0; | |
| 1300 hr = file.WriteAt(0, &buffer_in.front(), buffer_in.size(), 0, &bytes_written); | |
| 1301 if (FAILED(hr)) { | |
| 1302 return hr; | |
| 1303 } | |
| 1304 if (bytes_written != buffer_in.size()) { | |
| 1305 // This shouldn't happen, caller needs to investigate what's up. | |
| 1306 ASSERT1(false); | |
| 1307 return E_UNEXPECTED; | |
| 1308 } | |
| 1309 | |
| 1310 return S_OK; | |
| 1311 } | |
| 1312 | |
| 1313 // Conversions between a byte stream and a std::string | |
| 1314 HRESULT BufferToString(const std::vector<byte>& buffer_in, CStringA* str_out) { | |
| 1315 ASSERT1(str_out); | |
| 1316 str_out->Append(reinterpret_cast<const char*>(&buffer_in.front()), | |
| 1317 buffer_in.size()); | |
| 1318 return S_OK; | |
| 1319 } | |
| 1320 | |
| 1321 HRESULT StringToBuffer(const CStringA& str_in, std::vector<byte>* buffer_out) { | |
| 1322 ASSERT1(buffer_out); | |
| 1323 buffer_out->assign(str_in.GetString(), | |
| 1324 str_in.GetString() + str_in.GetLength()); | |
| 1325 return S_OK; | |
| 1326 } | |
| 1327 | |
| 1328 HRESULT BufferToString(const std::vector<byte>& buffer_in, CString* str_out) { | |
| 1329 ASSERT1(str_out); | |
| 1330 | |
| 1331 size_t len2 = buffer_in.size(); | |
| 1332 ASSERT1(len2 % 2 == 0); | |
| 1333 size_t len = len2 / 2; | |
| 1334 | |
| 1335 str_out->Append(reinterpret_cast<const TCHAR*>(&buffer_in.front()), len); | |
| 1336 | |
| 1337 return S_OK; | |
| 1338 } | |
| 1339 | |
| 1340 HRESULT StringToBuffer(const CString& str_in, std::vector<byte>* buffer_out) { | |
| 1341 ASSERT1(buffer_out); | |
| 1342 | |
| 1343 size_t len = str_in.GetLength(); | |
| 1344 size_t len2 = len * 2; | |
| 1345 | |
| 1346 buffer_out->resize(len2); | |
| 1347 ::memcpy(&buffer_out->front(), str_in.GetString(), len2); | |
| 1348 | |
| 1349 return S_OK; | |
| 1350 } | |
| 1351 | |
| 1352 HRESULT RegSplitKeyvalueName(const CString& keyvalue_name, | |
| 1353 CString* key_name, | |
| 1354 CString* value_name) { | |
| 1355 ASSERT1(key_name); | |
| 1356 ASSERT1(value_name); | |
| 1357 | |
| 1358 const TCHAR kDefault[] = _T("\\(default)"); | |
| 1359 | |
| 1360 if (String_EndsWith(keyvalue_name, _T("\\"), false)) { | |
| 1361 key_name->SetString(keyvalue_name, keyvalue_name.GetLength() - 1); | |
| 1362 value_name->Empty(); | |
| 1363 } else if (String_EndsWith(keyvalue_name, kDefault, true)) { | |
| 1364 key_name->SetString(keyvalue_name, | |
| 1365 keyvalue_name.GetLength() - TSTR_SIZE(kDefault)); | |
| 1366 value_name->Empty(); | |
| 1367 } else { | |
| 1368 int last_slash = String_ReverseFindChar(keyvalue_name, _T('\\')); | |
| 1369 if (last_slash == -1) { | |
| 1370 // No slash found - bizzare and wrong | |
| 1371 return E_FAIL; | |
| 1372 } | |
| 1373 key_name->SetString(keyvalue_name, last_slash); | |
| 1374 value_name->SetString(keyvalue_name.GetString() + last_slash + 1, | |
| 1375 keyvalue_name.GetLength() - last_slash - 1); | |
| 1376 } | |
| 1377 | |
| 1378 return S_OK; | |
| 1379 } | |
| 1380 | |
| 1381 HRESULT ExpandEnvLikeStrings(const TCHAR* src, | |
| 1382 const std::map<CString, CString>& keywords, | |
| 1383 CString* dest) { | |
| 1384 ASSERT1(src); | |
| 1385 ASSERT1(dest); | |
| 1386 | |
| 1387 const TCHAR kMarker = _T('%'); | |
| 1388 | |
| 1389 dest->Empty(); | |
| 1390 | |
| 1391 // Loop while finding the marker in the string | |
| 1392 HRESULT hr = S_OK; | |
| 1393 int pos = 0; | |
| 1394 int marker_pos1 = -1; | |
| 1395 while ((marker_pos1 = String_FindChar(src, kMarker, pos)) != -1) { | |
| 1396 // Try to find the right marker | |
| 1397 int marker_pos2 = -1; | |
| 1398 const TCHAR* s = src + marker_pos1 + 1; | |
| 1399 for (; *s; ++s) { | |
| 1400 if (*s == kMarker) { | |
| 1401 marker_pos2 = s - src; | |
| 1402 break; | |
| 1403 } | |
| 1404 if (!String_IsIdentifierChar(*s)) { | |
| 1405 break; | |
| 1406 } | |
| 1407 } | |
| 1408 if (marker_pos2 == -1) { | |
| 1409 // Unmatched marker found, skip | |
| 1410 dest->Append(src + pos, marker_pos1 - pos + 1); | |
| 1411 pos = marker_pos1 + 1; | |
| 1412 continue; | |
| 1413 } | |
| 1414 | |
| 1415 // Get the name - without the % markers on each end | |
| 1416 CString name(src + marker_pos1 + 1, marker_pos2 - marker_pos1 - 1); | |
| 1417 | |
| 1418 bool found = false; | |
| 1419 for (std::map<CString, CString>::const_iterator it(keywords.begin()); | |
| 1420 it != keywords.end(); | |
| 1421 ++it) { | |
| 1422 if (_tcsicmp(it->first, name) == 0) { | |
| 1423 dest->Append(src + pos, marker_pos1 - pos); | |
| 1424 dest->Append(it->second); | |
| 1425 found = true; | |
| 1426 break; | |
| 1427 } | |
| 1428 } | |
| 1429 if (!found) { | |
| 1430 // No mapping found | |
| 1431 UTIL_LOG(LE, (_T("[ExpandEnvLikeStrings]") | |
| 1432 _T("[no mapping found for '%s' in '%s']"), name, src)); | |
| 1433 dest->Append(src + pos, marker_pos2 - pos + 1); | |
| 1434 hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
| 1435 } | |
| 1436 | |
| 1437 pos = marker_pos2 + 1; | |
| 1438 } | |
| 1439 | |
| 1440 int len = _tcslen(src); | |
| 1441 if (pos < len) { | |
| 1442 dest->Append(src + pos, len - pos); | |
| 1443 } | |
| 1444 | |
| 1445 return hr; | |
| 1446 } | |
| 1447 | |
| 1448 bool IsRegistryPath(const TCHAR* path) { | |
| 1449 return String_StartsWith(path, _T("HKLM\\"), false) || | |
| 1450 String_StartsWith(path, _T("HKCU\\"), false) || | |
| 1451 String_StartsWith(path, _T("HKCR\\"), false) || | |
| 1452 String_StartsWith(path, _T("HKEY_LOCAL_MACHINE\\"), false) || | |
| 1453 String_StartsWith(path, _T("HKEY_CURRENT_USER\\"), false) || | |
| 1454 String_StartsWith(path, _T("HKEY_CLASSES_ROOT\\"), false); | |
| 1455 } | |
| 1456 | |
| 1457 bool IsUrl(const TCHAR* path) { | |
| 1458 // Currently we only check for "http://" and "https://" | |
| 1459 return String_StartsWith(path, kHttpProto, true) || | |
| 1460 String_StartsWith(path, kHttpsProto, true); | |
| 1461 } | |
| 1462 | |
| 1463 | |
| 1464 CString GuidToString(const GUID& guid) { | |
| 1465 TCHAR guid_str[40] = {0}; | |
| 1466 VERIFY1(::StringFromGUID2(guid, guid_str, arraysize(guid_str))); | |
| 1467 String_ToUpper(guid_str); | |
| 1468 return guid_str; | |
| 1469 } | |
| 1470 | |
| 1471 HRESULT StringToGuidSafe(const CString& str, GUID* guid) { | |
| 1472 ASSERT1(guid); | |
| 1473 TCHAR* s = const_cast<TCHAR*>(str.GetString()); | |
| 1474 return ::IIDFromString(s, guid); | |
| 1475 } | |
| 1476 | |
| 1477 // Helper function to convert a variant containing a list of strings | |
| 1478 void VariantToStringList(VARIANT var, std::vector<CString>* list) { | |
| 1479 ASSERT1(list); | |
| 1480 | |
| 1481 list->clear(); | |
| 1482 | |
| 1483 ASSERT1(V_VT(&var) == VT_DISPATCH); | |
| 1484 CComPtr<IDispatch> obj = V_DISPATCH(&var); | |
| 1485 ASSERT1(obj); | |
| 1486 | |
| 1487 CComVariant var_length; | |
| 1488 VERIFY1(SUCCEEDED(obj.GetPropertyByName(_T("length"), &var_length))); | |
| 1489 ASSERT1(V_VT(&var_length) == VT_I4); | |
| 1490 int length = V_I4(&var_length); | |
| 1491 | |
| 1492 for (int i = 0; i < length; ++i) { | |
| 1493 CComVariant value; | |
| 1494 VERIFY1(SUCCEEDED(obj.GetPropertyByName(itostr(i), &value))); | |
| 1495 if (V_VT(&value) == VT_BSTR) { | |
| 1496 list->push_back(V_BSTR(&value)); | |
| 1497 } else { | |
| 1498 ASSERT1(false); | |
| 1499 } | |
| 1500 } | |
| 1501 } | |
| 1502 | |
| 1503 | |
| 1504 HRESULT GetCurrentProcessHandle(HANDLE* handle) { | |
| 1505 ASSERT1(handle); | |
| 1506 scoped_process real_handle; | |
| 1507 HANDLE pseudo_handle = ::GetCurrentProcess(); | |
| 1508 bool res = ::DuplicateHandle( | |
| 1509 pseudo_handle, // this process pseudo-handle | |
| 1510 pseudo_handle, // handle to duplicate | |
| 1511 pseudo_handle, // the process receiving the handle | |
| 1512 address(real_handle), // this process real handle | |
| 1513 0, // ignored | |
| 1514 false, // don't inherit this handle | |
| 1515 DUPLICATE_SAME_ACCESS) != 0; | |
| 1516 | |
| 1517 *handle = NULL; | |
| 1518 if (!res) { | |
| 1519 return HRESULTFromLastError(); | |
| 1520 } | |
| 1521 *handle = release(real_handle); | |
| 1522 return S_OK; | |
| 1523 } | |
| 1524 | |
| 1525 HRESULT DuplicateTokenIntoCurrentProcess(HANDLE source_process, | |
| 1526 HANDLE token_to_duplicate, | |
| 1527 CAccessToken* duplicated_token) { | |
| 1528 ASSERT1(source_process); | |
| 1529 ASSERT1(token_to_duplicate); | |
| 1530 ASSERT1(duplicated_token); | |
| 1531 | |
| 1532 scoped_handle alt_token; | |
| 1533 bool res = ::DuplicateHandle( | |
| 1534 source_process, // Process whose handle needs duplicating. | |
| 1535 token_to_duplicate, // Handle to duplicate. | |
| 1536 ::GetCurrentProcess(), // Current process receives the handle. | |
| 1537 address(alt_token), // Duplicated handle. | |
| 1538 TOKEN_ALL_ACCESS, // Access requested for the new handle. | |
| 1539 false, // Do not inherit the new handle. | |
| 1540 DUPLICATE_SAME_ACCESS) != 0; // Same access as token_to_duplicate. | |
| 1541 | |
| 1542 if (!res) { | |
| 1543 HRESULT hr = HRESULTFromLastError(); | |
| 1544 CORE_LOG(LE, (_T("[DuplicateTokenIntoCurrentProcess failed][0x%x]"), hr)); | |
| 1545 return hr; | |
| 1546 } | |
| 1547 | |
| 1548 duplicated_token->Attach(release(alt_token)); | |
| 1549 return S_OK; | |
| 1550 } | |
| 1551 | |
| 1552 | |
| 1553 // get a time64 value | |
| 1554 // NOTE: If the value is greater than the | |
| 1555 // max value, then SetValue will be called using the max_value. | |
| 1556 HRESULT GetLimitedTimeValue(const TCHAR* full_key_name, const TCHAR* value_name, | |
| 1557 time64 max_time, time64* value, | |
| 1558 bool* limited_value) { | |
| 1559 ASSERT1(full_key_name); | |
| 1560 ASSERT1(value); | |
| 1561 STATIC_ASSERT(sizeof(time64) == sizeof(DWORD64)); | |
| 1562 | |
| 1563 if (limited_value) { | |
| 1564 *limited_value = false; | |
| 1565 } | |
| 1566 HRESULT hr = RegKey::GetValue(full_key_name, value_name, value); | |
| 1567 if (SUCCEEDED(hr) && *value > max_time) { | |
| 1568 *value = max_time; | |
| 1569 | |
| 1570 // Use a different hr for the setting of the value b/c | |
| 1571 // the returned hr should reflect the success/failure of reading the key | |
| 1572 HRESULT set_value_hr = RegKey::SetValue(full_key_name, value_name, *value); | |
| 1573 ASSERT(SUCCEEDED(set_value_hr), | |
| 1574 (_T("GetLimitedTimeValue - failed when setting a value: 0x%08x]"), | |
| 1575 set_value_hr)); | |
| 1576 if (SUCCEEDED(set_value_hr) && limited_value) { | |
| 1577 *limited_value = true; | |
| 1578 } | |
| 1579 } | |
| 1580 return hr; | |
| 1581 } | |
| 1582 | |
| 1583 // get a time64 value trying reg keys successively if there is a | |
| 1584 // failure in getting a value. | |
| 1585 HRESULT GetLimitedTimeValues(const TCHAR* full_key_names[], | |
| 1586 int key_names_length, | |
| 1587 const TCHAR* value_name, | |
| 1588 time64 max_time, | |
| 1589 time64* value, | |
| 1590 bool* limited_value) { | |
| 1591 ASSERT1(full_key_names); | |
| 1592 ASSERT1(value); | |
| 1593 ASSERT1(key_names_length > 0); | |
| 1594 | |
| 1595 HRESULT hr = E_FAIL; | |
| 1596 for (int i = 0; i < key_names_length; ++i) { | |
| 1597 hr = GetLimitedTimeValue(full_key_names[i], value_name, max_time, value, | |
| 1598 limited_value); | |
| 1599 if (SUCCEEDED(hr)) { | |
| 1600 return hr; | |
| 1601 } | |
| 1602 } | |
| 1603 return hr; | |
| 1604 } | |
| 1605 | |
| 1606 // Wininet.dll (and especially the version that comes with IE7, with 01/12/07 | |
| 1607 // timestamp) incorrectly initializes Rasman.dll. As a result, there is a race | |
| 1608 // condition that causes double-free on a memory from process heap. | |
| 1609 // This causes memory corruption in the heap that may later produce a variety | |
| 1610 // of ill effects, most frequently a crash with a callstack that contains | |
| 1611 // wininet and rasman, or ntdll!RtlAllocHeap. The root cause is that | |
| 1612 // Rasapi32!LoadRasmanDllAndInit is not thread safe and can start very involved | |
| 1613 // process of initialization on 2 threads at the same time. It's a bug. | |
| 1614 // Solution: in the begining of the program, trigger synchronous load of | |
| 1615 // rasman dll. The easy way is to call a public ras api that does synchronous | |
| 1616 // initialization, which is what we do here. | |
| 1617 void EnsureRasmanLoaded() { | |
| 1618 RASENTRYNAME ras_entry_name = {0}; | |
| 1619 DWORD size_bytes = sizeof(ras_entry_name); | |
| 1620 DWORD number_of_entries = 0; | |
| 1621 ras_entry_name.dwSize = size_bytes; | |
| 1622 // we don't really need results of this method, | |
| 1623 // it simply triggers RASAPI32!LoadRasmanDllAndInit() internally. | |
| 1624 ::RasEnumEntries(NULL, | |
| 1625 NULL, | |
| 1626 &ras_entry_name, | |
| 1627 &size_bytes, | |
| 1628 &number_of_entries); | |
| 1629 } | |
| 1630 | |
| 1631 // Appends two reg keys. Handles the situation where there are traling | |
| 1632 // back slashes in one and leading back slashes in two. | |
| 1633 CString AppendRegKeyPath(const CString& one, const CString& two) { | |
| 1634 CString leftpart(one); | |
| 1635 int length = leftpart.GetLength(); | |
| 1636 int i = 0; | |
| 1637 for (i = length - 1; i >= 0; --i) { | |
| 1638 if (leftpart[i] != _T('\\')) { | |
| 1639 break; | |
| 1640 } | |
| 1641 } | |
| 1642 leftpart = leftpart.Left(i+1); | |
| 1643 | |
| 1644 CString rightpart(two); | |
| 1645 int lengthr = rightpart.GetLength(); | |
| 1646 for (i = 0; i < lengthr; ++i) { | |
| 1647 if (rightpart[i] != _T('\\')) { | |
| 1648 break; | |
| 1649 } | |
| 1650 } | |
| 1651 rightpart = rightpart.Right(lengthr - i); | |
| 1652 | |
| 1653 CString result; | |
| 1654 SafeCStringFormat(&result, _T("%s\\%s"), leftpart, rightpart); | |
| 1655 return result; | |
| 1656 } | |
| 1657 | |
| 1658 CString AppendRegKeyPath(const CString& one, const CString& two, | |
| 1659 const CString& three) { | |
| 1660 CString result = AppendRegKeyPath(one, two); | |
| 1661 result = AppendRegKeyPath(result, three); | |
| 1662 return result; | |
| 1663 } | |
| 1664 | |
| 1665 | |
| 1666 HRESULT GetUserKeysFromHkeyUsers(std::vector<CString>* key_names) { | |
| 1667 ASSERT1(key_names); | |
| 1668 CORE_LOG(L3, (_T("[GetUserKeysFromHkeyUsers]"))); | |
| 1669 | |
| 1670 TCHAR user_key_name[MAX_PATH] = {0}; | |
| 1671 int i = 0; | |
| 1672 while (::RegEnumKey(HKEY_USERS, i++, user_key_name, MAX_PATH) != | |
| 1673 ERROR_NO_MORE_ITEMS) { | |
| 1674 byte sid_buffer[SECURITY_MAX_SID_SIZE] = {0}; | |
| 1675 PSID sid = reinterpret_cast<PSID>(sid_buffer); | |
| 1676 if (::ConvertStringSidToSid(user_key_name, &sid) != 0) { | |
| 1677 // We could convert the string SID into a real SID. If not | |
| 1678 // we just ignore. | |
| 1679 DWORD size = MAX_PATH; | |
| 1680 DWORD size_domain = MAX_PATH; | |
| 1681 SID_NAME_USE sid_type = SidTypeComputer; | |
| 1682 TCHAR user_name[MAX_PATH] = {0}; | |
| 1683 TCHAR domain_name[MAX_PATH] = {0}; | |
| 1684 | |
| 1685 if (::LookupAccountSid(NULL, sid, user_name, &size, | |
| 1686 domain_name, &size_domain, &sid_type) == 0) { | |
| 1687 HRESULT hr = HRESULTFromLastError(); | |
| 1688 CORE_LOG(LW, (_T("[GetUserKeysFromHkeyUsers LookupAccountSid failed]") | |
| 1689 _T("[0x%08x]"), hr)); | |
| 1690 continue; | |
| 1691 } | |
| 1692 | |
| 1693 if (sid_type == SidTypeUser) { | |
| 1694 // Change the RunAs keys for the user goopdates to point to the | |
| 1695 // machine install. | |
| 1696 CString user_reg_key_name = AppendRegKeyPath(USERS_KEY, user_key_name); | |
| 1697 key_names->push_back(user_reg_key_name); | |
| 1698 } | |
| 1699 } | |
| 1700 } | |
| 1701 | |
| 1702 return S_OK; | |
| 1703 } | |
| 1704 | |
| 1705 HRESULT IsSystemProcess(bool* is_system_process) { | |
| 1706 CAccessToken current_process_token; | |
| 1707 if (!current_process_token.GetProcessToken(TOKEN_QUERY, | |
| 1708 ::GetCurrentProcess())) { | |
| 1709 HRESULT hr = HRESULTFromLastError(); | |
| 1710 ASSERT(false, (_T("CAccessToken::GetProcessToken failed: 0x%08x"), hr)); | |
| 1711 return hr; | |
| 1712 } | |
| 1713 CSid logon_sid; | |
| 1714 if (!current_process_token.GetUser(&logon_sid)) { | |
| 1715 HRESULT hr = HRESULTFromLastError(); | |
| 1716 ASSERT(false, (_T("CAccessToken::GetUser failed: 0x%08x"), hr)); | |
| 1717 return hr; | |
| 1718 } | |
| 1719 *is_system_process = logon_sid == Sids::System(); | |
| 1720 return S_OK; | |
| 1721 } | |
| 1722 | |
| 1723 HRESULT IsUserLoggedOn(bool* is_logged_on) { | |
| 1724 ASSERT1(is_logged_on); | |
| 1725 bool is_local_system(false); | |
| 1726 HRESULT hr = IsSystemProcess(&is_local_system); | |
| 1727 if (SUCCEEDED(hr) && is_local_system) { | |
| 1728 *is_logged_on = true; | |
| 1729 return S_OK; | |
| 1730 } | |
| 1731 return UserRights::UserIsLoggedOnInteractively(is_logged_on); | |
| 1732 } | |
| 1733 | |
| 1734 bool IsClickOnceDisabled() { | |
| 1735 CComPtr<IInternetZoneManager> zone_mgr; | |
| 1736 HRESULT hr = zone_mgr.CoCreateInstance(CLSID_InternetZoneManager); | |
| 1737 if (FAILED(hr)) { | |
| 1738 UTIL_LOG(LE, (_T("[InternetZoneManager CreateInstance fail][0x%08x]"), hr)); | |
| 1739 return true; | |
| 1740 } | |
| 1741 | |
| 1742 DWORD policy = URLPOLICY_DISALLOW; | |
| 1743 size_t policy_size = sizeof(policy); | |
| 1744 hr = zone_mgr->GetZoneActionPolicy(URLZONE_INTERNET, | |
| 1745 URLACTION_MANAGED_UNSIGNED, | |
| 1746 reinterpret_cast<BYTE*>(&policy), | |
| 1747 policy_size, | |
| 1748 URLZONEREG_DEFAULT); | |
| 1749 if (FAILED(hr)) { | |
| 1750 UTIL_LOG(LE, (_T("[GetZoneActionPolicy failed][0x%08x]"), hr)); | |
| 1751 return true; | |
| 1752 } | |
| 1753 | |
| 1754 return policy == URLPOLICY_DISALLOW; | |
| 1755 } | |
| 1756 | |
| 1757 // This function only uses kernel32, and it is safe to call from DllMain. | |
| 1758 HRESULT PinModuleIntoProcess(const CString& module_name) { | |
| 1759 ASSERT1(!module_name.IsEmpty()); | |
| 1760 static HMODULE module_handle = NULL; | |
| 1761 typedef BOOL (WINAPI *Fun)(DWORD flags, | |
| 1762 LPCWSTR module_name, | |
| 1763 HMODULE* module_handle); | |
| 1764 | |
| 1765 HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); | |
| 1766 ASSERT1(kernel_instance); | |
| 1767 Fun pfn = NULL; | |
| 1768 if (GPA(kernel_instance, "GetModuleHandleExW", &pfn)) { | |
| 1769 if ((*pfn)(GET_MODULE_HANDLE_EX_FLAG_PIN, module_name, &module_handle)) { | |
| 1770 return S_OK; | |
| 1771 } | |
| 1772 ASSERT(false, (_T("GetModuleHandleExW() failed: %d"), ::GetLastError())); | |
| 1773 } | |
| 1774 | |
| 1775 module_handle = ::LoadLibrary(module_name); | |
| 1776 ASSERT(NULL != module_handle, (_T("LoadLibrary fail: %d"), ::GetLastError())); | |
| 1777 if (NULL == module_handle) { | |
| 1778 return HRESULTFromLastError(); | |
| 1779 } | |
| 1780 | |
| 1781 return S_OK; | |
| 1782 } | |
| 1783 | |
| 1784 bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info) { | |
| 1785 UTIL_LOG(L3, (_T("[ShellExecuteExEnsureParent]"))); | |
| 1786 | |
| 1787 ASSERT1(shell_exec_info); | |
| 1788 bool shell_exec_succeeded(false); | |
| 1789 DWORD last_error(ERROR_SUCCESS); | |
| 1790 | |
| 1791 { | |
| 1792 // hwnd_parent window is destroyed at the end of the scope when the | |
| 1793 // destructor of scoped_window calls ::DestroyWindow. | |
| 1794 scoped_window hwnd_parent; | |
| 1795 | |
| 1796 if (!shell_exec_info->hwnd && vista_util::IsVistaOrLater()) { | |
| 1797 reset(hwnd_parent, CreateForegroundParentWindowForUAC()); | |
| 1798 | |
| 1799 if (!hwnd_parent) { | |
| 1800 last_error = ::GetLastError(); | |
| 1801 UTIL_LOG(LE, (_T("[CreateDummyOverlappedWindow failed]"))); | |
| 1802 // Restore last error in case the logging reset it. | |
| 1803 ::SetLastError(last_error); | |
| 1804 return false; | |
| 1805 } | |
| 1806 | |
| 1807 shell_exec_info->hwnd = get(hwnd_parent); | |
| 1808 | |
| 1809 // If elevation is required on Vista, call ::SetForegroundWindow(). This | |
| 1810 // will make sure that the elevation prompt, as well as the elevated | |
| 1811 // process window comes up in the foreground. It will also ensure that in | |
| 1812 // the case where the elevation prompt is cancelled, the error dialog | |
| 1813 // shown from this process comes up in the foreground. | |
| 1814 if (shell_exec_info->lpVerb && | |
| 1815 _tcsicmp(shell_exec_info->lpVerb, _T("runas")) == 0) { | |
| 1816 if (!::SetForegroundWindow(get(hwnd_parent))) { | |
| 1817 UTIL_LOG(LW, (_T("[SetForegroundWindow failed][%d]"), | |
| 1818 ::GetLastError())); | |
| 1819 } | |
| 1820 } | |
| 1821 } | |
| 1822 | |
| 1823 shell_exec_succeeded = !!::ShellExecuteEx(shell_exec_info); | |
| 1824 | |
| 1825 if (shell_exec_succeeded) { | |
| 1826 if (shell_exec_info->hProcess) { | |
| 1827 DWORD pid = Process::GetProcessIdFromHandle(shell_exec_info->hProcess); | |
| 1828 OPT_LOG(L1, (_T("[Started process][%u]"), pid)); | |
| 1829 if (!::AllowSetForegroundWindow(pid)) { | |
| 1830 UTIL_LOG(LW, (_T("[AllowSetForegroundWindow failed][%d]"), | |
| 1831 ::GetLastError())); | |
| 1832 } | |
| 1833 } else { | |
| 1834 OPT_LOG(L1, (_T("[Started process][PID unknown]"))); | |
| 1835 } | |
| 1836 } else { | |
| 1837 last_error = ::GetLastError(); | |
| 1838 UTIL_LOG(LE, (_T("[ShellExecuteEx failed][%s][%s][0x%08x]"), | |
| 1839 shell_exec_info->lpFile, shell_exec_info->lpParameters, | |
| 1840 last_error)); | |
| 1841 } | |
| 1842 } | |
| 1843 | |
| 1844 // The implicit ::DestroyWindow call from the scoped_window could have reset | |
| 1845 // the last error, so restore it. | |
| 1846 ::SetLastError(last_error); | |
| 1847 | |
| 1848 return shell_exec_succeeded; | |
| 1849 } | |
| 1850 | |
| 1851 // Loads and unloads advapi32.dll for every call. If performance is an issue | |
| 1852 // consider keeping the dll always loaded and holding the pointer to the | |
| 1853 // RtlGenRandom in a static variable. | |
| 1854 // Use the function with care. While the function is documented, it may be | |
| 1855 // altered or made unavailable in future versions of the operating system. | |
| 1856 bool GenRandom(void* buffer, size_t buffer_length) { | |
| 1857 ASSERT1(buffer); | |
| 1858 scoped_library lib(::LoadLibrary(_T("ADVAPI32.DLL"))); | |
| 1859 if (lib) { | |
| 1860 typedef BOOLEAN (APIENTRY *RtlGenRandomType)(void*, ULONG); | |
| 1861 RtlGenRandomType rtl_gen_random = reinterpret_cast<RtlGenRandomType>( | |
| 1862 ::GetProcAddress(get(lib), "SystemFunction036")); | |
| 1863 return rtl_gen_random && rtl_gen_random(buffer, buffer_length); | |
| 1864 } | |
| 1865 | |
| 1866 // Use CAPI to generate randomness for systems which do not support | |
| 1867 // RtlGenRandomType, for instance Windows 2000. | |
| 1868 const uint32 kCspFlags = CRYPT_VERIFYCONTEXT | CRYPT_SILENT; | |
| 1869 HCRYPTPROV csp = NULL; | |
| 1870 if (::CryptAcquireContext(&csp, NULL, NULL, PROV_RSA_FULL, kCspFlags)) { | |
| 1871 if (::CryptGenRandom(csp, buffer_length, static_cast<BYTE*>(buffer))) { | |
| 1872 return true; | |
| 1873 } | |
| 1874 } | |
| 1875 VERIFY1(::CryptReleaseContext(csp, 0)); | |
| 1876 return false; | |
| 1877 } | |
| 1878 | |
| 1879 // Assumes the path in command is properly enclosed if necessary. | |
| 1880 HRESULT ConfigureRunAtStartup(const CString& root_key_name, | |
| 1881 const CString& run_value_name, | |
| 1882 const CString& command, | |
| 1883 bool install) { | |
| 1884 UTIL_LOG(L3, (_T("[ConfigureRunAtStartup]"))); | |
| 1885 | |
| 1886 const CString key_path = AppendRegKeyPath(root_key_name, REGSTR_PATH_RUN); | |
| 1887 HRESULT hr(S_OK); | |
| 1888 | |
| 1889 if (install) { | |
| 1890 hr = RegKey::SetValue(key_path, run_value_name, command); | |
| 1891 } else { | |
| 1892 hr = RegKey::DeleteValue(key_path, run_value_name); | |
| 1893 } | |
| 1894 | |
| 1895 return hr; | |
| 1896 } | |
| 1897 | |
| 1898 HRESULT GetExePathFromCommandLine(const TCHAR* command_line, | |
| 1899 CString* exe_path) { | |
| 1900 ASSERT1(exe_path); | |
| 1901 CString command_line_str(command_line); | |
| 1902 command_line_str.Trim(_T(' ')); | |
| 1903 if (command_line_str.IsEmpty()) { | |
| 1904 // ::CommandLineToArgvW parses the current process command line for blank | |
| 1905 // strings. We do not want this behavior. | |
| 1906 return E_INVALIDARG; | |
| 1907 } | |
| 1908 | |
| 1909 int argc = 0; | |
| 1910 wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc); | |
| 1911 if (argc == 0 || !argv) { | |
| 1912 HRESULT hr = HRESULTFromLastError(); | |
| 1913 UTIL_LOG(LE, (_T("[::CommandLineToArgvW failed][0x%08x]"), hr)); | |
| 1914 return hr; | |
| 1915 } | |
| 1916 | |
| 1917 *exe_path = argv[0]; | |
| 1918 ::LocalFree(argv); | |
| 1919 exe_path->Trim(_T(' ')); | |
| 1920 ASSERT1(!exe_path->IsEmpty()); | |
| 1921 return S_OK; | |
| 1922 } | |
| 1923 | |
| 1924 // Tries to open the _MSIExecute mutex and tests its state. MSI sets the | |
| 1925 // mutex when processing sequence tables. This indicates MSI is busy. | |
| 1926 // The function returns S_OK if the mutex is not owned by MSI or the mutex has | |
| 1927 // not been created. | |
| 1928 HRESULT WaitForMSIExecute(int timeout_ms) { | |
| 1929 const TCHAR* mutex_name = _T("Global\\_MSIExecute"); | |
| 1930 scoped_mutex mutex(::OpenMutex(SYNCHRONIZE, false, mutex_name)); | |
| 1931 if (!mutex) { | |
| 1932 DWORD error = ::GetLastError(); | |
| 1933 return (error == ERROR_FILE_NOT_FOUND) ? S_OK : HRESULT_FROM_WIN32(error); | |
| 1934 } | |
| 1935 UTIL_LOG(L3, (_T("[Wait for _MSIExecute]"))); | |
| 1936 switch (::WaitForSingleObject(get(mutex), timeout_ms)) { | |
| 1937 case WAIT_OBJECT_0: | |
| 1938 case WAIT_ABANDONED: | |
| 1939 VERIFY1(::ReleaseMutex(get(mutex))); | |
| 1940 return S_OK; | |
| 1941 case WAIT_TIMEOUT: | |
| 1942 return HRESULT_FROM_WIN32(ERROR_TIMEOUT); | |
| 1943 case WAIT_FAILED: | |
| 1944 return HRESULTFromLastError(); | |
| 1945 default: | |
| 1946 return E_FAIL; | |
| 1947 } | |
| 1948 } | |
| 1949 | |
| 1950 CString GetEnvironmentVariableAsString(const TCHAR* name) { | |
| 1951 CString value; | |
| 1952 size_t value_length = ::GetEnvironmentVariable(name, NULL, 0); | |
| 1953 if (value_length) { | |
| 1954 VERIFY1(::GetEnvironmentVariable(name, | |
| 1955 CStrBuf(value, value_length), | |
| 1956 value_length)); | |
| 1957 } | |
| 1958 return value; | |
| 1959 } | |
| 1960 | |
| 1961 // States are documented at | |
| 1962 // http://technet.microsoft.com/en-us/library/cc721913.aspx. | |
| 1963 bool IsWindowsInstalling() { | |
| 1964 static const TCHAR kVistaSetupStateKey[] = | |
| 1965 _T("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"); | |
| 1966 static const TCHAR kImageStateValueName[] = _T("ImageState"); | |
| 1967 static const TCHAR kImageStateUnuseableValue[] = | |
| 1968 _T("IMAGE_STATE_UNDEPLOYABLE"); | |
| 1969 static const TCHAR kImageStateGeneralAuditValue[] = | |
| 1970 _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT"); | |
| 1971 static const TCHAR kImageStateSpecialAuditValue[] = | |
| 1972 _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT"); | |
| 1973 | |
| 1974 static const TCHAR kXPSetupStateKey[] = _T("System\\Setup"); | |
| 1975 static const TCHAR kAuditFlagValueName[] = _T("AuditInProgress"); | |
| 1976 | |
| 1977 if (vista_util::IsVistaOrLater()) { | |
| 1978 RegKey vista_setup_key; | |
| 1979 HRESULT hr = | |
| 1980 vista_setup_key.Open(HKEY_LOCAL_MACHINE, kVistaSetupStateKey, KEY_READ); | |
| 1981 if (SUCCEEDED(hr)) { | |
| 1982 CString state; | |
| 1983 hr = vista_setup_key.GetValue(kImageStateValueName, &state); | |
| 1984 if (SUCCEEDED(hr) && | |
| 1985 !state.IsEmpty() && | |
| 1986 (0 == state.CompareNoCase(kImageStateUnuseableValue) || | |
| 1987 0 == state.CompareNoCase(kImageStateGeneralAuditValue) || | |
| 1988 0 == state.CompareNoCase(kImageStateSpecialAuditValue))) | |
| 1989 return true; // Vista is still installing. | |
| 1990 } | |
| 1991 } else { | |
| 1992 RegKey xp_setup_key; | |
| 1993 HRESULT hr = | |
| 1994 xp_setup_key.Open(HKEY_LOCAL_MACHINE, kXPSetupStateKey, KEY_READ); | |
| 1995 if (SUCCEEDED(hr)) { | |
| 1996 DWORD audit_flag(0); | |
| 1997 hr = xp_setup_key.GetValue(kAuditFlagValueName, &audit_flag); | |
| 1998 if (SUCCEEDED(hr) && 0 != audit_flag) | |
| 1999 return true; // XP is still installing. | |
| 2000 } | |
| 2001 } | |
| 2002 return false; | |
| 2003 } | |
| 2004 | |
| 2005 HRESULT GetGuid(CString* guid) { | |
| 2006 GUID guid_local = {0}; | |
| 2007 HRESULT hr = ::CoCreateGuid(&guid_local); | |
| 2008 if (FAILED(hr)) { | |
| 2009 return hr; | |
| 2010 } | |
| 2011 *guid = GuidToString(guid_local); | |
| 2012 return S_OK; | |
| 2013 } | |
| 2014 | |
| 2015 CString GetMessageForSystemErrorCode(DWORD error_code) { | |
| 2016 CORE_LOG(L3, (_T("[GetMessageForSystemErrorCode][%u]"), error_code)); | |
| 2017 | |
| 2018 TCHAR* system_allocated_buffer = NULL; | |
| 2019 const DWORD kFormatOptions = FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
| 2020 FORMAT_MESSAGE_FROM_SYSTEM | | |
| 2021 FORMAT_MESSAGE_IGNORE_INSERTS | | |
| 2022 FORMAT_MESSAGE_MAX_WIDTH_MASK; | |
| 2023 DWORD tchars_written = ::FormatMessage( | |
| 2024 kFormatOptions, | |
| 2025 NULL, | |
| 2026 error_code, | |
| 2027 0, | |
| 2028 reinterpret_cast<LPWSTR>(&system_allocated_buffer), | |
| 2029 0, | |
| 2030 NULL); | |
| 2031 | |
| 2032 CString message; | |
| 2033 if (tchars_written > 0) { | |
| 2034 message = system_allocated_buffer; | |
| 2035 } else { | |
| 2036 UTIL_LOG(LW, (_T("[::FormatMessage failed][%u]"), ::GetLastError())); | |
| 2037 } | |
| 2038 | |
| 2039 VERIFY1(!::LocalFree(system_allocated_buffer)); | |
| 2040 | |
| 2041 return message; | |
| 2042 } | |
| 2043 | |
| 2044 } // namespace omaha | |
| OLD | NEW |