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.
5 // mini_installer.exe is the first exe that is run when chrome is being
6 // installed or upgraded. It is designed to be extremely small (~5KB with no
7 // extra resources linked) and it has two main jobs:
8 // 1) unpack the resources (possibly decompressing some)
9 // 2) run the real installer (setup.exe) with appropriate flags.
11 // In order to be really small the app doesn't link against the CRT and
12 // defines the following compiler/linker flags:
13 // EnableIntrinsicFunctions="true" compiler: /Oi
14 // BasicRuntimeChecks="0"
15 // BufferSecurityCheck="false" compiler: /GS-
16 // EntryPointSymbol="MainEntryPoint" linker: /ENTRY
17 // IgnoreAllDefaultLibraries="true" linker: /NODEFAULTLIB
18 // OptimizeForWindows98="1" liker: /OPT:NOWIN98
19 // linker: /SAFESEH:NO
21 // have the linker merge the sections, saving us ~500 bytes.
22 #pragma comment(linker, "/MERGE:.rdata=.text")
28 #include "chrome/installer/mini_installer/appid.h"
29 #include "chrome/installer/mini_installer/configuration.h"
30 #include "chrome/installer/mini_installer/decompress.h"
31 #include "chrome/installer/mini_installer/exit_code.h"
32 #include "chrome/installer/mini_installer/mini_installer_constants.h"
33 #include "chrome/installer/mini_installer/mini_string.h"
34 #include "chrome/installer/mini_installer/pe_resource.h"
36 namespace mini_installer
{
38 typedef DWORD ProcessExitCode
;
39 typedef StackString
<MAX_PATH
> PathString
;
40 typedef StackString
<MAX_PATH
* 4> CommandString
;
42 // This structure passes data back and forth for the processing
43 // of resource callbacks.
45 // Input to the call back method. Specifies the dir to save resources.
46 const wchar_t* base_path
;
47 // First output from call back method. Full path of Chrome archive.
48 PathString
* chrome_resource_path
;
49 // Second output from call back method. Full path of Setup archive/exe.
50 PathString
* setup_resource_path
;
53 // A helper class used to manipulate the Windows registry. Typically, members
54 // return Windows last-error codes a la the Win32 registry API.
57 RegKey() : key_(NULL
) { }
58 ~RegKey() { Close(); }
60 // Opens the key named |sub_key| with given |access| rights. Returns
61 // ERROR_SUCCESS or some other error.
62 LONG
Open(HKEY key
, const wchar_t* sub_key
, REGSAM access
);
64 // Returns true if a key is open.
65 bool is_valid() const { return key_
!= NULL
; }
67 // Read a REG_SZ value from the registry into the memory indicated by |value|
68 // (of |value_size| wchar_t units). Returns ERROR_SUCCESS,
69 // ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, or some other error. |value| is
70 // guaranteed to be null-terminated on success.
71 LONG
ReadValue(const wchar_t* value_name
,
73 size_t value_size
) const;
75 // Write a REG_SZ value to the registry. |value| must be null-terminated.
76 // Returns ERROR_SUCCESS or an error code.
77 LONG
WriteValue(const wchar_t* value_name
, const wchar_t* value
);
79 // Closes the key if it was open.
83 RegKey(const RegKey
&);
84 RegKey
& operator=(const RegKey
&);
89 LONG
RegKey::Open(HKEY key
, const wchar_t* sub_key
, REGSAM access
) {
91 return ::RegOpenKeyEx(key
, sub_key
, NULL
, access
, &key_
);
94 LONG
RegKey::ReadValue(const wchar_t* value_name
,
96 size_t value_size
) const {
98 DWORD byte_length
= static_cast<DWORD
>(value_size
* sizeof(wchar_t));
99 LONG result
= ::RegQueryValueEx(key_
, value_name
, NULL
, &type
,
100 reinterpret_cast<BYTE
*>(value
),
102 if (result
== ERROR_SUCCESS
) {
103 if (type
!= REG_SZ
) {
104 result
= ERROR_NOT_SUPPORTED
;
105 } else if (byte_length
== 0) {
107 } else if (value
[byte_length
/sizeof(wchar_t) - 1] != L
'\0') {
108 if ((byte_length
/ sizeof(wchar_t)) < value_size
)
109 value
[byte_length
/ sizeof(wchar_t)] = L
'\0';
111 result
= ERROR_MORE_DATA
;
117 LONG
RegKey::WriteValue(const wchar_t* value_name
, const wchar_t* value
) {
118 return ::RegSetValueEx(key_
, value_name
, 0, REG_SZ
,
119 reinterpret_cast<const BYTE
*>(value
),
120 (lstrlen(value
) + 1) * sizeof(wchar_t));
123 void RegKey::Close() {
130 // Helper function to read a value from registry. Returns true if value
131 // is read successfully and stored in parameter value. Returns false otherwise.
132 // |size| is measured in wchar_t units.
133 bool ReadValueFromRegistry(HKEY root_key
, const wchar_t *sub_key
,
134 const wchar_t *value_name
, wchar_t *value
,
138 if (key
.Open(root_key
, sub_key
, KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
139 key
.ReadValue(value_name
, value
, size
) == ERROR_SUCCESS
) {
145 // Opens the Google Update ClientState key for a product. This finds only
146 // registry entries for Chrome; it does not support the Chromium registry
148 bool OpenClientStateKey(HKEY root_key
, const wchar_t* app_guid
, REGSAM access
,
150 PathString client_state_key
;
151 return client_state_key
.assign(kClientStateKeyBase
) &&
152 client_state_key
.append(app_guid
) &&
154 client_state_key
.get(),
155 access
| KEY_WOW64_32KEY
) == ERROR_SUCCESS
);
158 // This function sets the flag in registry to indicate that Google Update
159 // should try full installer next time. If the current installer works, this
160 // flag is cleared by setup.exe at the end of install. The flag will by default
161 // be written to HKCU, but if --system-level is included in the command line,
162 // it will be written to HKLM instead.
163 // TODO(grt): Write a unit test for this that uses registry virtualization.
164 void SetInstallerFlags(const Configuration
& configuration
) {
166 const REGSAM key_access
= KEY_QUERY_VALUE
| KEY_SET_VALUE
;
167 const HKEY root_key
=
168 configuration
.is_system_level() ? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
169 // This is ignored if multi-install is true.
170 const wchar_t* app_guid
=
171 configuration
.has_chrome_frame() ?
172 google_update::kChromeFrameAppGuid
:
173 configuration
.chrome_app_guid();
174 StackString
<128> value
;
175 LONG ret
= ERROR_SUCCESS
;
177 // When multi_install is true, we are potentially:
178 // 1. Performing a multi-install of some product(s) on a clean machine.
179 // Neither the product(s) nor the multi-installer will have a ClientState
180 // key in the registry, so there is nothing to be done.
181 // 2. Upgrading an existing multi-install. The multi-installer will have a
182 // ClientState key in the registry. Only it need be modified.
183 // 3. Migrating a single-install into a multi-install. The product will have
184 // a ClientState key in the registry. Only it need be modified.
185 // To handle all cases, we inspect the product's ClientState to see if it
186 // exists and its "ap" value does not contain "-multi". This is case 3, so we
187 // modify the product's ClientState. Otherwise, we check the
188 // multi-installer's ClientState and modify it if it exists.
189 if (configuration
.is_multi_install()) {
190 if (OpenClientStateKey(root_key
, app_guid
, key_access
, &key
)) {
191 // The product has a client state key. See if it's a single-install.
192 ret
= key
.ReadValue(kApRegistryValue
, value
.get(), value
.capacity());
193 if (ret
!= ERROR_FILE_NOT_FOUND
&&
194 (ret
!= ERROR_SUCCESS
||
195 FindTagInStr(value
.get(), kMultiInstallTag
, NULL
))) {
196 // Error or case 2: modify the multi-installer's value.
198 app_guid
= google_update::kMultiInstallAppGuid
;
199 } // else case 3: modify this value.
201 // case 1 or 2: modify the multi-installer's value.
203 app_guid
= google_update::kMultiInstallAppGuid
;
207 if (!key
.is_valid()) {
208 if (!OpenClientStateKey(root_key
, app_guid
, key_access
, &key
))
212 ret
= key
.ReadValue(kApRegistryValue
, value
.get(), value
.capacity());
215 // The conditions below are handling two cases:
216 // 1. When ap value is present, we want to add the required tag only if it is
218 // 2. When ap value is missing, we are going to create it with the required
220 if ((ret
== ERROR_SUCCESS
) || (ret
== ERROR_FILE_NOT_FOUND
)) {
221 if (ret
== ERROR_FILE_NOT_FOUND
)
224 if (!StrEndsWith(value
.get(), kFullInstallerSuffix
) &&
225 value
.append(kFullInstallerSuffix
)) {
226 key
.WriteValue(kApRegistryValue
, value
.get());
231 // Gets the setup.exe path from Registry by looking at the value of Uninstall
232 // string. |size| is measured in wchar_t units.
233 ProcessExitCode
GetSetupExePathForAppGuid(bool system_level
,
234 const wchar_t* app_guid
,
235 const wchar_t* previous_version
,
238 const HKEY root_key
= system_level
? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
240 if (!OpenClientStateKey(root_key
, app_guid
, KEY_QUERY_VALUE
, &key
) ||
241 (key
.ReadValue(kUninstallRegistryValue
, path
, size
) != ERROR_SUCCESS
)) {
242 return UNABLE_TO_FIND_REGISTRY_KEY
;
245 // Check that the path to the existing installer includes the expected
246 // version number. It's not necessary for accuracy to verify before/after
248 if (!SearchStringI(path
, previous_version
))
249 return PATCH_NOT_FOR_INSTALLED_VERSION
;
251 return SUCCESS_EXIT_CODE
;
254 // Gets the path to setup.exe of the previous version. The overall path is found
255 // in the Uninstall string in the registry. A previous version number specified
256 // in |configuration| is used if available. |size| is measured in wchar_t units.
257 ProcessExitCode
GetPreviousSetupExePath(const Configuration
& configuration
,
260 bool system_level
= configuration
.is_system_level();
261 const wchar_t* previous_version
= configuration
.previous_version();
262 ProcessExitCode exit_code
= GENERIC_ERROR
;
264 // If this is a multi install, first try looking in the binaries for the path.
265 if (configuration
.is_multi_install()) {
266 exit_code
= GetSetupExePathForAppGuid(
267 system_level
, google_update::kMultiInstallAppGuid
, previous_version
,
271 // Failing that, look in Chrome Frame's client state key if --chrome-frame was
273 if (exit_code
!= SUCCESS_EXIT_CODE
&& configuration
.has_chrome_frame()) {
274 exit_code
= GetSetupExePathForAppGuid(
275 system_level
, google_update::kChromeFrameAppGuid
, previous_version
,
279 // Make a last-ditch effort to look in the Chrome client state key.
280 if (exit_code
!= SUCCESS_EXIT_CODE
) {
281 exit_code
= GetSetupExePathForAppGuid(
282 system_level
, configuration
.chrome_app_guid(), previous_version
,
289 // Calls CreateProcess with good default parameters and waits for the process to
290 // terminate returning the process exit code. |exit_code|, if non-NULL, is
291 // populated with the process exit code.
292 ProcessExitCode
RunProcessAndWait(const wchar_t* exe_path
, wchar_t* cmdline
) {
293 STARTUPINFOW si
= {sizeof(si
)};
294 PROCESS_INFORMATION pi
= {0};
295 if (!::CreateProcess(exe_path
, cmdline
, NULL
, NULL
, FALSE
, CREATE_NO_WINDOW
,
296 NULL
, NULL
, &si
, &pi
)) {
297 return COULD_NOT_CREATE_PROCESS
;
300 ::CloseHandle(pi
.hThread
);
302 ProcessExitCode exit_code
= SUCCESS_EXIT_CODE
;
303 DWORD wr
= ::WaitForSingleObject(pi
.hProcess
, INFINITE
);
304 if (WAIT_OBJECT_0
!= wr
|| !::GetExitCodeProcess(pi
.hProcess
, &exit_code
))
305 exit_code
= WAIT_FOR_PROCESS_FAILED
;
307 ::CloseHandle(pi
.hProcess
);
312 // Appends any command line params passed to mini_installer to the given buffer
313 // so that they can be passed on to setup.exe.
314 // |buffer| is unchanged in case of error.
315 void AppendCommandLineFlags(const Configuration
& configuration
,
316 CommandString
* buffer
) {
317 PathString full_exe_path
;
318 size_t len
= ::GetModuleFileName(NULL
, full_exe_path
.get(),
319 full_exe_path
.capacity());
320 if (!len
|| len
>= full_exe_path
.capacity())
323 const wchar_t* exe_name
= GetNameFromPathExt(full_exe_path
.get(), len
);
325 // - configuration.program() returns the first command line argument
326 // passed into the program (that the user probably typed in this case).
327 // "mini_installer.exe"
329 // "out\Release\mini_installer"
330 // - |exe_name| is the executable file of the current process.
331 // "mini_installer.exe"
333 // Note that there are three possibilities to handle here.
334 // Receive a cmdline containing:
335 // 1) executable name WITH extension
336 // 2) executable name with NO extension
337 // 3) NO executable name as part of cmdline
338 const wchar_t* cmd_to_append
= L
"";
339 const wchar_t* arg0
= configuration
.program();
342 const wchar_t* arg0_base_name
= GetNameFromPathExt(arg0
, ::lstrlen(arg0
));
343 if (!StrStartsWith(exe_name
, arg0_base_name
)) {
344 // State 3: NO executable name as part of cmdline.
345 buffer
->append(L
" ");
346 cmd_to_append
= configuration
.command_line();
347 } else if (configuration
.argument_count() > 1) {
348 // State 1 or 2: Executable name is in cmdline.
349 // - Append everything AFTER the executable name.
350 // (Using arg0_base_name here to make sure to match with or without
351 // extension. Then move to the space following the token.)
352 const wchar_t* tmp
= SearchStringI(configuration
.command_line(),
354 tmp
= SearchStringI(tmp
, L
" ");
358 buffer
->append(cmd_to_append
);
362 // Windows defined callback used in the EnumResourceNames call. For each
363 // matching resource found, the callback is invoked and at this point we write
364 // it to disk. We expect resource names to start with 'chrome' or 'setup'. Any
365 // other name is treated as an error.
366 BOOL CALLBACK
OnResourceFound(HMODULE module
, const wchar_t* type
,
367 wchar_t* name
, LONG_PTR context
) {
371 Context
* ctx
= reinterpret_cast<Context
*>(context
);
373 PEResource
resource(name
, type
, module
);
374 if ((!resource
.IsValid()) ||
375 (resource
.Size() < 1) ||
376 (resource
.Size() > kMaxResourceSize
)) {
380 PathString full_path
;
381 if (!full_path
.assign(ctx
->base_path
) ||
382 !full_path
.append(name
) ||
383 !resource
.WriteToDisk(full_path
.get()))
386 if (StrStartsWith(name
, kChromeArchivePrefix
)) {
387 if (!ctx
->chrome_resource_path
->assign(full_path
.get()))
389 } else if (StrStartsWith(name
, kSetupPrefix
)) {
390 if (!ctx
->setup_resource_path
->assign(full_path
.get()))
393 // Resources should either start with 'chrome' or 'setup'. We don't handle
401 #if defined(COMPONENT_BUILD)
402 // An EnumResNameProc callback that writes the resource |name| to disk in the
403 // directory |base_path_ptr| (which must end with a path separator).
404 BOOL CALLBACK
WriteResourceToDirectory(HMODULE module
,
407 LONG_PTR base_path_ptr
) {
408 const wchar_t* base_path
= reinterpret_cast<const wchar_t*>(base_path_ptr
);
409 PathString full_path
;
411 PEResource
resource(name
, type
, module
);
412 return (resource
.IsValid() &&
413 full_path
.assign(base_path
) &&
414 full_path
.append(name
) &&
415 resource
.WriteToDisk(full_path
.get()));
419 // Finds and writes to disk resources of various types. Returns false
420 // if there is a problem in writing any resource to disk. setup.exe resource
421 // can come in one of three possible forms:
422 // - Resource type 'B7', compressed using LZMA (*.7z)
423 // - Resource type 'BL', compressed using LZ (*.ex_)
424 // - Resource type 'BN', uncompressed (*.exe)
425 // If setup.exe is present in more than one form, the precedence order is
427 // For more details see chrome/tools/build/win/create_installer_archive.py.
428 // For component builds (where setup.ex_ is always used), all files stored as
429 // uncompressed 'BN' resources are also extracted. This is generally the set of
430 // DLLs/resources needed by setup.exe to run.
431 ProcessExitCode
UnpackBinaryResources(const Configuration
& configuration
,
432 HMODULE module
, const wchar_t* base_path
,
433 PathString
* archive_path
,
434 PathString
* setup_path
) {
435 // Generate the setup.exe path where we patch/uncompress setup resource.
436 PathString setup_dest_path
;
437 if (!setup_dest_path
.assign(base_path
) ||
438 !setup_dest_path
.append(kSetupExe
))
439 return PATH_STRING_OVERFLOW
;
441 // Prepare the input to OnResourceFound method that needs a location where
442 // it will write all the resources.
449 // Get the resources of type 'B7' (7zip archive).
450 // We need a chrome archive to do the installation. So if there
451 // is a problem in fetching B7 resource, just return an error.
452 if (!::EnumResourceNames(module
, kLZMAResourceType
, OnResourceFound
,
453 reinterpret_cast<LONG_PTR
>(&context
)) ||
454 archive_path
->length() == 0)
455 return UNABLE_TO_EXTRACT_CHROME_ARCHIVE
;
457 ProcessExitCode exit_code
= SUCCESS_EXIT_CODE
;
459 // If we found setup 'B7' resource (used for differential updates), handle
460 // it. Note that this is only for Chrome; Chromium installs are always
462 if (setup_path
->length() > 0) {
463 CommandString cmd_line
;
465 // Get the path to setup.exe first.
466 exit_code
= GetPreviousSetupExePath(configuration
, exe_path
.get(),
467 exe_path
.capacity());
468 if (exit_code
== SUCCESS_EXIT_CODE
) {
469 if (!cmd_line
.append(exe_path
.get()) ||
470 !cmd_line
.append(L
" --") ||
471 !cmd_line
.append(kCmdUpdateSetupExe
) ||
472 !cmd_line
.append(L
"=\"") ||
473 !cmd_line
.append(setup_path
->get()) ||
474 !cmd_line
.append(L
"\" --") ||
475 !cmd_line
.append(kCmdNewSetupExe
) ||
476 !cmd_line
.append(L
"=\"") ||
477 !cmd_line
.append(setup_dest_path
.get()) ||
478 !cmd_line
.append(L
"\"")) {
479 exit_code
= COMMAND_STRING_OVERFLOW
;
483 // Get any command line option specified for mini_installer and pass them
484 // on to setup.exe. This is important since switches such as
485 // --multi-install and --chrome-frame affect where setup.exe will write
486 // installer results for consumption by Google Update.
487 AppendCommandLineFlags(configuration
, &cmd_line
);
489 if (exit_code
== SUCCESS_EXIT_CODE
)
490 exit_code
= RunProcessAndWait(exe_path
.get(), cmd_line
.get());
492 if (exit_code
!= SUCCESS_EXIT_CODE
)
493 DeleteFile(setup_path
->get());
494 else if (!setup_path
->assign(setup_dest_path
.get()))
495 exit_code
= PATH_STRING_OVERFLOW
;
500 // setup.exe wasn't sent as 'B7', lets see if it was sent as 'BL'
501 // (compressed setup).
502 if (!::EnumResourceNames(module
, kLZCResourceType
, OnResourceFound
,
503 reinterpret_cast<LONG_PTR
>(&context
)) &&
504 ::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND
)
505 return UNABLE_TO_EXTRACT_SETUP_B7
;
507 if (setup_path
->length() > 0) {
508 // Uncompress LZ compressed resource. Setup is packed with 'MSCF'
509 // as opposed to old DOS way of 'SZDD'. Hence we don't use LZCopy.
510 bool success
= mini_installer::Expand(setup_path
->get(),
511 setup_dest_path
.get());
512 ::DeleteFile(setup_path
->get());
514 if (!setup_path
->assign(setup_dest_path
.get())) {
515 ::DeleteFile(setup_dest_path
.get());
516 exit_code
= PATH_STRING_OVERFLOW
;
519 exit_code
= UNABLE_TO_EXTRACT_SETUP_EXE
;
522 #if defined(COMPONENT_BUILD)
523 // Extract the (uncompressed) modules required by setup.exe.
524 if (!::EnumResourceNames(module
, kBinResourceType
, WriteResourceToDirectory
,
525 reinterpret_cast<LONG_PTR
>(base_path
)))
526 return UNABLE_TO_EXTRACT_SETUP
;
532 // setup.exe still not found. So finally check if it was sent as 'BN'
533 // (uncompressed setup).
534 // TODO(tommi): We don't need BN anymore so let's remove it (and remove
535 // it from create_installer_archive.py).
536 if (!::EnumResourceNames(module
, kBinResourceType
, OnResourceFound
,
537 reinterpret_cast<LONG_PTR
>(&context
)) &&
538 ::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND
)
539 return UNABLE_TO_EXTRACT_SETUP_BN
;
541 if (setup_path
->length() > 0) {
542 if (setup_path
->comparei(setup_dest_path
.get()) != 0) {
543 if (!::MoveFileEx(setup_path
->get(), setup_dest_path
.get(),
544 MOVEFILE_COPY_ALLOWED
| MOVEFILE_REPLACE_EXISTING
)) {
545 ::DeleteFile(setup_path
->get());
547 } else if (!setup_path
->assign(setup_dest_path
.get())) {
548 ::DeleteFile(setup_dest_path
.get());
553 if (setup_path
->length() == 0)
554 exit_code
= UNABLE_TO_EXTRACT_SETUP
;
559 // Executes setup.exe, waits for it to finish and returns the exit code.
560 ProcessExitCode
RunSetup(const Configuration
& configuration
,
561 const wchar_t* archive_path
,
562 const wchar_t* setup_path
) {
563 // There could be three full paths in the command line for setup.exe (path
564 // to exe itself, path to archive and path to log file), so we declare
565 // total size as three + one additional to hold command line options.
566 CommandString cmd_line
;
568 // Get the path to setup.exe first.
569 if (::lstrlen(setup_path
) > 0) {
570 if (!cmd_line
.assign(L
"\"") ||
571 !cmd_line
.append(setup_path
) ||
572 !cmd_line
.append(L
"\""))
573 return COMMAND_STRING_OVERFLOW
;
575 ProcessExitCode exit_code
= GetPreviousSetupExePath(
576 configuration
, cmd_line
.get(), cmd_line
.capacity());
577 if (exit_code
!= SUCCESS_EXIT_CODE
)
581 // Append the command line param for chrome archive file.
582 if (!cmd_line
.append(L
" --") ||
583 #if defined(COMPONENT_BUILD)
584 // For faster developer turnaround, the component build generates
585 // uncompressed archives.
586 !cmd_line
.append(kCmdUncompressedArchive
) ||
588 !cmd_line
.append(kCmdInstallArchive
) ||
590 !cmd_line
.append(L
"=\"") ||
591 !cmd_line
.append(archive_path
) ||
592 !cmd_line
.append(L
"\""))
593 return COMMAND_STRING_OVERFLOW
;
595 // Append the command line param for chrome previous version.
596 if (configuration
.previous_version() &&
597 (!cmd_line
.append(L
" --") ||
598 !cmd_line
.append(kCmdPreviousVersion
) ||
599 !cmd_line
.append(L
"=\"") ||
600 !cmd_line
.append(configuration
.previous_version()) ||
601 !cmd_line
.append(L
"\""))) {
602 return COMMAND_STRING_OVERFLOW
;
605 // Get any command line option specified for mini_installer and pass them
607 AppendCommandLineFlags(configuration
, &cmd_line
);
609 return RunProcessAndWait(NULL
, cmd_line
.get());
612 // Deletes given files and working dir.
613 void DeleteExtractedFiles(const wchar_t* base_path
,
614 const wchar_t* archive_path
,
615 const wchar_t* setup_path
) {
616 ::DeleteFile(archive_path
);
617 ::DeleteFile(setup_path
);
618 // Delete the temp dir (if it is empty, otherwise fail).
619 ::RemoveDirectory(base_path
);
622 // Creates a temporary directory under |base_path| and returns the full path
623 // of created directory in |work_dir|. If successful return true, otherwise
624 // false. When successful, the returned |work_dir| will always have a trailing
625 // backslash and this function requires that |base_path| always includes a
626 // trailing backslash as well.
627 // We do not use GetTempFileName here to avoid running into AV software that
628 // might hold on to the temp file as soon as we create it and then we can't
629 // delete it and create a directory in its place. So, we use our own mechanism
630 // for creating a directory with a hopefully-unique name. In the case of a
631 // collision, we retry a few times with a new name before failing.
632 bool CreateWorkDir(const wchar_t* base_path
, PathString
* work_dir
) {
633 if (!work_dir
->assign(base_path
) || !work_dir
->append(kTempPrefix
))
636 // Store the location where we'll append the id.
637 size_t end
= work_dir
->length();
639 // Check if we'll have enough buffer space to continue.
640 // The name of the directory will use up 11 chars and then we need to append
641 // the trailing backslash and a terminator. We've already added the prefix
642 // to the buffer, so let's just make sure we've got enough space for the rest.
643 if ((work_dir
->capacity() - end
) < (_countof("fffff.tmp") + 1))
646 // Generate a unique id. We only use the lowest 20 bits, so take the top
647 // 12 bits and xor them with the lower bits.
648 DWORD id
= ::GetTickCount();
651 int max_attempts
= 10;
652 while (max_attempts
--) {
653 // This converts 'id' to a string in the format "78563412" on windows
654 // because of little endianness, but we don't care since it's just
656 if (!HexEncode(&id
, sizeof(id
), work_dir
->get() + end
,
657 work_dir
->capacity() - end
)) {
661 // We only want the first 5 digits to remain within the 8.3 file name
662 // format (compliant with previous implementation).
663 work_dir
->truncate_at(end
+ 5);
665 // for consistency with the previous implementation which relied on
666 // GetTempFileName, we append the .tmp extension.
667 work_dir
->append(L
".tmp");
668 if (::CreateDirectory(work_dir
->get(), NULL
)) {
669 // Yay! Now let's just append the backslash and we're done.
670 return work_dir
->append(L
"\\");
672 ++id
; // Try a different name.
678 // Creates and returns a temporary directory in |work_dir| that can be used to
679 // extract mini_installer payload. |work_dir| ends with a path separator.
680 bool GetWorkDir(HMODULE module
, PathString
* work_dir
) {
681 PathString base_path
;
682 DWORD len
= ::GetTempPath(base_path
.capacity(), base_path
.get());
683 if (!len
|| len
>= base_path
.capacity() ||
684 !CreateWorkDir(base_path
.get(), work_dir
)) {
685 // Problem creating the work dir under TEMP path, so try using the
686 // current directory as the base path.
687 len
= ::GetModuleFileName(module
, base_path
.get(), base_path
.capacity());
688 if (len
>= base_path
.capacity() || !len
)
689 return false; // Can't even get current directory? Return an error.
691 wchar_t* name
= GetNameFromPathExt(base_path
.get(), len
);
692 if (name
== base_path
.get())
693 return false; // There was no directory in the string! Bail out.
697 return CreateWorkDir(base_path
.get(), work_dir
);
702 // Returns true for ".." and "." directories.
703 bool IsCurrentOrParentDirectory(const wchar_t* dir
) {
706 (dir
[1] == L
'\0' || (dir
[1] == L
'.' && dir
[2] == L
'\0'));
709 // Best effort directory tree deletion including the directory specified
710 // by |path|, which must not end in a separator.
711 // The |path| argument is writable so that each recursion can use the same
712 // buffer as was originally allocated for the path. The path will be unchanged
714 void RecursivelyDeleteDirectory(PathString
* path
) {
715 // |path| will never have a trailing backslash.
716 size_t end
= path
->length();
717 if (!path
->append(L
"\\*.*"))
720 WIN32_FIND_DATA find_data
= {0};
721 HANDLE find
= ::FindFirstFile(path
->get(), &find_data
);
722 if (find
!= INVALID_HANDLE_VALUE
) {
724 // Use the short name if available to make the most of our buffer.
725 const wchar_t* name
= find_data
.cAlternateFileName
[0] ?
726 find_data
.cAlternateFileName
: find_data
.cFileName
;
727 if (IsCurrentOrParentDirectory(name
))
730 path
->truncate_at(end
+ 1); // Keep the trailing backslash.
731 if (!path
->append(name
))
732 continue; // Continue in spite of too long names.
734 if (find_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
735 RecursivelyDeleteDirectory(path
);
737 ::DeleteFile(path
->get());
739 } while (::FindNextFile(find
, &find_data
));
743 // Restore the path and delete the directory before we return.
744 path
->truncate_at(end
);
745 ::RemoveDirectory(path
->get());
748 // Enumerates subdirectories of |parent_dir| and deletes all subdirectories
749 // that match with a given |prefix|. |parent_dir| must have a trailing
751 // The process is done on a best effort basis, so conceivably there might
752 // still be matches left when the function returns.
753 void DeleteDirectoriesWithPrefix(const wchar_t* parent_dir
,
754 const wchar_t* prefix
) {
755 // |parent_dir| is guaranteed to always have a trailing backslash.
757 if (!spec
.assign(parent_dir
) || !spec
.append(prefix
) || !spec
.append(L
"*.*"))
760 WIN32_FIND_DATA find_data
= {0};
761 HANDLE find
= ::FindFirstFileEx(spec
.get(), FindExInfoStandard
, &find_data
,
762 FindExSearchLimitToDirectories
, NULL
, 0);
763 if (find
== INVALID_HANDLE_VALUE
)
768 if (find_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
769 // Use the short name if available to make the most of our buffer.
770 const wchar_t* name
= find_data
.cAlternateFileName
[0] ?
771 find_data
.cAlternateFileName
: find_data
.cFileName
;
772 if (IsCurrentOrParentDirectory(name
))
774 if (path
.assign(parent_dir
) && path
.append(name
))
775 RecursivelyDeleteDirectory(&path
);
777 } while (::FindNextFile(find
, &find_data
));
781 // Attempts to free up space by deleting temp directories that previous
782 // installer runs have failed to clean up.
783 void DeleteOldChromeTempDirectories() {
784 static const wchar_t* const kDirectoryPrefixes
[] = {
786 L
"chrome_" // Previous installers created directories with this prefix
787 // and there are still some lying around.
791 // GetTempPath always returns a path with a trailing backslash.
792 DWORD len
= ::GetTempPath(temp
.capacity(), temp
.get());
793 // GetTempPath returns 0 or number of chars copied, not including the
795 if (!len
|| len
>= temp
.capacity())
798 for (int i
= 0; i
< _countof(kDirectoryPrefixes
); ++i
) {
799 DeleteDirectoriesWithPrefix(temp
.get(), kDirectoryPrefixes
[i
]);
803 // Checks the command line for specific mini installer flags.
804 // If the function returns true, the command line has been processed and all
805 // required actions taken. The installer must exit and return the returned
807 bool ProcessNonInstallOperations(const Configuration
& configuration
,
808 ProcessExitCode
* exit_code
) {
809 switch (configuration
.operation()) {
810 case Configuration::CLEANUP
:
811 // Cleanup has already taken place in DeleteOldChromeTempDirectories at
812 // this point, so just tell our caller to exit early.
813 *exit_code
= SUCCESS_EXIT_CODE
;
821 // Returns true if we should delete the temp files we create (default).
822 // Returns false iff the user has manually created a ChromeInstallerCleanup
823 // string value in the registry under HKCU\\Software\\[Google|Chromium]
824 // and set its value to "0". That explicitly forbids the mini installer from
825 // deleting these files.
826 // Support for this has been publicly mentioned in troubleshooting tips so
827 // we continue to support it.
828 bool ShouldDeleteExtractedFiles() {
829 wchar_t value
[2] = {0};
830 if (ReadValueFromRegistry(HKEY_CURRENT_USER
, kCleanupRegistryKey
,
831 kCleanupRegistryValue
, value
, _countof(value
)) &&
839 // Main function. First gets a working dir, unpacks the resources and finally
840 // executes setup.exe to do the install/upgrade.
841 ProcessExitCode
WMain(HMODULE module
) {
842 // Always start with deleting potential leftovers from previous installations.
843 // This can make the difference between success and failure. We've seen
844 // many installations out in the field fail due to out of disk space problems
845 // so this could buy us some space.
846 DeleteOldChromeTempDirectories();
848 ProcessExitCode exit_code
= SUCCESS_EXIT_CODE
;
850 // Parse configuration from the command line and resources.
851 Configuration configuration
;
852 if (!configuration
.Initialize(module
))
853 return GENERIC_INITIALIZATION_FAILURE
;
855 // If the --cleanup switch was specified on the command line, then that means
856 // we should only do the cleanup and then exit.
857 if (ProcessNonInstallOperations(configuration
, &exit_code
))
860 // First get a path where we can extract payload
861 PathString base_path
;
862 if (!GetWorkDir(module
, &base_path
))
863 return UNABLE_TO_GET_WORK_DIRECTORY
;
865 #if defined(GOOGLE_CHROME_BUILD)
866 // Set the magic suffix in registry to try full installer next time. We ignore
867 // any errors here and we try to set the suffix for user level unless
868 // --system-level is on the command line in which case we set it for system
869 // level instead. This only applies to the Google Chrome distribution.
870 SetInstallerFlags(configuration
);
873 PathString archive_path
;
874 PathString setup_path
;
875 exit_code
= UnpackBinaryResources(configuration
, module
, base_path
.get(),
876 &archive_path
, &setup_path
);
878 // While unpacking the binaries, we paged in a whole bunch of memory that
879 // we don't need anymore. Let's give it back to the pool before running
881 ::SetProcessWorkingSetSize(::GetCurrentProcess(), -1, -1);
883 if (exit_code
== SUCCESS_EXIT_CODE
)
884 exit_code
= RunSetup(configuration
, archive_path
.get(), setup_path
.get());
886 if (ShouldDeleteExtractedFiles())
887 DeleteExtractedFiles(base_path
.get(), archive_path
.get(), setup_path
.get());
892 } // namespace mini_installer
894 int MainEntryPoint() {
895 mini_installer::ProcessExitCode result
=
896 mini_installer::WMain(::GetModuleHandle(NULL
));
897 ::ExitProcess(result
);
900 // VC Express editions don't come with the memset CRT obj file and linking to
901 // the obj files between versions becomes a bit problematic. Therefore,
902 // simply implement memset.
904 // This also avoids having to explicitly set the __sse2_available hack when
905 // linking with both the x64 and x86 obj files which is required when not
906 // linking with the std C lib in certain instances (including Chromium) with
907 // MSVC. __sse2_available determines whether to use SSE2 intructions with
908 // std C lib routines, and is set by MSVC's std C lib implementation normally.
910 #pragma function(memset)
911 void* memset(void* dest
, int c
, size_t count
) {
914 *reinterpret_cast<char*>(dest
) = static_cast<char>(c
);
915 dest
= reinterpret_cast<char*>(dest
) + 1;