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"
35 #include "chrome/installer/mini_installer/regkey.h"
37 namespace mini_installer
{
39 typedef StackString
<MAX_PATH
> PathString
;
40 typedef StackString
<MAX_PATH
* 4> CommandString
;
42 struct ProcessExitResult
{
46 explicit ProcessExitResult(DWORD exit
) : exit_code(exit
), windows_error(0) {}
47 ProcessExitResult(DWORD exit
, DWORD win
)
48 : exit_code(exit
), windows_error(win
) {
52 return exit_code
== SUCCESS_EXIT_CODE
;
56 // This structure passes data back and forth for the processing
57 // of resource callbacks.
59 // Input to the call back method. Specifies the dir to save resources.
60 const wchar_t* base_path
;
61 // First output from call back method. Full path of Chrome archive.
62 PathString
* chrome_resource_path
;
63 // Second output from call back method. Full path of Setup archive/exe.
64 PathString
* setup_resource_path
;
68 // Opens the Google Update ClientState key for the current install
69 // configuration. This includes locating the correct key in the face of
70 // multi-install. The flag will by default be written to HKCU, but if
71 // --system-level is included in the command line, it will be written to
73 bool OpenInstallStateKey(const Configuration
& configuration
, RegKey
* key
) {
75 configuration
.is_system_level() ? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
76 const wchar_t* app_guid
= configuration
.chrome_app_guid();
77 const REGSAM key_access
= KEY_QUERY_VALUE
| KEY_SET_VALUE
;
79 return OpenClientStateKey(root_key
, app_guid
, key_access
, key
);
82 // Writes install results into registry where it is read by Google Update.
83 // Don't write anything if there is already a result present, likely
84 // written by setup.exe.
85 void WriteInstallResults(const Configuration
& configuration
,
86 ProcessExitResult result
) {
87 #if defined(GOOGLE_CHROME_BUILD)
88 // Calls to setup.exe will write a "success" result if everything was good
89 // so we don't need to write anything from here.
90 if (result
.IsSuccess())
95 if (OpenInstallStateKey(configuration
, &key
)) {
96 if (key
.ReadDWValue(kInstallerResultRegistryValue
, &value
)
97 != ERROR_SUCCESS
|| value
== 0) {
98 key
.WriteDWValue(kInstallerResultRegistryValue
,
99 result
.exit_code
? 1 /* FAILED_CUSTOM_ERROR */
101 key
.WriteDWValue(kInstallerErrorRegistryValue
, result
.exit_code
);
102 key
.WriteDWValue(kInstallerExtraCode1RegistryValue
, result
.windows_error
);
109 // This function sets the flag in registry to indicate that Google Update
110 // should try full installer next time. If the current installer works, this
111 // flag is cleared by setup.exe at the end of install.
112 void SetInstallerFlags(const Configuration
& configuration
) {
114 StackString
<128> value
;
115 LONG ret
= ERROR_SUCCESS
;
117 if (!OpenInstallStateKey(configuration
, &key
))
120 ret
= key
.ReadSZValue(kApRegistryValue
, value
.get(), value
.capacity());
122 // The conditions below are handling two cases:
123 // 1. When ap value is present, we want to add the required tag only if it is
125 // 2. When ap value is missing, we are going to create it with the required
127 if ((ret
== ERROR_SUCCESS
) || (ret
== ERROR_FILE_NOT_FOUND
)) {
128 if (ret
== ERROR_FILE_NOT_FOUND
)
131 if (!StrEndsWith(value
.get(), kFullInstallerSuffix
) &&
132 value
.append(kFullInstallerSuffix
)) {
133 key
.WriteSZValue(kApRegistryValue
, value
.get());
138 // Gets the setup.exe path from Registry by looking at the value of Uninstall
139 // string. |size| is measured in wchar_t units.
140 ProcessExitResult
GetSetupExePathForAppGuid(bool system_level
,
141 const wchar_t* app_guid
,
142 const wchar_t* previous_version
,
145 const HKEY root_key
= system_level
? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
147 if (!OpenClientStateKey(root_key
, app_guid
, KEY_QUERY_VALUE
, &key
))
148 return ProcessExitResult(UNABLE_TO_FIND_REGISTRY_KEY
);
149 DWORD result
= key
.ReadSZValue(kUninstallRegistryValue
, path
, size
);
150 if (result
!= ERROR_SUCCESS
)
151 return ProcessExitResult(UNABLE_TO_FIND_REGISTRY_KEY
, result
);
153 // Check that the path to the existing installer includes the expected
154 // version number. It's not necessary for accuracy to verify before/after
156 if (!SearchStringI(path
, previous_version
))
157 return ProcessExitResult(PATCH_NOT_FOR_INSTALLED_VERSION
);
159 return ProcessExitResult(SUCCESS_EXIT_CODE
);
162 // Gets the path to setup.exe of the previous version. The overall path is found
163 // in the Uninstall string in the registry. A previous version number specified
164 // in |configuration| is used if available. |size| is measured in wchar_t units.
165 ProcessExitResult
GetPreviousSetupExePath(const Configuration
& configuration
,
168 bool system_level
= configuration
.is_system_level();
169 const wchar_t* previous_version
= configuration
.previous_version();
170 ProcessExitResult exit_code
= ProcessExitResult(GENERIC_ERROR
);
172 // If this is a multi install, first try looking in the binaries for the path.
173 if (configuration
.is_multi_install()) {
174 exit_code
= GetSetupExePathForAppGuid(
175 system_level
, google_update::kMultiInstallAppGuid
, previous_version
,
179 // Failing that, look in Chrome Frame's client state key if --chrome-frame was
181 if (!exit_code
.IsSuccess() && configuration
.has_chrome_frame()) {
182 exit_code
= GetSetupExePathForAppGuid(
183 system_level
, google_update::kChromeFrameAppGuid
, previous_version
,
187 // Make a last-ditch effort to look in the Chrome client state key.
188 if (!exit_code
.IsSuccess()) {
189 exit_code
= GetSetupExePathForAppGuid(
190 system_level
, configuration
.chrome_app_guid(), previous_version
,
197 // Calls CreateProcess with good default parameters and waits for the process to
198 // terminate returning the process exit code. |exit_code|, if non-NULL, is
199 // populated with the process exit code.
200 ProcessExitResult
RunProcessAndWait(const wchar_t* exe_path
, wchar_t* cmdline
) {
201 STARTUPINFOW si
= {sizeof(si
)};
202 PROCESS_INFORMATION pi
= {0};
203 if (!::CreateProcess(exe_path
, cmdline
, NULL
, NULL
, FALSE
, CREATE_NO_WINDOW
,
204 NULL
, NULL
, &si
, &pi
)) {
205 return ProcessExitResult(COULD_NOT_CREATE_PROCESS
, ::GetLastError());
208 ::CloseHandle(pi
.hThread
);
210 DWORD exit_code
= SUCCESS_EXIT_CODE
;
211 DWORD wr
= ::WaitForSingleObject(pi
.hProcess
, INFINITE
);
212 if (WAIT_OBJECT_0
!= wr
|| !::GetExitCodeProcess(pi
.hProcess
, &exit_code
)) {
213 // Note: We've assumed that WAIT_OBJCT_0 != wr means a failure. The call
214 // could return a different object but since we never spawn more than one
215 // sub-process at a time that case should never happen.
216 return ProcessExitResult(WAIT_FOR_PROCESS_FAILED
, ::GetLastError());
219 ::CloseHandle(pi
.hProcess
);
221 return ProcessExitResult(exit_code
);
224 // Appends any command line params passed to mini_installer to the given buffer
225 // so that they can be passed on to setup.exe.
226 // |buffer| is unchanged in case of error.
227 void AppendCommandLineFlags(const Configuration
& configuration
,
228 CommandString
* buffer
) {
229 PathString full_exe_path
;
230 size_t len
= ::GetModuleFileName(
231 NULL
, full_exe_path
.get(), static_cast<DWORD
>(full_exe_path
.capacity()));
232 if (!len
|| len
>= full_exe_path
.capacity())
235 const wchar_t* exe_name
=
236 GetNameFromPathExt(full_exe_path
.get(), static_cast<DWORD
>(len
));
238 // - configuration.program() returns the first command line argument
239 // passed into the program (that the user probably typed in this case).
240 // "mini_installer.exe"
242 // "out\Release\mini_installer"
243 // - |exe_name| is the executable file of the current process.
244 // "mini_installer.exe"
246 // Note that there are three possibilities to handle here.
247 // Receive a cmdline containing:
248 // 1) executable name WITH extension
249 // 2) executable name with NO extension
250 // 3) NO executable name as part of cmdline
251 const wchar_t* cmd_to_append
= L
"";
252 const wchar_t* arg0
= configuration
.program();
255 const wchar_t* arg0_base_name
= GetNameFromPathExt(arg0
, ::lstrlen(arg0
));
256 if (!StrStartsWith(exe_name
, arg0_base_name
)) {
257 // State 3: NO executable name as part of cmdline.
258 buffer
->append(L
" ");
259 cmd_to_append
= configuration
.command_line();
260 } else if (configuration
.argument_count() > 1) {
261 // State 1 or 2: Executable name is in cmdline.
262 // - Append everything AFTER the executable name.
263 // (Using arg0_base_name here to make sure to match with or without
264 // extension. Then move to the space following the token.)
265 const wchar_t* tmp
= SearchStringI(configuration
.command_line(),
267 tmp
= SearchStringI(tmp
, L
" ");
271 buffer
->append(cmd_to_append
);
275 // Windows defined callback used in the EnumResourceNames call. For each
276 // matching resource found, the callback is invoked and at this point we write
277 // it to disk. We expect resource names to start with 'chrome' or 'setup'. Any
278 // other name is treated as an error.
279 BOOL CALLBACK
OnResourceFound(HMODULE module
, const wchar_t* type
,
280 wchar_t* name
, LONG_PTR context
) {
284 Context
* ctx
= reinterpret_cast<Context
*>(context
);
286 PEResource
resource(name
, type
, module
);
287 if ((!resource
.IsValid()) ||
288 (resource
.Size() < 1) ||
289 (resource
.Size() > kMaxResourceSize
)) {
293 PathString full_path
;
294 if (!full_path
.assign(ctx
->base_path
) ||
295 !full_path
.append(name
) ||
296 !resource
.WriteToDisk(full_path
.get()))
299 if (StrStartsWith(name
, kChromeArchivePrefix
)) {
300 if (!ctx
->chrome_resource_path
->assign(full_path
.get()))
302 } else if (StrStartsWith(name
, kSetupPrefix
)) {
303 if (!ctx
->setup_resource_path
->assign(full_path
.get()))
306 // Resources should either start with 'chrome' or 'setup'. We don't handle
314 #if defined(COMPONENT_BUILD)
315 // An EnumResNameProc callback that writes the resource |name| to disk in the
316 // directory |base_path_ptr| (which must end with a path separator).
317 BOOL CALLBACK
WriteResourceToDirectory(HMODULE module
,
320 LONG_PTR base_path_ptr
) {
321 const wchar_t* base_path
= reinterpret_cast<const wchar_t*>(base_path_ptr
);
322 PathString full_path
;
324 PEResource
resource(name
, type
, module
);
325 return (resource
.IsValid() &&
326 full_path
.assign(base_path
) &&
327 full_path
.append(name
) &&
328 resource
.WriteToDisk(full_path
.get()));
332 // Finds and writes to disk resources of various types. Returns false
333 // if there is a problem in writing any resource to disk. setup.exe resource
334 // can come in one of three possible forms:
335 // - Resource type 'B7', compressed using LZMA (*.7z)
336 // - Resource type 'BL', compressed using LZ (*.ex_)
337 // - Resource type 'BN', uncompressed (*.exe)
338 // If setup.exe is present in more than one form, the precedence order is
340 // For more details see chrome/tools/build/win/create_installer_archive.py.
341 // For component builds (where setup.ex_ is always used), all files stored as
342 // uncompressed 'BN' resources are also extracted. This is generally the set of
343 // DLLs/resources needed by setup.exe to run.
344 ProcessExitResult
UnpackBinaryResources(const Configuration
& configuration
,
345 HMODULE module
, const wchar_t* base_path
,
346 PathString
* archive_path
,
347 PathString
* setup_path
) {
348 // Generate the setup.exe path where we patch/uncompress setup resource.
349 PathString setup_dest_path
;
350 if (!setup_dest_path
.assign(base_path
) ||
351 !setup_dest_path
.append(kSetupExe
))
352 return ProcessExitResult(PATH_STRING_OVERFLOW
);
354 // Prepare the input to OnResourceFound method that needs a location where
355 // it will write all the resources.
362 // Get the resources of type 'B7' (7zip archive).
363 // We need a chrome archive to do the installation. So if there
364 // is a problem in fetching B7 resource, just return an error.
365 if (!::EnumResourceNames(module
, kLZMAResourceType
, OnResourceFound
,
366 reinterpret_cast<LONG_PTR
>(&context
))) {
367 return ProcessExitResult(UNABLE_TO_EXTRACT_CHROME_ARCHIVE
,
370 if (archive_path
->length() == 0) {
371 return ProcessExitResult(UNABLE_TO_EXTRACT_CHROME_ARCHIVE
);
374 ProcessExitResult exit_code
= ProcessExitResult(SUCCESS_EXIT_CODE
);
376 // If we found setup 'B7' resource (used for differential updates), handle
377 // it. Note that this is only for Chrome; Chromium installs are always
379 if (setup_path
->length() > 0) {
380 CommandString cmd_line
;
382 // Get the path to setup.exe first.
383 exit_code
= GetPreviousSetupExePath(configuration
, exe_path
.get(),
384 exe_path
.capacity());
385 if (exit_code
.IsSuccess()) {
386 if (!cmd_line
.append(exe_path
.get()) ||
387 !cmd_line
.append(L
" --") ||
388 !cmd_line
.append(kCmdUpdateSetupExe
) ||
389 !cmd_line
.append(L
"=\"") ||
390 !cmd_line
.append(setup_path
->get()) ||
391 !cmd_line
.append(L
"\" --") ||
392 !cmd_line
.append(kCmdNewSetupExe
) ||
393 !cmd_line
.append(L
"=\"") ||
394 !cmd_line
.append(setup_dest_path
.get()) ||
395 !cmd_line
.append(L
"\"")) {
396 exit_code
= ProcessExitResult(COMMAND_STRING_OVERFLOW
);
400 // Get any command line option specified for mini_installer and pass them
401 // on to setup.exe. This is important since switches such as
402 // --multi-install and --chrome-frame affect where setup.exe will write
403 // installer results for consumption by Google Update.
404 AppendCommandLineFlags(configuration
, &cmd_line
);
406 if (exit_code
.IsSuccess())
407 exit_code
= RunProcessAndWait(exe_path
.get(), cmd_line
.get());
409 if (!exit_code
.IsSuccess())
410 DeleteFile(setup_path
->get());
411 else if (!setup_path
->assign(setup_dest_path
.get()))
412 exit_code
= ProcessExitResult(PATH_STRING_OVERFLOW
);
417 // setup.exe wasn't sent as 'B7', lets see if it was sent as 'BL'
418 // (compressed setup).
419 if (!::EnumResourceNames(module
, kLZCResourceType
, OnResourceFound
,
420 reinterpret_cast<LONG_PTR
>(&context
)) &&
421 ::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND
) {
422 return ProcessExitResult(UNABLE_TO_EXTRACT_SETUP_B7
, ::GetLastError());
425 if (setup_path
->length() > 0) {
426 // Uncompress LZ compressed resource. Setup is packed with 'MSCF'
427 // as opposed to old DOS way of 'SZDD'. Hence we don't use LZCopy.
428 bool success
= mini_installer::Expand(setup_path
->get(),
429 setup_dest_path
.get());
430 ::DeleteFile(setup_path
->get());
432 if (!setup_path
->assign(setup_dest_path
.get())) {
433 ::DeleteFile(setup_dest_path
.get());
434 exit_code
= ProcessExitResult(PATH_STRING_OVERFLOW
);
437 exit_code
= ProcessExitResult(UNABLE_TO_EXTRACT_SETUP_EXE
);
440 #if defined(COMPONENT_BUILD)
441 // Extract the (uncompressed) modules required by setup.exe.
442 if (!::EnumResourceNames(module
, kBinResourceType
, WriteResourceToDirectory
,
443 reinterpret_cast<LONG_PTR
>(base_path
))) {
444 return ProcessExitResult(UNABLE_TO_EXTRACT_SETUP
, ::GetLastError());
451 // setup.exe still not found. So finally check if it was sent as 'BN'
452 // (uncompressed setup).
453 // TODO(tommi): We don't need BN anymore so let's remove it (and remove
454 // it from create_installer_archive.py).
455 if (!::EnumResourceNames(module
, kBinResourceType
, OnResourceFound
,
456 reinterpret_cast<LONG_PTR
>(&context
)) &&
457 ::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND
) {
458 return ProcessExitResult(UNABLE_TO_EXTRACT_SETUP_BN
, ::GetLastError());
461 if (setup_path
->length() > 0) {
462 if (setup_path
->comparei(setup_dest_path
.get()) != 0) {
463 if (!::MoveFileEx(setup_path
->get(), setup_dest_path
.get(),
464 MOVEFILE_COPY_ALLOWED
| MOVEFILE_REPLACE_EXISTING
)) {
465 ::DeleteFile(setup_path
->get());
467 } else if (!setup_path
->assign(setup_dest_path
.get())) {
468 ::DeleteFile(setup_dest_path
.get());
473 if (setup_path
->length() == 0)
474 exit_code
= ProcessExitResult(UNABLE_TO_EXTRACT_SETUP
);
479 // Executes setup.exe, waits for it to finish and returns the exit code.
480 ProcessExitResult
RunSetup(const Configuration
& configuration
,
481 const wchar_t* archive_path
,
482 const wchar_t* setup_path
) {
483 // There could be three full paths in the command line for setup.exe (path
484 // to exe itself, path to archive and path to log file), so we declare
485 // total size as three + one additional to hold command line options.
486 CommandString cmd_line
;
488 // Get the path to setup.exe first.
489 if (::lstrlen(setup_path
) > 0) {
490 if (!cmd_line
.assign(L
"\"") ||
491 !cmd_line
.append(setup_path
) ||
492 !cmd_line
.append(L
"\""))
493 return ProcessExitResult(COMMAND_STRING_OVERFLOW
);
495 ProcessExitResult exit_code
= GetPreviousSetupExePath(
496 configuration
, cmd_line
.get(), cmd_line
.capacity());
497 if (!exit_code
.IsSuccess())
501 // Append the command line param for chrome archive file.
502 if (!cmd_line
.append(L
" --") ||
503 #if defined(COMPONENT_BUILD)
504 // For faster developer turnaround, the component build generates
505 // uncompressed archives.
506 !cmd_line
.append(kCmdUncompressedArchive
) ||
508 !cmd_line
.append(kCmdInstallArchive
) ||
510 !cmd_line
.append(L
"=\"") ||
511 !cmd_line
.append(archive_path
) ||
512 !cmd_line
.append(L
"\""))
513 return ProcessExitResult(COMMAND_STRING_OVERFLOW
);
515 // Append the command line param for chrome previous version.
516 if (configuration
.previous_version() &&
517 (!cmd_line
.append(L
" --") ||
518 !cmd_line
.append(kCmdPreviousVersion
) ||
519 !cmd_line
.append(L
"=\"") ||
520 !cmd_line
.append(configuration
.previous_version()) ||
521 !cmd_line
.append(L
"\""))) {
522 return ProcessExitResult(COMMAND_STRING_OVERFLOW
);
525 // Get any command line option specified for mini_installer and pass them
527 AppendCommandLineFlags(configuration
, &cmd_line
);
529 return RunProcessAndWait(NULL
, cmd_line
.get());
532 // Deletes given files and working dir.
533 void DeleteExtractedFiles(const wchar_t* base_path
,
534 const wchar_t* archive_path
,
535 const wchar_t* setup_path
) {
536 ::DeleteFile(archive_path
);
537 ::DeleteFile(setup_path
);
538 // Delete the temp dir (if it is empty, otherwise fail).
539 ::RemoveDirectory(base_path
);
542 // Creates a temporary directory under |base_path| and returns the full path
543 // of created directory in |work_dir|. If successful return true, otherwise
544 // false. When successful, the returned |work_dir| will always have a trailing
545 // backslash and this function requires that |base_path| always includes a
546 // trailing backslash as well.
547 // We do not use GetTempFileName here to avoid running into AV software that
548 // might hold on to the temp file as soon as we create it and then we can't
549 // delete it and create a directory in its place. So, we use our own mechanism
550 // for creating a directory with a hopefully-unique name. In the case of a
551 // collision, we retry a few times with a new name before failing.
552 bool CreateWorkDir(const wchar_t* base_path
, PathString
* work_dir
) {
553 if (!work_dir
->assign(base_path
) || !work_dir
->append(kTempPrefix
))
556 // Store the location where we'll append the id.
557 size_t end
= work_dir
->length();
559 // Check if we'll have enough buffer space to continue.
560 // The name of the directory will use up 11 chars and then we need to append
561 // the trailing backslash and a terminator. We've already added the prefix
562 // to the buffer, so let's just make sure we've got enough space for the rest.
563 if ((work_dir
->capacity() - end
) < (_countof("fffff.tmp") + 1))
566 // Generate a unique id. We only use the lowest 20 bits, so take the top
567 // 12 bits and xor them with the lower bits.
568 DWORD id
= ::GetTickCount();
571 int max_attempts
= 10;
572 while (max_attempts
--) {
573 // This converts 'id' to a string in the format "78563412" on windows
574 // because of little endianness, but we don't care since it's just
576 if (!HexEncode(&id
, sizeof(id
), work_dir
->get() + end
,
577 work_dir
->capacity() - end
)) {
581 // We only want the first 5 digits to remain within the 8.3 file name
582 // format (compliant with previous implementation).
583 work_dir
->truncate_at(end
+ 5);
585 // for consistency with the previous implementation which relied on
586 // GetTempFileName, we append the .tmp extension.
587 work_dir
->append(L
".tmp");
588 if (::CreateDirectory(work_dir
->get(), NULL
)) {
589 // Yay! Now let's just append the backslash and we're done.
590 return work_dir
->append(L
"\\");
592 ++id
; // Try a different name.
598 // Creates and returns a temporary directory in |work_dir| that can be used to
599 // extract mini_installer payload. |work_dir| ends with a path separator.
600 bool GetWorkDir(HMODULE module
, PathString
* work_dir
) {
601 PathString base_path
;
602 DWORD len
= ::GetTempPath(static_cast<DWORD
>(base_path
.capacity()),
604 if (!len
|| len
>= base_path
.capacity() ||
605 !CreateWorkDir(base_path
.get(), work_dir
)) {
606 // Problem creating the work dir under TEMP path, so try using the
607 // current directory as the base path.
608 len
= ::GetModuleFileName(module
, base_path
.get(),
609 static_cast<DWORD
>(base_path
.capacity()));
610 if (len
>= base_path
.capacity() || !len
)
611 return false; // Can't even get current directory? Return an error.
613 wchar_t* name
= GetNameFromPathExt(base_path
.get(), len
);
614 if (name
== base_path
.get())
615 return false; // There was no directory in the string! Bail out.
619 return CreateWorkDir(base_path
.get(), work_dir
);
624 // Returns true for ".." and "." directories.
625 bool IsCurrentOrParentDirectory(const wchar_t* dir
) {
628 (dir
[1] == L
'\0' || (dir
[1] == L
'.' && dir
[2] == L
'\0'));
631 // Best effort directory tree deletion including the directory specified
632 // by |path|, which must not end in a separator.
633 // The |path| argument is writable so that each recursion can use the same
634 // buffer as was originally allocated for the path. The path will be unchanged
636 void RecursivelyDeleteDirectory(PathString
* path
) {
637 // |path| will never have a trailing backslash.
638 size_t end
= path
->length();
639 if (!path
->append(L
"\\*.*"))
642 WIN32_FIND_DATA find_data
= {0};
643 HANDLE find
= ::FindFirstFile(path
->get(), &find_data
);
644 if (find
!= INVALID_HANDLE_VALUE
) {
646 // Use the short name if available to make the most of our buffer.
647 const wchar_t* name
= find_data
.cAlternateFileName
[0] ?
648 find_data
.cAlternateFileName
: find_data
.cFileName
;
649 if (IsCurrentOrParentDirectory(name
))
652 path
->truncate_at(end
+ 1); // Keep the trailing backslash.
653 if (!path
->append(name
))
654 continue; // Continue in spite of too long names.
656 if (find_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
657 RecursivelyDeleteDirectory(path
);
659 ::DeleteFile(path
->get());
661 } while (::FindNextFile(find
, &find_data
));
665 // Restore the path and delete the directory before we return.
666 path
->truncate_at(end
);
667 ::RemoveDirectory(path
->get());
670 // Enumerates subdirectories of |parent_dir| and deletes all subdirectories
671 // that match with a given |prefix|. |parent_dir| must have a trailing
673 // The process is done on a best effort basis, so conceivably there might
674 // still be matches left when the function returns.
675 void DeleteDirectoriesWithPrefix(const wchar_t* parent_dir
,
676 const wchar_t* prefix
) {
677 // |parent_dir| is guaranteed to always have a trailing backslash.
679 if (!spec
.assign(parent_dir
) || !spec
.append(prefix
) || !spec
.append(L
"*.*"))
682 WIN32_FIND_DATA find_data
= {0};
683 HANDLE find
= ::FindFirstFileEx(spec
.get(), FindExInfoStandard
, &find_data
,
684 FindExSearchLimitToDirectories
, NULL
, 0);
685 if (find
== INVALID_HANDLE_VALUE
)
690 if (find_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
691 // Use the short name if available to make the most of our buffer.
692 const wchar_t* name
= find_data
.cAlternateFileName
[0] ?
693 find_data
.cAlternateFileName
: find_data
.cFileName
;
694 if (IsCurrentOrParentDirectory(name
))
696 if (path
.assign(parent_dir
) && path
.append(name
))
697 RecursivelyDeleteDirectory(&path
);
699 } while (::FindNextFile(find
, &find_data
));
703 // Attempts to free up space by deleting temp directories that previous
704 // installer runs have failed to clean up.
705 void DeleteOldChromeTempDirectories() {
706 static const wchar_t* const kDirectoryPrefixes
[] = {
708 L
"chrome_" // Previous installers created directories with this prefix
709 // and there are still some lying around.
713 // GetTempPath always returns a path with a trailing backslash.
714 DWORD len
= ::GetTempPath(static_cast<DWORD
>(temp
.capacity()), temp
.get());
715 // GetTempPath returns 0 or number of chars copied, not including the
717 if (!len
|| len
>= temp
.capacity())
720 for (int i
= 0; i
< _countof(kDirectoryPrefixes
); ++i
) {
721 DeleteDirectoriesWithPrefix(temp
.get(), kDirectoryPrefixes
[i
]);
725 // Checks the command line for specific mini installer flags.
726 // If the function returns true, the command line has been processed and all
727 // required actions taken. The installer must exit and return the returned
729 bool ProcessNonInstallOperations(const Configuration
& configuration
,
730 ProcessExitResult
* exit_code
) {
731 switch (configuration
.operation()) {
732 case Configuration::CLEANUP
:
733 // Cleanup has already taken place in DeleteOldChromeTempDirectories at
734 // this point, so just tell our caller to exit early.
735 *exit_code
= ProcessExitResult(SUCCESS_EXIT_CODE
);
743 // Returns true if we should delete the temp files we create (default).
744 // Returns false iff the user has manually created a ChromeInstallerCleanup
745 // string value in the registry under HKCU\\Software\\[Google|Chromium]
746 // and set its value to "0". That explicitly forbids the mini installer from
747 // deleting these files.
748 // Support for this has been publicly mentioned in troubleshooting tips so
749 // we continue to support it.
750 bool ShouldDeleteExtractedFiles() {
751 wchar_t value
[2] = {0};
752 if (RegKey::ReadSZValue(HKEY_CURRENT_USER
, kCleanupRegistryKey
,
753 kCleanupRegistryValue
, value
, _countof(value
)) &&
761 // Main function. First gets a working dir, unpacks the resources and finally
762 // executes setup.exe to do the install/upgrade.
763 ProcessExitResult
WMain(HMODULE module
) {
764 // Always start with deleting potential leftovers from previous installations.
765 // This can make the difference between success and failure. We've seen
766 // many installations out in the field fail due to out of disk space problems
767 // so this could buy us some space.
768 DeleteOldChromeTempDirectories();
770 ProcessExitResult exit_code
= ProcessExitResult(SUCCESS_EXIT_CODE
);
772 // Parse configuration from the command line and resources.
773 Configuration configuration
;
774 if (!configuration
.Initialize(module
))
775 return ProcessExitResult(GENERIC_INITIALIZATION_FAILURE
);
777 // If the --cleanup switch was specified on the command line, then that means
778 // we should only do the cleanup and then exit.
779 if (ProcessNonInstallOperations(configuration
, &exit_code
))
782 // First get a path where we can extract payload
783 PathString base_path
;
784 if (!GetWorkDir(module
, &base_path
))
785 return ProcessExitResult(UNABLE_TO_GET_WORK_DIRECTORY
);
787 #if defined(GOOGLE_CHROME_BUILD)
788 // Set the magic suffix in registry to try full installer next time. We ignore
789 // any errors here and we try to set the suffix for user level unless
790 // --system-level is on the command line in which case we set it for system
791 // level instead. This only applies to the Google Chrome distribution.
792 SetInstallerFlags(configuration
);
795 PathString archive_path
;
796 PathString setup_path
;
797 exit_code
= UnpackBinaryResources(configuration
, module
, base_path
.get(),
798 &archive_path
, &setup_path
);
800 // While unpacking the binaries, we paged in a whole bunch of memory that
801 // we don't need anymore. Let's give it back to the pool before running
803 ::SetProcessWorkingSetSize(::GetCurrentProcess(), (SIZE_T
)-1, (SIZE_T
)-1);
805 if (exit_code
.IsSuccess())
806 exit_code
= RunSetup(configuration
, archive_path
.get(), setup_path
.get());
808 if (ShouldDeleteExtractedFiles())
809 DeleteExtractedFiles(base_path
.get(), archive_path
.get(), setup_path
.get());
811 WriteInstallResults(configuration
, exit_code
);
815 } // namespace mini_installer
817 int MainEntryPoint() {
818 mini_installer::ProcessExitResult result
=
819 mini_installer::WMain(::GetModuleHandle(NULL
));
821 ::ExitProcess(result
.exit_code
);
824 // VC Express editions don't come with the memset CRT obj file and linking to
825 // the obj files between versions becomes a bit problematic. Therefore,
826 // simply implement memset.
828 // This also avoids having to explicitly set the __sse2_available hack when
829 // linking with both the x64 and x86 obj files which is required when not
830 // linking with the std C lib in certain instances (including Chromium) with
831 // MSVC. __sse2_available determines whether to use SSE2 intructions with
832 // std C lib routines, and is set by MSVC's std C lib implementation normally.
834 #pragma function(memset)
835 void* memset(void* dest
, int c
, size_t count
) {
838 *reinterpret_cast<char*>(dest
) = static_cast<char>(c
);
839 dest
= reinterpret_cast<char*>(dest
) + 1;