Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / installer / setup / setup_util.cc
blobb9575d02240b68b59d2ec4aad4f047e0ff133d7e
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>
10 #include <stdint.h>
12 #include "base/command_line.h"
13 #include "base/cpu.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/logging.h"
18 #include "base/process/kill.h"
19 #include "base/process/launch.h"
20 #include "base/process/process_handle.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/version.h"
26 #include "base/win/registry.h"
27 #include "base/win/win_util.h"
28 #include "base/win/windows_version.h"
29 #include "chrome/installer/setup/setup_constants.h"
30 #include "chrome/installer/setup/update_active_setup_version_work_item.h"
31 #include "chrome/installer/util/app_registration_data.h"
32 #include "chrome/installer/util/app_registration_data.h"
33 #include "chrome/installer/util/copy_tree_work_item.h"
34 #include "chrome/installer/util/google_update_constants.h"
35 #include "chrome/installer/util/install_util.h"
36 #include "chrome/installer/util/installation_state.h"
37 #include "chrome/installer/util/installer_state.h"
38 #include "chrome/installer/util/master_preferences.h"
39 #include "chrome/installer/util/util_constants.h"
40 #include "chrome/installer/util/work_item.h"
41 #include "courgette/courgette.h"
42 #include "courgette/third_party/bsdiff.h"
43 #include "third_party/bspatch/mbspatch.h"
45 namespace installer {
47 namespace {
49 // Launches |setup_exe| with |command_line|, save --install-archive and its
50 // value if present. Returns false if the process failed to launch. Otherwise,
51 // waits indefinitely for it to exit and populates |exit_code| as expected. On
52 // the off chance that waiting itself fails, |exit_code| is set to
53 // WAIT_FOR_EXISTING_FAILED.
54 bool LaunchAndWaitForExistingInstall(const base::FilePath& setup_exe,
55 const base::CommandLine& command_line,
56 int* exit_code) {
57 DCHECK(exit_code);
58 base::CommandLine new_cl(setup_exe);
60 // Copy over all switches but --install-archive.
61 base::CommandLine::SwitchMap switches(command_line.GetSwitches());
62 switches.erase(switches::kInstallArchive);
63 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
64 i != switches.end(); ++i) {
65 if (i->second.empty())
66 new_cl.AppendSwitch(i->first);
67 else
68 new_cl.AppendSwitchNative(i->first, i->second);
71 // Copy over all arguments.
72 base::CommandLine::StringVector args(command_line.GetArgs());
73 for (base::CommandLine::StringVector::const_iterator i = args.begin();
74 i != args.end(); ++i) {
75 new_cl.AppendArgNative(*i);
78 // Launch the process and wait for it to exit.
79 VLOG(1) << "Launching existing installer with command: "
80 << new_cl.GetCommandLineString();
81 base::Process process = base::LaunchProcess(new_cl, base::LaunchOptions());
82 if (!process.IsValid()) {
83 PLOG(ERROR) << "Failed to launch existing installer with command: "
84 << new_cl.GetCommandLineString();
85 return false;
87 if (!process.WaitForExit(exit_code)) {
88 PLOG(DFATAL) << "Failed to get exit code from existing installer";
89 *exit_code = WAIT_FOR_EXISTING_FAILED;
90 } else {
91 VLOG(1) << "Existing installer returned exit code " << *exit_code;
93 return true;
96 // Returns true if product |type| cam be meaningfully installed without the
97 // --multi-install flag.
98 bool SupportsSingleInstall(BrowserDistribution::Type type) {
99 return (type == BrowserDistribution::CHROME_BROWSER ||
100 type == BrowserDistribution::CHROME_FRAME);
103 } // namespace
105 bool UpdateLastOSUpgradeHandledByActiveSetup(BrowserDistribution* dist) {
106 // FIRST: Find the value of the latest OS upgrade registered in the Active
107 // Setup version (bumped on every major OS upgrade), defaults to 0 if no OS
108 // upgrade was ever encountered by this install.
109 DWORD latest_os_upgrade = 0;
112 const base::string16 active_setup_key_path(
113 InstallUtil::GetActiveSetupPath(dist));
115 base::win::RegKey active_setup_key;
116 if (active_setup_key.Open(HKEY_LOCAL_MACHINE, active_setup_key_path.c_str(),
117 KEY_QUERY_VALUE) == ERROR_SUCCESS) {
118 base::string16 existing_version;
119 if (active_setup_key.ReadValue(L"Version",
120 &existing_version) == ERROR_SUCCESS) {
121 std::vector<base::string16> version_components =
122 base::SplitString(existing_version, L",", base::TRIM_WHITESPACE,
123 base::SPLIT_WANT_NONEMPTY);
124 uint32_t latest_os_upgrade_uint = 0;
125 if (version_components.size() == 4U &&
126 base::StringToUint(
127 version_components[UpdateActiveSetupVersionWorkItem::
128 VersionComponent::OS_UPGRADES],
129 &latest_os_upgrade_uint)) {
130 latest_os_upgrade = static_cast<DWORD>(latest_os_upgrade_uint);
131 } else {
132 LOG(ERROR) << "Failed to parse OS_UPGRADES component of "
133 << existing_version;
139 // Whether the read failed or the existing value is 0, do not proceed.
140 if (latest_os_upgrade == 0U)
141 return false;
143 static const wchar_t kLastOSUpgradeHandledRegName[] = L"LastOSUpgradeHandled";
145 // SECOND: Find out the value of the last OS upgrade handled, defaults to 0 if
146 // none was ever handled.
147 DWORD last_os_upgrade_handled = 0;
149 base::string16 last_upgrade_handled_key_path =
150 dist->GetAppRegistrationData().GetStateMediumKey();
151 last_upgrade_handled_key_path.push_back(L'\\');
152 last_upgrade_handled_key_path.append(kLastOSUpgradeHandledRegName);
154 base::string16 user_specific_value;
155 // This should never fail. If it does, the beacon will be written in the key's
156 // default value, which is okay since the majority case is likely a machine
157 // with a single user.
158 if (!base::win::GetUserSidString(&user_specific_value))
159 NOTREACHED();
161 base::win::RegKey last_upgrade_key;
162 if (last_upgrade_key.Create(
163 HKEY_LOCAL_MACHINE, last_upgrade_handled_key_path.c_str(),
164 KEY_WOW64_32KEY | KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS) {
165 LOG(ERROR) << "Failed to create LastOSUpgradeHandled value @ "
166 << last_upgrade_handled_key_path;
167 // If the key is not read/writeable, do not proceed as this could result in
168 // handling an OS upgrade twice.
169 return false;
172 // It's okay for this read to fail (i.e. there is an OS upgrade available but
173 // this user never handled one yet).
174 last_upgrade_key.ReadValueDW(user_specific_value.c_str(),
175 &last_os_upgrade_handled);
177 // THIRD: Figure out whether the latest OS upgrade has been handled already.
179 if (last_os_upgrade_handled >= latest_os_upgrade) {
180 LOG_IF(ERROR, last_os_upgrade_handled > latest_os_upgrade)
181 << "Last OS upgrade handled is somehow ahead of the latest OS upgrade?";
182 VLOG_IF(1, last_os_upgrade_handled == latest_os_upgrade)
183 << "Latest OS upgrade already handled.";
184 return false;
187 // At this point |last_os_upgrade_handled < latest_os_upgrade| so,
188 // FOURTH: store the fact that the latest OS upgrade has been handled and
189 // return true for the caller to act accordingly.
191 if (last_upgrade_key.WriteValue(user_specific_value.c_str(),
192 latest_os_upgrade) != ERROR_SUCCESS) {
193 LOG(ERROR) << "Failed to save latest_os_upgrade value ("
194 << latest_os_upgrade << ") to " << last_upgrade_handled_key_path;
195 // Do not proceed if the write fails as this could otherwise result in
196 // handling this OS upgrade multiple times.
197 return false;
200 return true;
203 int CourgettePatchFiles(const base::FilePath& src,
204 const base::FilePath& patch,
205 const base::FilePath& dest) {
206 VLOG(1) << "Applying Courgette patch " << patch.value()
207 << " to file " << src.value()
208 << " and generating file " << dest.value();
210 if (src.empty() || patch.empty() || dest.empty())
211 return installer::PATCH_INVALID_ARGUMENTS;
213 const courgette::Status patch_status =
214 courgette::ApplyEnsemblePatch(src.value().c_str(),
215 patch.value().c_str(),
216 dest.value().c_str());
217 const int exit_code = (patch_status != courgette::C_OK) ?
218 static_cast<int>(patch_status) + kCourgetteErrorOffset : 0;
220 LOG_IF(ERROR, exit_code)
221 << "Failed to apply Courgette patch " << patch.value()
222 << " to file " << src.value() << " and generating file " << dest.value()
223 << ". err=" << exit_code;
225 return exit_code;
228 int BsdiffPatchFiles(const base::FilePath& src,
229 const base::FilePath& patch,
230 const base::FilePath& dest) {
231 VLOG(1) << "Applying bsdiff patch " << patch.value()
232 << " to file " << src.value()
233 << " and generating file " << dest.value();
235 if (src.empty() || patch.empty() || dest.empty())
236 return installer::PATCH_INVALID_ARGUMENTS;
238 const int patch_status = courgette::ApplyBinaryPatch(src, patch, dest);
239 const int exit_code = patch_status != OK ?
240 patch_status + kBsdiffErrorOffset : 0;
242 LOG_IF(ERROR, exit_code)
243 << "Failed to apply bsdiff patch " << patch.value()
244 << " to file " << src.value() << " and generating file " << dest.value()
245 << ". err=" << exit_code;
247 return exit_code;
250 Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path) {
251 VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value();
252 base::FileEnumerator version_enum(chrome_path, false,
253 base::FileEnumerator::DIRECTORIES);
254 // TODO(tommi): The version directory really should match the version of
255 // setup.exe. To begin with, we should at least DCHECK that that's true.
257 scoped_ptr<Version> max_version(new Version("0.0.0.0"));
258 bool version_found = false;
260 while (!version_enum.Next().empty()) {
261 base::FileEnumerator::FileInfo find_data = version_enum.GetInfo();
262 VLOG(1) << "directory found: " << find_data.GetName().value();
264 scoped_ptr<Version> found_version(
265 new Version(base::UTF16ToASCII(find_data.GetName().value())));
266 if (found_version->IsValid() &&
267 found_version->CompareTo(*max_version.get()) > 0) {
268 max_version.reset(found_version.release());
269 version_found = true;
273 return (version_found ? max_version.release() : NULL);
276 base::FilePath FindArchiveToPatch(const InstallationState& original_state,
277 const InstallerState& installer_state,
278 const base::Version& desired_version) {
279 if (desired_version.IsValid()) {
280 base::FilePath archive(installer_state.GetInstallerDirectory(
281 desired_version).Append(kChromeArchive));
282 return base::PathExists(archive) ? archive : base::FilePath();
285 // Check based on the version number advertised to Google Update, since that
286 // is the value used to select a specific differential update. If an archive
287 // can't be found using that, fallback to using the newest version present.
288 base::FilePath patch_source;
289 const ProductState* product =
290 original_state.GetProductState(installer_state.system_install(),
291 installer_state.state_type());
292 if (product) {
293 patch_source = installer_state.GetInstallerDirectory(product->version())
294 .Append(installer::kChromeArchive);
295 if (base::PathExists(patch_source))
296 return patch_source;
298 scoped_ptr<Version> version(
299 installer::GetMaxVersionFromArchiveDir(installer_state.target_path()));
300 if (version) {
301 patch_source = installer_state.GetInstallerDirectory(*version)
302 .Append(installer::kChromeArchive);
303 if (base::PathExists(patch_source))
304 return patch_source;
306 return base::FilePath();
309 bool DeleteFileFromTempProcess(const base::FilePath& path,
310 uint32 delay_before_delete_ms) {
311 static const wchar_t kRunDll32Path[] =
312 L"%SystemRoot%\\System32\\rundll32.exe";
313 wchar_t rundll32[MAX_PATH];
314 DWORD size =
315 ExpandEnvironmentStrings(kRunDll32Path, rundll32, arraysize(rundll32));
316 if (!size || size >= MAX_PATH)
317 return false;
319 STARTUPINFO startup = { sizeof(STARTUPINFO) };
320 PROCESS_INFORMATION pi = {0};
321 BOOL ok = ::CreateProcess(NULL, rundll32, NULL, NULL, FALSE, CREATE_SUSPENDED,
322 NULL, NULL, &startup, &pi);
323 if (ok) {
324 // We use the main thread of the new process to run:
325 // Sleep(delay_before_delete_ms);
326 // DeleteFile(path);
327 // ExitProcess(0);
328 // This runs before the main routine of the process runs, so it doesn't
329 // matter much which executable we choose except that we don't want to
330 // use e.g. a console app that causes a window to be created.
331 size = (path.value().length() + 1) * sizeof(path.value()[0]);
332 void* mem = ::VirtualAllocEx(pi.hProcess, NULL, size, MEM_COMMIT,
333 PAGE_READWRITE);
334 if (mem) {
335 SIZE_T written = 0;
336 ::WriteProcessMemory(
337 pi.hProcess, mem, path.value().c_str(),
338 (path.value().size() + 1) * sizeof(path.value()[0]), &written);
339 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
340 PAPCFUNC sleep = reinterpret_cast<PAPCFUNC>(
341 ::GetProcAddress(kernel32, "Sleep"));
342 PAPCFUNC delete_file = reinterpret_cast<PAPCFUNC>(
343 ::GetProcAddress(kernel32, "DeleteFileW"));
344 PAPCFUNC exit_process = reinterpret_cast<PAPCFUNC>(
345 ::GetProcAddress(kernel32, "ExitProcess"));
346 if (!sleep || !delete_file || !exit_process) {
347 NOTREACHED();
348 ok = FALSE;
349 } else {
350 ::QueueUserAPC(sleep, pi.hThread, delay_before_delete_ms);
351 ::QueueUserAPC(delete_file, pi.hThread,
352 reinterpret_cast<ULONG_PTR>(mem));
353 ::QueueUserAPC(exit_process, pi.hThread, 0);
354 ::ResumeThread(pi.hThread);
356 } else {
357 PLOG(ERROR) << "VirtualAllocEx";
358 ::TerminateProcess(pi.hProcess, ~static_cast<UINT>(0));
360 ::CloseHandle(pi.hThread);
361 ::CloseHandle(pi.hProcess);
364 return ok != FALSE;
367 bool GetExistingHigherInstaller(
368 const InstallationState& original_state,
369 bool system_install,
370 const Version& installer_version,
371 base::FilePath* setup_exe) {
372 DCHECK(setup_exe);
373 bool trying_single_browser = false;
374 const ProductState* existing_state =
375 original_state.GetProductState(system_install,
376 BrowserDistribution::CHROME_BINARIES);
377 if (!existing_state) {
378 // The binaries aren't installed, but perhaps a single-install Chrome is.
379 trying_single_browser = true;
380 existing_state =
381 original_state.GetProductState(system_install,
382 BrowserDistribution::CHROME_BROWSER);
385 if (!existing_state ||
386 existing_state->version().CompareTo(installer_version) <= 0) {
387 return false;
390 *setup_exe = existing_state->GetSetupPath();
392 VLOG_IF(1, !setup_exe->empty()) << "Found a higher version of "
393 << (trying_single_browser ? "single-install Chrome."
394 : "multi-install Chrome binaries.");
396 return !setup_exe->empty();
399 bool DeferToExistingInstall(const base::FilePath& setup_exe,
400 const base::CommandLine& command_line,
401 const InstallerState& installer_state,
402 const base::FilePath& temp_path,
403 InstallStatus* install_status) {
404 // Copy a master_preferences file if there is one.
405 base::FilePath prefs_source_path(command_line.GetSwitchValueNative(
406 switches::kInstallerData));
407 base::FilePath prefs_dest_path(installer_state.target_path().AppendASCII(
408 kDefaultMasterPrefs));
409 scoped_ptr<WorkItem> copy_prefs(WorkItem::CreateCopyTreeWorkItem(
410 prefs_source_path, prefs_dest_path, temp_path, WorkItem::ALWAYS,
411 base::FilePath()));
412 // There's nothing to rollback if the copy fails, so punt if so.
413 if (!copy_prefs->Do())
414 copy_prefs.reset();
416 int exit_code = 0;
417 if (!LaunchAndWaitForExistingInstall(setup_exe, command_line, &exit_code)) {
418 if (copy_prefs)
419 copy_prefs->Rollback();
420 return false;
422 *install_status = static_cast<InstallStatus>(exit_code);
423 return true;
426 // There are 4 disjoint cases => return values {false,true}:
427 // (1) Product is being uninstalled => false.
428 // (2) Product is being installed => true.
429 // (3) Current operation ignores product, product is absent => false.
430 // (4) Current operation ignores product, product is present => true.
431 bool WillProductBePresentAfterSetup(
432 const installer::InstallerState& installer_state,
433 const installer::InstallationState& machine_state,
434 BrowserDistribution::Type type) {
435 DCHECK(SupportsSingleInstall(type) || installer_state.is_multi_install());
437 const ProductState* product_state =
438 machine_state.GetProductState(installer_state.system_install(), type);
440 // Determine if the product is present prior to the current operation.
441 bool is_present = (product_state != NULL);
442 bool is_uninstall = installer_state.operation() == InstallerState::UNINSTALL;
444 // Determine if current operation affects the product.
445 const Product* product = installer_state.FindProduct(type);
446 bool is_affected = (product != NULL);
448 // Decide among {(1),(2),(3),(4)}.
449 return is_affected ? !is_uninstall : is_present;
452 bool AdjustProcessPriority() {
453 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
454 DWORD priority_class = ::GetPriorityClass(::GetCurrentProcess());
455 if (priority_class == 0) {
456 PLOG(WARNING) << "Failed to get the process's priority class.";
457 } else if (priority_class == BELOW_NORMAL_PRIORITY_CLASS ||
458 priority_class == IDLE_PRIORITY_CLASS) {
459 BOOL result = ::SetPriorityClass(::GetCurrentProcess(),
460 PROCESS_MODE_BACKGROUND_BEGIN);
461 PLOG_IF(WARNING, !result) << "Failed to enter background mode.";
462 return !!result;
465 return false;
468 void MigrateGoogleUpdateStateMultiToSingle(
469 bool system_level,
470 BrowserDistribution::Type to_migrate,
471 const installer::InstallationState& machine_state) {
472 const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
473 const ProductState* product = NULL;
474 BrowserDistribution* dist = NULL;
475 LONG result = ERROR_SUCCESS;
476 base::win::RegKey state_key;
478 Product product_to_migrate(
479 BrowserDistribution::GetSpecificDistribution(to_migrate));
481 // Copy usagestats from the binaries to the product's ClientState key.
482 product = machine_state.GetProductState(system_level,
483 BrowserDistribution::CHROME_BINARIES);
484 DWORD usagestats = 0;
485 if (product && product->GetUsageStats(&usagestats)) {
486 dist = product_to_migrate.distribution();
487 result = state_key.Open(root, dist->GetStateKey().c_str(),
488 KEY_SET_VALUE);
489 if (result != ERROR_SUCCESS) {
490 LOG(ERROR) << "Failed opening ClientState key for "
491 << dist->GetDisplayName() << " to migrate usagestats.";
492 } else {
493 state_key.WriteValue(google_update::kRegUsageStatsField, usagestats);
497 // Remove the migrating product from the "ap" value of other multi-install
498 // products.
499 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
500 BrowserDistribution::Type type =
501 static_cast<BrowserDistribution::Type>(i);
502 if (type == to_migrate)
503 continue;
504 product = machine_state.GetProductState(system_level, type);
505 if (product && product->is_multi_install()) {
506 installer::ChannelInfo channel_info;
507 dist = BrowserDistribution::GetSpecificDistribution(type);
508 result = state_key.Open(root, dist->GetStateKey().c_str(),
509 KEY_QUERY_VALUE | KEY_SET_VALUE);
510 if (result == ERROR_SUCCESS &&
511 channel_info.Initialize(state_key) &&
512 product_to_migrate.SetChannelFlags(false, &channel_info)) {
513 VLOG(1) << "Moving " << dist->GetDisplayName()
514 << " to channel: " << channel_info.value();
515 channel_info.Write(&state_key);
520 // Remove -multi, all product modifiers, and everything else but the channel
521 // name from the "ap" value of the product to migrate.
522 dist = product_to_migrate.distribution();
523 result = state_key.Open(root, dist->GetStateKey().c_str(),
524 KEY_QUERY_VALUE | KEY_SET_VALUE);
525 if (result == ERROR_SUCCESS) {
526 installer::ChannelInfo channel_info;
527 if (!channel_info.Initialize(state_key)) {
528 LOG(ERROR) << "Failed reading " << dist->GetDisplayName()
529 << " channel info.";
530 } else if (channel_info.RemoveAllModifiersAndSuffixes()) {
531 VLOG(1) << "Moving " << dist->GetDisplayName()
532 << " to channel: " << channel_info.value();
533 channel_info.Write(&state_key);
538 bool IsUninstallSuccess(InstallStatus install_status) {
539 // The following status values represent failed uninstalls:
540 // 15: CHROME_NOT_INSTALLED
541 // 20: UNINSTALL_FAILED
542 // 21: UNINSTALL_CANCELLED
543 return (install_status == UNINSTALL_SUCCESSFUL ||
544 install_status == UNINSTALL_REQUIRES_REBOOT);
547 bool ContainsUnsupportedSwitch(const base::CommandLine& cmd_line) {
548 static const char* const kLegacySwitches[] = {
549 // Chrome Frame ready-mode.
550 "ready-mode",
551 "ready-mode-opt-in",
552 "ready-mode-temp-opt-out",
553 "ready-mode-end-temp-opt-out",
554 // Chrome Frame quick-enable.
555 "quick-enable-cf",
556 // Installation of Chrome Frame.
557 "chrome-frame",
558 "migrate-chrome-frame",
559 // Stand-alone App Launcher.
560 "app-host",
561 "app-launcher",
563 for (size_t i = 0; i < arraysize(kLegacySwitches); ++i) {
564 if (cmd_line.HasSwitch(kLegacySwitches[i]))
565 return true;
567 return false;
570 bool IsProcessorSupported() {
571 return base::CPU().has_sse2();
574 base::string16 GetRegistrationDataCommandKey(
575 const AppRegistrationData& reg_data,
576 const wchar_t* name) {
577 base::string16 cmd_key(reg_data.GetVersionKey());
578 cmd_key.append(1, base::FilePath::kSeparators[0])
579 .append(google_update::kRegCommandsKey)
580 .append(1, base::FilePath::kSeparators[0])
581 .append(name);
582 return cmd_key;
585 ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name)
586 : is_enabled_(false) {
587 HANDLE temp_handle;
588 if (!::OpenProcessToken(::GetCurrentProcess(),
589 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
590 &temp_handle)) {
591 return;
593 token_.Set(temp_handle);
595 LUID privilege_luid;
596 if (!::LookupPrivilegeValue(NULL, privilege_name, &privilege_luid)) {
597 token_.Close();
598 return;
601 // Adjust the token's privileges to enable |privilege_name|. If this privilege
602 // was already enabled, |previous_privileges_|.PrivilegeCount will be set to 0
603 // and we then know not to disable this privilege upon destruction.
604 TOKEN_PRIVILEGES tp;
605 tp.PrivilegeCount = 1;
606 tp.Privileges[0].Luid = privilege_luid;
607 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
608 DWORD return_length;
609 if (!::AdjustTokenPrivileges(token_.Get(), FALSE, &tp,
610 sizeof(TOKEN_PRIVILEGES),
611 &previous_privileges_, &return_length)) {
612 token_.Close();
613 return;
616 is_enabled_ = true;
619 ScopedTokenPrivilege::~ScopedTokenPrivilege() {
620 if (is_enabled_ && previous_privileges_.PrivilegeCount != 0) {
621 ::AdjustTokenPrivileges(token_.Get(), FALSE, &previous_privileges_,
622 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
626 } // namespace installer