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 |