Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chrome / installer / setup / setup_util.cc
blobaf474401c9709d9dbd29faca40c870ee59937a4a
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This file declares util functions for setup project.
7 #include "chrome/installer/setup/setup_util.h"
9 #include <windows.h>
11 #include <algorithm>
12 #include <iterator>
13 #include <set>
15 #include "base/command_line.h"
16 #include "base/cpu.h"
17 #include "base/files/file_enumerator.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/logging.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/process/kill.h"
23 #include "base/process/launch.h"
24 #include "base/process/process_handle.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/version.h"
28 #include "base/win/registry.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/installer/setup/setup_constants.h"
31 #include "chrome/installer/util/app_registration_data.h"
32 #include "chrome/installer/util/copy_tree_work_item.h"
33 #include "chrome/installer/util/google_update_constants.h"
34 #include "chrome/installer/util/installation_state.h"
35 #include "chrome/installer/util/installer_state.h"
36 #include "chrome/installer/util/master_preferences.h"
37 #include "chrome/installer/util/util_constants.h"
38 #include "chrome/installer/util/work_item.h"
39 #include "courgette/courgette.h"
40 #include "courgette/third_party/bsdiff.h"
41 #include "third_party/bspatch/mbspatch.h"
43 namespace installer {
45 namespace {
47 // Launches |setup_exe| with |command_line|, save --install-archive and its
48 // value if present. Returns false if the process failed to launch. Otherwise,
49 // waits indefinitely for it to exit and populates |exit_code| as expected. On
50 // the off chance that waiting itself fails, |exit_code| is set to
51 // WAIT_FOR_EXISTING_FAILED.
52 bool LaunchAndWaitForExistingInstall(const base::FilePath& setup_exe,
53 const base::CommandLine& command_line,
54 int* exit_code) {
55 DCHECK(exit_code);
56 base::CommandLine new_cl(setup_exe);
58 // Copy over all switches but --install-archive.
59 base::CommandLine::SwitchMap switches(command_line.GetSwitches());
60 switches.erase(switches::kInstallArchive);
61 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
62 i != switches.end(); ++i) {
63 if (i->second.empty())
64 new_cl.AppendSwitch(i->first);
65 else
66 new_cl.AppendSwitchNative(i->first, i->second);
69 // Copy over all arguments.
70 base::CommandLine::StringVector args(command_line.GetArgs());
71 for (base::CommandLine::StringVector::const_iterator i = args.begin();
72 i != args.end(); ++i) {
73 new_cl.AppendArgNative(*i);
76 // Launch the process and wait for it to exit.
77 VLOG(1) << "Launching existing installer with command: "
78 << new_cl.GetCommandLineString();
79 base::Process process = base::LaunchProcess(new_cl, base::LaunchOptions());
80 if (!process.IsValid()) {
81 PLOG(ERROR) << "Failed to launch existing installer with command: "
82 << new_cl.GetCommandLineString();
83 return false;
85 if (!process.WaitForExit(exit_code)) {
86 PLOG(DFATAL) << "Failed to get exit code from existing installer";
87 *exit_code = WAIT_FOR_EXISTING_FAILED;
88 } else {
89 VLOG(1) << "Existing installer returned exit code " << *exit_code;
91 return true;
94 // Returns true if product |type| cam be meaningfully installed without the
95 // --multi-install flag.
96 bool SupportsSingleInstall(BrowserDistribution::Type type) {
97 return (type == BrowserDistribution::CHROME_BROWSER ||
98 type == BrowserDistribution::CHROME_FRAME);
101 } // namespace
103 int CourgettePatchFiles(const base::FilePath& src,
104 const base::FilePath& patch,
105 const base::FilePath& dest) {
106 VLOG(1) << "Applying Courgette patch " << patch.value()
107 << " to file " << src.value()
108 << " and generating file " << dest.value();
110 if (src.empty() || patch.empty() || dest.empty())
111 return installer::PATCH_INVALID_ARGUMENTS;
113 const courgette::Status patch_status =
114 courgette::ApplyEnsemblePatch(src.value().c_str(),
115 patch.value().c_str(),
116 dest.value().c_str());
117 const int exit_code = (patch_status != courgette::C_OK) ?
118 static_cast<int>(patch_status) + kCourgetteErrorOffset : 0;
120 LOG_IF(ERROR, exit_code)
121 << "Failed to apply Courgette patch " << patch.value()
122 << " to file " << src.value() << " and generating file " << dest.value()
123 << ". err=" << exit_code;
125 return exit_code;
128 int BsdiffPatchFiles(const base::FilePath& src,
129 const base::FilePath& patch,
130 const base::FilePath& dest) {
131 VLOG(1) << "Applying bsdiff patch " << patch.value()
132 << " to file " << src.value()
133 << " and generating file " << dest.value();
135 if (src.empty() || patch.empty() || dest.empty())
136 return installer::PATCH_INVALID_ARGUMENTS;
138 const int patch_status = courgette::ApplyBinaryPatch(src, patch, dest);
139 const int exit_code = patch_status != OK ?
140 patch_status + kBsdiffErrorOffset : 0;
142 LOG_IF(ERROR, exit_code)
143 << "Failed to apply bsdiff patch " << patch.value()
144 << " to file " << src.value() << " and generating file " << dest.value()
145 << ". err=" << exit_code;
147 return exit_code;
150 Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path) {
151 VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value();
152 base::FileEnumerator version_enum(chrome_path, false,
153 base::FileEnumerator::DIRECTORIES);
154 // TODO(tommi): The version directory really should match the version of
155 // setup.exe. To begin with, we should at least DCHECK that that's true.
157 scoped_ptr<Version> max_version(new Version("0.0.0.0"));
158 bool version_found = false;
160 while (!version_enum.Next().empty()) {
161 base::FileEnumerator::FileInfo find_data = version_enum.GetInfo();
162 VLOG(1) << "directory found: " << find_data.GetName().value();
164 scoped_ptr<Version> found_version(
165 new Version(base::UTF16ToASCII(find_data.GetName().value())));
166 if (found_version->IsValid() &&
167 found_version->CompareTo(*max_version.get()) > 0) {
168 max_version.reset(found_version.release());
169 version_found = true;
173 return (version_found ? max_version.release() : NULL);
176 base::FilePath FindArchiveToPatch(const InstallationState& original_state,
177 const InstallerState& installer_state,
178 const base::Version& desired_version) {
179 if (desired_version.IsValid()) {
180 base::FilePath archive(installer_state.GetInstallerDirectory(
181 desired_version).Append(kChromeArchive));
182 return base::PathExists(archive) ? archive : base::FilePath();
185 // Check based on the version number advertised to Google Update, since that
186 // is the value used to select a specific differential update. If an archive
187 // can't be found using that, fallback to using the newest version present.
188 base::FilePath patch_source;
189 const ProductState* product =
190 original_state.GetProductState(installer_state.system_install(),
191 installer_state.state_type());
192 if (product) {
193 patch_source = installer_state.GetInstallerDirectory(product->version())
194 .Append(installer::kChromeArchive);
195 if (base::PathExists(patch_source))
196 return patch_source;
198 scoped_ptr<Version> version(
199 installer::GetMaxVersionFromArchiveDir(installer_state.target_path()));
200 if (version) {
201 patch_source = installer_state.GetInstallerDirectory(*version)
202 .Append(installer::kChromeArchive);
203 if (base::PathExists(patch_source))
204 return patch_source;
206 return base::FilePath();
209 bool DeleteFileFromTempProcess(const base::FilePath& path,
210 uint32 delay_before_delete_ms) {
211 static const wchar_t kRunDll32Path[] =
212 L"%SystemRoot%\\System32\\rundll32.exe";
213 wchar_t rundll32[MAX_PATH];
214 DWORD size =
215 ExpandEnvironmentStrings(kRunDll32Path, rundll32, arraysize(rundll32));
216 if (!size || size >= MAX_PATH)
217 return false;
219 STARTUPINFO startup = { sizeof(STARTUPINFO) };
220 PROCESS_INFORMATION pi = {0};
221 BOOL ok = ::CreateProcess(NULL, rundll32, NULL, NULL, FALSE, CREATE_SUSPENDED,
222 NULL, NULL, &startup, &pi);
223 if (ok) {
224 // We use the main thread of the new process to run:
225 // Sleep(delay_before_delete_ms);
226 // DeleteFile(path);
227 // ExitProcess(0);
228 // This runs before the main routine of the process runs, so it doesn't
229 // matter much which executable we choose except that we don't want to
230 // use e.g. a console app that causes a window to be created.
231 size = static_cast<DWORD>(
232 (path.value().length() + 1) * sizeof(path.value()[0]));
233 void* mem = ::VirtualAllocEx(pi.hProcess, NULL, size, MEM_COMMIT,
234 PAGE_READWRITE);
235 if (mem) {
236 SIZE_T written = 0;
237 ::WriteProcessMemory(
238 pi.hProcess, mem, path.value().c_str(),
239 (path.value().size() + 1) * sizeof(path.value()[0]), &written);
240 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
241 PAPCFUNC sleep = reinterpret_cast<PAPCFUNC>(
242 ::GetProcAddress(kernel32, "Sleep"));
243 PAPCFUNC delete_file = reinterpret_cast<PAPCFUNC>(
244 ::GetProcAddress(kernel32, "DeleteFileW"));
245 PAPCFUNC exit_process = reinterpret_cast<PAPCFUNC>(
246 ::GetProcAddress(kernel32, "ExitProcess"));
247 if (!sleep || !delete_file || !exit_process) {
248 NOTREACHED();
249 ok = FALSE;
250 } else {
251 ::QueueUserAPC(sleep, pi.hThread, delay_before_delete_ms);
252 ::QueueUserAPC(delete_file, pi.hThread,
253 reinterpret_cast<ULONG_PTR>(mem));
254 ::QueueUserAPC(exit_process, pi.hThread, 0);
255 ::ResumeThread(pi.hThread);
257 } else {
258 PLOG(ERROR) << "VirtualAllocEx";
259 ::TerminateProcess(pi.hProcess, ~static_cast<UINT>(0));
261 ::CloseHandle(pi.hThread);
262 ::CloseHandle(pi.hProcess);
265 return ok != FALSE;
268 bool GetExistingHigherInstaller(
269 const InstallationState& original_state,
270 bool system_install,
271 const Version& installer_version,
272 base::FilePath* setup_exe) {
273 DCHECK(setup_exe);
274 bool trying_single_browser = false;
275 const ProductState* existing_state =
276 original_state.GetProductState(system_install,
277 BrowserDistribution::CHROME_BINARIES);
278 if (!existing_state) {
279 // The binaries aren't installed, but perhaps a single-install Chrome is.
280 trying_single_browser = true;
281 existing_state =
282 original_state.GetProductState(system_install,
283 BrowserDistribution::CHROME_BROWSER);
286 if (!existing_state ||
287 existing_state->version().CompareTo(installer_version) <= 0) {
288 return false;
291 *setup_exe = existing_state->GetSetupPath();
293 VLOG_IF(1, !setup_exe->empty()) << "Found a higher version of "
294 << (trying_single_browser ? "single-install Chrome."
295 : "multi-install Chrome binaries.");
297 return !setup_exe->empty();
300 bool DeferToExistingInstall(const base::FilePath& setup_exe,
301 const base::CommandLine& command_line,
302 const InstallerState& installer_state,
303 const base::FilePath& temp_path,
304 InstallStatus* install_status) {
305 // Copy a master_preferences file if there is one.
306 base::FilePath prefs_source_path(command_line.GetSwitchValueNative(
307 switches::kInstallerData));
308 base::FilePath prefs_dest_path(installer_state.target_path().AppendASCII(
309 kDefaultMasterPrefs));
310 scoped_ptr<WorkItem> copy_prefs(WorkItem::CreateCopyTreeWorkItem(
311 prefs_source_path, prefs_dest_path, temp_path, WorkItem::ALWAYS,
312 base::FilePath()));
313 // There's nothing to rollback if the copy fails, so punt if so.
314 if (!copy_prefs->Do())
315 copy_prefs.reset();
317 int exit_code = 0;
318 if (!LaunchAndWaitForExistingInstall(setup_exe, command_line, &exit_code)) {
319 if (copy_prefs)
320 copy_prefs->Rollback();
321 return false;
323 *install_status = static_cast<InstallStatus>(exit_code);
324 return true;
327 // There are 4 disjoint cases => return values {false,true}:
328 // (1) Product is being uninstalled => false.
329 // (2) Product is being installed => true.
330 // (3) Current operation ignores product, product is absent => false.
331 // (4) Current operation ignores product, product is present => true.
332 bool WillProductBePresentAfterSetup(
333 const installer::InstallerState& installer_state,
334 const installer::InstallationState& machine_state,
335 BrowserDistribution::Type type) {
336 DCHECK(SupportsSingleInstall(type) || installer_state.is_multi_install());
338 const ProductState* product_state =
339 machine_state.GetProductState(installer_state.system_install(), type);
341 // Determine if the product is present prior to the current operation.
342 bool is_present = (product_state != NULL);
343 bool is_uninstall = installer_state.operation() == InstallerState::UNINSTALL;
345 // Determine if current operation affects the product.
346 const Product* product = installer_state.FindProduct(type);
347 bool is_affected = (product != NULL);
349 // Decide among {(1),(2),(3),(4)}.
350 return is_affected ? !is_uninstall : is_present;
353 bool AdjustProcessPriority() {
354 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
355 DWORD priority_class = ::GetPriorityClass(::GetCurrentProcess());
356 if (priority_class == 0) {
357 PLOG(WARNING) << "Failed to get the process's priority class.";
358 } else if (priority_class == BELOW_NORMAL_PRIORITY_CLASS ||
359 priority_class == IDLE_PRIORITY_CLASS) {
360 BOOL result = ::SetPriorityClass(::GetCurrentProcess(),
361 PROCESS_MODE_BACKGROUND_BEGIN);
362 PLOG_IF(WARNING, !result) << "Failed to enter background mode.";
363 return !!result;
366 return false;
369 void MigrateGoogleUpdateStateMultiToSingle(
370 bool system_level,
371 BrowserDistribution::Type to_migrate,
372 const installer::InstallationState& machine_state) {
373 const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
374 const ProductState* product = NULL;
375 BrowserDistribution* dist = NULL;
376 LONG result = ERROR_SUCCESS;
377 base::win::RegKey state_key;
379 Product product_to_migrate(
380 BrowserDistribution::GetSpecificDistribution(to_migrate));
382 // Copy usagestats from the binaries to the product's ClientState key.
383 product = machine_state.GetProductState(system_level,
384 BrowserDistribution::CHROME_BINARIES);
385 DWORD usagestats = 0;
386 if (product && product->GetUsageStats(&usagestats)) {
387 dist = product_to_migrate.distribution();
388 result = state_key.Open(root, dist->GetStateKey().c_str(),
389 KEY_SET_VALUE);
390 if (result != ERROR_SUCCESS) {
391 LOG(ERROR) << "Failed opening ClientState key for "
392 << dist->GetDisplayName() << " to migrate usagestats.";
393 } else {
394 state_key.WriteValue(google_update::kRegUsageStatsField, usagestats);
398 // Remove the migrating product from the "ap" value of other multi-install
399 // products.
400 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
401 BrowserDistribution::Type type =
402 static_cast<BrowserDistribution::Type>(i);
403 if (type == to_migrate)
404 continue;
405 product = machine_state.GetProductState(system_level, type);
406 if (product && product->is_multi_install()) {
407 installer::ChannelInfo channel_info;
408 dist = BrowserDistribution::GetSpecificDistribution(type);
409 result = state_key.Open(root, dist->GetStateKey().c_str(),
410 KEY_QUERY_VALUE | KEY_SET_VALUE);
411 if (result == ERROR_SUCCESS &&
412 channel_info.Initialize(state_key) &&
413 product_to_migrate.SetChannelFlags(false, &channel_info)) {
414 VLOG(1) << "Moving " << dist->GetDisplayName()
415 << " to channel: " << channel_info.value();
416 channel_info.Write(&state_key);
421 // Remove -multi, all product modifiers, and everything else but the channel
422 // name from the "ap" value of the product to migrate.
423 dist = product_to_migrate.distribution();
424 result = state_key.Open(root, dist->GetStateKey().c_str(),
425 KEY_QUERY_VALUE | KEY_SET_VALUE);
426 if (result == ERROR_SUCCESS) {
427 installer::ChannelInfo channel_info;
428 if (!channel_info.Initialize(state_key)) {
429 LOG(ERROR) << "Failed reading " << dist->GetDisplayName()
430 << " channel info.";
431 } else if (channel_info.RemoveAllModifiersAndSuffixes()) {
432 VLOG(1) << "Moving " << dist->GetDisplayName()
433 << " to channel: " << channel_info.value();
434 channel_info.Write(&state_key);
439 bool IsUninstallSuccess(InstallStatus install_status) {
440 // The following status values represent failed uninstalls:
441 // 15: CHROME_NOT_INSTALLED
442 // 20: UNINSTALL_FAILED
443 // 21: UNINSTALL_CANCELLED
444 return (install_status == UNINSTALL_SUCCESSFUL ||
445 install_status == UNINSTALL_REQUIRES_REBOOT);
448 bool ContainsUnsupportedSwitch(const base::CommandLine& cmd_line) {
449 static const char* const kLegacySwitches[] = {
450 // Chrome Frame ready-mode.
451 "ready-mode",
452 "ready-mode-opt-in",
453 "ready-mode-temp-opt-out",
454 "ready-mode-end-temp-opt-out",
455 // Chrome Frame quick-enable.
456 "quick-enable-cf",
457 // Installation of Chrome Frame.
458 "chrome-frame",
459 "migrate-chrome-frame",
460 // Stand-alone App Launcher.
461 "app-host",
462 "app-launcher",
464 for (size_t i = 0; i < arraysize(kLegacySwitches); ++i) {
465 if (cmd_line.HasSwitch(kLegacySwitches[i]))
466 return true;
468 return false;
471 bool IsProcessorSupported() {
472 return base::CPU().has_sse2();
475 base::string16 GetRegistrationDataCommandKey(
476 const AppRegistrationData& reg_data,
477 const wchar_t* name) {
478 base::string16 cmd_key(reg_data.GetVersionKey());
479 cmd_key.append(1, base::FilePath::kSeparators[0])
480 .append(google_update::kRegCommandsKey)
481 .append(1, base::FilePath::kSeparators[0])
482 .append(name);
483 return cmd_key;
486 void DeleteRegistryKeyPartial(
487 HKEY root,
488 const base::string16& path,
489 const std::vector<base::string16>& keys_to_preserve) {
490 // Downcase the list of keys to preserve (all must be ASCII strings).
491 std::set<base::string16> lowered_keys_to_preserve;
492 std::transform(
493 keys_to_preserve.begin(), keys_to_preserve.end(),
494 std::inserter(lowered_keys_to_preserve, lowered_keys_to_preserve.begin()),
495 [](const base::string16& str) {
496 DCHECK(!str.empty());
497 DCHECK(base::IsStringASCII(str));
498 return base::ToLowerASCII(str);
500 base::win::RegKey key;
501 LONG result = key.Open(root, path.c_str(), (KEY_ENUMERATE_SUB_KEYS |
502 KEY_QUERY_VALUE | KEY_SET_VALUE));
503 if (result != ERROR_SUCCESS) {
504 LOG_IF(ERROR, result != ERROR_FILE_NOT_FOUND) << "Failed to open " << path
505 << "; result = " << result;
506 return;
509 // Repeatedly iterate over all subkeys deleting those that should not be
510 // preserved until only those remain. Multiple passes are needed since
511 // deleting one key may change the enumeration order of all remaining keys.
513 // Subkeys or values to be skipped on subsequent passes.
514 std::set<base::string16> to_skip;
515 DWORD index = 0;
516 const size_t kMaxKeyNameLength = 256; // MSDN says 255; +1 for terminator.
517 base::string16 name(kMaxKeyNameLength, base::char16());
518 bool did_delete = false; // True if at least one item was deleted.
519 while (true) {
520 DWORD name_length = base::saturated_cast<DWORD>(name.capacity());
521 name.resize(name_length);
522 result = ::RegEnumKeyEx(key.Handle(), index, &name[0], &name_length,
523 nullptr, nullptr, nullptr, nullptr);
524 if (result == ERROR_MORE_DATA) {
525 // Unexpected, but perhaps the max key name length was raised. MSDN
526 // doesn't clearly say that name_length will contain the necessary
527 // length in this case, so double the buffer and try again.
528 name.reserve(name.capacity() * 2);
529 continue;
531 if (result == ERROR_NO_MORE_ITEMS) {
532 if (!did_delete)
533 break; // All subkeys were deleted. The job is done.
534 // Otherwise, loop again.
535 did_delete = false;
536 index = 0;
537 continue;
539 if (result != ERROR_SUCCESS)
540 break;
541 // Shrink the string to the actual length of the name.
542 name.resize(name_length);
544 // Skip over this key if it couldn't be deleted on a previous iteration.
545 if (to_skip.count(name)) {
546 ++index;
547 continue;
550 // Skip over this key if it is one of the keys to preserve.
551 if (base::IsStringASCII(name) &&
552 lowered_keys_to_preserve.count(base::ToLowerASCII(name))) {
553 // Add the true name of the key to the list of keys to skip for subsequent
554 // iterations.
555 to_skip.insert(name);
556 ++index;
557 continue;
560 // Delete this key.
561 result = key.DeleteKey(name.c_str());
562 if (result != ERROR_SUCCESS) {
563 LOG(ERROR) << "Failed to delete subkey " << name << " under path "
564 << path;
565 // Skip over this key on subsequent iterations.
566 to_skip.insert(name);
567 ++index;
568 continue;
570 did_delete = true;
573 // Delete the key if it no longer has any subkeys.
574 if (to_skip.empty()) {
575 result = key.DeleteEmptyKey(L"");
576 LOG_IF(ERROR, result != ERROR_SUCCESS) << "Failed to delete empty key "
577 << path << "; result: " << result;
578 return;
581 // Delete all values since subkeys are being preserved.
582 to_skip.clear();
583 did_delete = false;
584 index = 0;
585 while (true) {
586 DWORD name_length = base::saturated_cast<int16_t>(name.capacity());
587 name.resize(name_length);
588 result = ::RegEnumValue(key.Handle(), index, &name[0], &name_length,
589 nullptr, nullptr, nullptr, nullptr);
590 if (result == ERROR_MORE_DATA) {
591 if (name_length <
592 static_cast<DWORD>(std::numeric_limits<int16_t>::max())) {
593 // Double the space to hold the value name and try again.
594 name.reserve(name.capacity() * 2);
595 continue;
597 // Otherwise, the max has been exceeded. Nothing more to be done.
598 break;
600 if (result == ERROR_NO_MORE_ITEMS) {
601 if (!did_delete)
602 break; // All values were deleted. The job is done.
603 // Otherwise, loop again.
604 did_delete = false;
605 index = 0;
606 continue;
608 if (result != ERROR_SUCCESS)
609 break;
610 // Shrink the string to the actual length of the name.
611 name.resize(name_length);
613 // Skip over this value if it couldn't be deleted on a previous iteration.
614 if (to_skip.count(name)) {
615 ++index;
616 continue;
619 // Delete this value.
620 result = key.DeleteValue(name.c_str());
621 if (result != ERROR_SUCCESS) {
622 LOG(ERROR) << "Failed to delete value " << name << " in key " << path;
623 // Skip over this value on subsequent iterations.
624 to_skip.insert(name);
625 ++index;
626 continue;
628 did_delete = true;
632 ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name)
633 : is_enabled_(false) {
634 HANDLE temp_handle;
635 if (!::OpenProcessToken(::GetCurrentProcess(),
636 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
637 &temp_handle)) {
638 return;
640 token_.Set(temp_handle);
642 LUID privilege_luid;
643 if (!::LookupPrivilegeValue(NULL, privilege_name, &privilege_luid)) {
644 token_.Close();
645 return;
648 // Adjust the token's privileges to enable |privilege_name|. If this privilege
649 // was already enabled, |previous_privileges_|.PrivilegeCount will be set to 0
650 // and we then know not to disable this privilege upon destruction.
651 TOKEN_PRIVILEGES tp;
652 tp.PrivilegeCount = 1;
653 tp.Privileges[0].Luid = privilege_luid;
654 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
655 DWORD return_length;
656 if (!::AdjustTokenPrivileges(token_.Get(), FALSE, &tp,
657 sizeof(TOKEN_PRIVILEGES),
658 &previous_privileges_, &return_length)) {
659 token_.Close();
660 return;
663 is_enabled_ = true;
666 ScopedTokenPrivilege::~ScopedTokenPrivilege() {
667 if (is_enabled_ && previous_privileges_.PrivilegeCount != 0) {
668 ::AdjustTokenPrivileges(token_.Get(), FALSE, &previous_privileges_,
669 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
673 } // namespace installer