- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / installer / mini_installer / mini_installer.cc
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 // 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.
10 //
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
20
21 // have the linker merge the sections, saving us ~500 bytes.
22 #pragma comment(linker, "/MERGE:.rdata=.text")
23
24 #include <windows.h>
25 #include <shellapi.h>
26
27 #include "chrome/installer/mini_installer/appid.h"
28 #include "chrome/installer/mini_installer/configuration.h"
29 #include "chrome/installer/mini_installer/decompress.h"
30 #include "chrome/installer/mini_installer/mini_installer.h"
31 #include "chrome/installer/mini_installer/mini_string.h"
32 #include "chrome/installer/mini_installer/pe_resource.h"
33
34 namespace mini_installer {
35
36 typedef StackString<MAX_PATH> PathString;
37 typedef StackString<MAX_PATH * 4> CommandString;
38
39 // This structure passes data back and forth for the processing
40 // of resource callbacks.
41 struct Context {
42   // Input to the call back method. Specifies the dir to save resources.
43   const wchar_t* base_path;
44   // First output from call back method. Full path of Chrome archive.
45   PathString* chrome_resource_path;
46   // Second output from call back method. Full path of Setup archive/exe.
47   PathString* setup_resource_path;
48 };
49
50 // A helper class used to manipulate the Windows registry.  Typically, members
51 // return Windows last-error codes a la the Win32 registry API.
52 class RegKey {
53  public:
54   RegKey() : key_(NULL) { }
55   ~RegKey() { Close(); }
56
57   // Opens the key named |sub_key| with given |access| rights.  Returns
58   // ERROR_SUCCESS or some other error.
59   LONG Open(HKEY key, const wchar_t* sub_key, REGSAM access);
60
61   // Returns true if a key is open.
62   bool is_valid() const { return key_ != NULL; }
63
64   // Read a REG_SZ value from the registry into the memory indicated by |value|
65   // (of |value_size| wchar_t units).  Returns ERROR_SUCCESS,
66   // ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, or some other error.  |value| is
67   // guaranteed to be null-terminated on success.
68   LONG ReadValue(const wchar_t* value_name,
69                  wchar_t* value,
70                  size_t value_size) const;
71
72   // Write a REG_SZ value to the registry.  |value| must be null-terminated.
73   // Returns ERROR_SUCCESS or an error code.
74   LONG WriteValue(const wchar_t* value_name, const wchar_t* value);
75
76   // Closes the key if it was open.
77   void Close();
78
79  private:
80   RegKey(const RegKey&);
81   RegKey& operator=(const RegKey&);
82
83   HKEY key_;
84 };  // class RegKey
85
86 LONG RegKey::Open(HKEY key, const wchar_t* sub_key, REGSAM access) {
87   Close();
88   return ::RegOpenKeyEx(key, sub_key, NULL, access, &key_);
89 }
90
91 LONG RegKey::ReadValue(const wchar_t* value_name,
92                        wchar_t* value,
93                        size_t value_size) const {
94   DWORD type;
95   DWORD byte_length = static_cast<DWORD>(value_size * sizeof(wchar_t));
96   LONG result = ::RegQueryValueEx(key_, value_name, NULL, &type,
97                                   reinterpret_cast<BYTE*>(value),
98                                   &byte_length);
99   if (result == ERROR_SUCCESS) {
100     if (type != REG_SZ) {
101       result = ERROR_NOT_SUPPORTED;
102     } else if (byte_length == 0) {
103       *value = L'\0';
104     } else if (value[byte_length/sizeof(wchar_t) - 1] != L'\0') {
105       if ((byte_length / sizeof(wchar_t)) < value_size)
106         value[byte_length / sizeof(wchar_t)] = L'\0';
107       else
108         result = ERROR_MORE_DATA;
109     }
110   }
111   return result;
112 }
113
114 LONG RegKey::WriteValue(const wchar_t* value_name, const wchar_t* value) {
115   return ::RegSetValueEx(key_, value_name, 0, REG_SZ,
116                          reinterpret_cast<const BYTE*>(value),
117                          (lstrlen(value) + 1) * sizeof(wchar_t));
118 }
119
120 void RegKey::Close() {
121   if (key_ != NULL) {
122     ::RegCloseKey(key_);
123     key_ = NULL;
124   }
125 }
126
127 // Helper function to read a value from registry. Returns true if value
128 // is read successfully and stored in parameter value. Returns false otherwise.
129 // |size| is measured in wchar_t units.
130 bool ReadValueFromRegistry(HKEY root_key, const wchar_t *sub_key,
131                            const wchar_t *value_name, wchar_t *value,
132                            size_t size) {
133   RegKey key;
134
135   if (key.Open(root_key, sub_key, KEY_QUERY_VALUE) == ERROR_SUCCESS &&
136       key.ReadValue(value_name, value, size) == ERROR_SUCCESS) {
137     return true;
138   }
139   return false;
140 }
141
142 // Opens the Google Update ClientState key for a product.
143 bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid, REGSAM access,
144                         RegKey* key) {
145   PathString client_state_key;
146   return client_state_key.assign(kApRegistryKeyBase) &&
147          client_state_key.append(app_guid) &&
148          (key->Open(root_key, client_state_key.get(), access) == ERROR_SUCCESS);
149 }
150
151 // This function sets the flag in registry to indicate that Google Update
152 // should try full installer next time. If the current installer works, this
153 // flag is cleared by setup.exe at the end of install. The flag will by default
154 // be written to HKCU, but if --system-level is included in the command line,
155 // it will be written to HKLM instead.
156 // TODO(grt): Write a unit test for this that uses registry virtualization.
157 void SetInstallerFlags(const Configuration& configuration) {
158   RegKey key;
159   const REGSAM key_access = KEY_QUERY_VALUE | KEY_SET_VALUE;
160   const HKEY root_key =
161       configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
162   // This is ignored if multi-install is true.
163   const wchar_t* app_guid =
164       configuration.has_chrome_frame() ?
165           google_update::kChromeFrameAppGuid :
166           configuration.chrome_app_guid();
167   StackString<128> value;
168   LONG ret;
169
170   // When multi_install is true, we are potentially:
171   // 1. Performing a multi-install of some product(s) on a clean machine.
172   //    Neither the product(s) nor the multi-installer will have a ClientState
173   //    key in the registry, so there is nothing to be done.
174   // 2. Upgrading an existing multi-install.  The multi-installer will have a
175   //    ClientState key in the registry.  Only it need be modified.
176   // 3. Migrating a single-install into a multi-install.  The product will have
177   //    a ClientState key in the registry.  Only it need be modified.
178   // To handle all cases, we inspect the product's ClientState to see if it
179   // exists and its "ap" value does not contain "-multi".  This is case 3, so we
180   // modify the product's ClientState.  Otherwise, we check the
181   // multi-installer's ClientState and modify it if it exists.
182   if (configuration.is_multi_install()) {
183     if (OpenClientStateKey(root_key, app_guid, key_access, &key)) {
184       // The product has a client state key.  See if it's a single-install.
185       ret = key.ReadValue(kApRegistryValueName, value.get(), value.capacity());
186       if (ret != ERROR_FILE_NOT_FOUND &&
187           (ret != ERROR_SUCCESS ||
188            FindTagInStr(value.get(), kMultiInstallTag, NULL))) {
189         // Error or case 2: modify the multi-installer's value.
190         key.Close();
191         app_guid = google_update::kMultiInstallAppGuid;
192       }  // else case 3: modify this value.
193     } else {
194       // case 1 or 2: modify the multi-installer's value.
195       key.Close();
196       app_guid = google_update::kMultiInstallAppGuid;
197     }
198   }
199
200   if (!key.is_valid()) {
201     if (!OpenClientStateKey(root_key, app_guid, key_access, &key))
202       return;
203
204     value.clear();
205     ret = key.ReadValue(kApRegistryValueName, value.get(), value.capacity());
206   }
207
208   // The conditions below are handling two cases:
209   // 1. When ap value is present, we want to add the required tag only if it is
210   //    not present.
211   // 2. When ap value is missing, we are going to create it with the required
212   //    tag.
213   if ((ret == ERROR_SUCCESS) || (ret == ERROR_FILE_NOT_FOUND)) {
214     if (ret == ERROR_FILE_NOT_FOUND)
215       value.clear();
216
217     if (!StrEndsWith(value.get(), kFullInstallerSuffix) &&
218         value.append(kFullInstallerSuffix)) {
219       key.WriteValue(kApRegistryValueName, value.get());
220     }
221   }
222 }
223
224 // Gets the setup.exe path from Registry by looking the value of Uninstall
225 // string.  |size| is measured in wchar_t units.
226 bool GetSetupExePathForGuidFromRegistry(bool system_level,
227                                         const wchar_t* app_guid,
228                                         wchar_t* path,
229                                         size_t size) {
230   const HKEY root_key = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
231   RegKey key;
232   return OpenClientStateKey(root_key, app_guid, KEY_QUERY_VALUE, &key) &&
233       (key.ReadValue(kUninstallRegistryValueName, path, size) == ERROR_SUCCESS);
234 }
235
236 // Gets the setup.exe path from Registry by looking the value of Uninstall
237 // string.  |size| is measured in wchar_t units.
238 bool GetSetupExePathFromRegistry(const Configuration& configuration,
239                                  wchar_t* path,
240                                  size_t size) {
241   bool system_level = configuration.is_system_level();
242
243   // If this is a multi install, first try looking in the binaries for the path.
244   if (configuration.is_multi_install() && GetSetupExePathForGuidFromRegistry(
245           system_level, google_update::kMultiInstallAppGuid, path, size)) {
246     return true;
247   }
248
249   // Failing that, look in Chrome Frame's client state key if --chrome-frame was
250   // specified.
251   if (configuration.has_chrome_frame() && GetSetupExePathForGuidFromRegistry(
252           system_level, google_update::kChromeFrameAppGuid, path, size)) {
253     return true;
254   }
255
256   // Make a last-ditch effort to look in the Chrome and App Host client state
257   // keys.
258   if (GetSetupExePathForGuidFromRegistry(
259           system_level, configuration.chrome_app_guid(), path, size)) {
260     return true;
261   }
262   if (configuration.has_app_host() && GetSetupExePathForGuidFromRegistry(
263           system_level, google_update::kChromeAppHostAppGuid, path, size)) {
264     return true;
265   }
266
267   return false;
268 }
269
270 // Calls CreateProcess with good default parameters and waits for the process
271 // to terminate returning the process exit code.
272 bool RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline,
273                        int* exit_code) {
274   STARTUPINFOW si = {sizeof(si)};
275   PROCESS_INFORMATION pi = {0};
276   if (!::CreateProcess(exe_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW,
277                        NULL, NULL, &si, &pi)) {
278     return false;
279   }
280
281   ::CloseHandle(pi.hThread);
282
283   bool ret = true;
284   DWORD wr = ::WaitForSingleObject(pi.hProcess, INFINITE);
285   if (WAIT_OBJECT_0 != wr) {
286     ret = false;
287   } else if (exit_code) {
288     if (!::GetExitCodeProcess(pi.hProcess,
289                               reinterpret_cast<DWORD*>(exit_code))) {
290       ret = false;
291     }
292   }
293
294   ::CloseHandle(pi.hProcess);
295
296   return ret;
297 }
298
299 // Append any command line params passed to mini_installer to the given buffer
300 // so that they can be passed on to setup.exe. We do not return any error from
301 // this method and simply skip making any changes in case of error.
302 void AppendCommandLineFlags(const Configuration& configuration,
303                             CommandString* buffer) {
304   PathString full_exe_path;
305   size_t len = ::GetModuleFileName(NULL, full_exe_path.get(),
306                                    full_exe_path.capacity());
307   if (!len || len >= full_exe_path.capacity())
308     return;
309
310   const wchar_t* exe_name = GetNameFromPathExt(full_exe_path.get(), len);
311   if (exe_name == NULL)
312     return;
313
314   const wchar_t* cmd_to_append = L"";
315   if (!StrEndsWith(configuration.program(), exe_name)) {
316     // Current executable name not in the command line so just append
317     // the whole command line.
318     cmd_to_append = configuration.command_line();
319   } else if (configuration.argument_count() > 1) {
320     const wchar_t* tmp = SearchStringI(configuration.command_line(), exe_name);
321     tmp = SearchStringI(tmp, L" ");
322     cmd_to_append = tmp;
323   }
324
325   buffer->append(cmd_to_append);
326 }
327
328
329 // Windows defined callback used in the EnumResourceNames call. For each
330 // matching resource found, the callback is invoked and at this point we write
331 // it to disk. We expect resource names to start with 'chrome' or 'setup'. Any
332 // other name is treated as an error.
333 BOOL CALLBACK OnResourceFound(HMODULE module, const wchar_t* type,
334                               wchar_t* name, LONG_PTR context) {
335   if (NULL == context)
336     return FALSE;
337
338   Context* ctx = reinterpret_cast<Context*>(context);
339
340   PEResource resource(name, type, module);
341   if ((!resource.IsValid()) ||
342       (resource.Size() < 1) ||
343       (resource.Size() > kMaxResourceSize)) {
344     return FALSE;
345   }
346
347   PathString full_path;
348   if (!full_path.assign(ctx->base_path) ||
349       !full_path.append(name) ||
350       !resource.WriteToDisk(full_path.get()))
351     return FALSE;
352
353   if (StrStartsWith(name, kChromePrefix)) {
354     if (!ctx->chrome_resource_path->assign(full_path.get()))
355       return FALSE;
356   } else if (StrStartsWith(name, kSetupPrefix)) {
357     if (!ctx->setup_resource_path->assign(full_path.get()))
358       return FALSE;
359   } else {
360     // Resources should either start with 'chrome' or 'setup'. We don't handle
361     // anything else.
362     return FALSE;
363   }
364
365   return TRUE;
366 }
367
368 // Finds and writes to disk resources of various types. Returns false
369 // if there is a problem in writing any resource to disk. setup.exe resource
370 // can come in one of three possible forms:
371 // - Resource type 'B7', compressed using LZMA (*.7z)
372 // - Resource type 'BL', compressed using LZ (*.ex_)
373 // - Resource type 'BN', uncompressed (*.exe)
374 // If setup.exe is present in more than one form, the precedence order is
375 // BN < BL < B7
376 // For more details see chrome/tools/build/win/create_installer_archive.py.
377 bool UnpackBinaryResources(const Configuration& configuration, HMODULE module,
378                            const wchar_t* base_path, PathString* archive_path,
379                            PathString* setup_path) {
380   // Generate the setup.exe path where we patch/uncompress setup resource.
381   PathString setup_dest_path;
382   if (!setup_dest_path.assign(base_path) ||
383       !setup_dest_path.append(kSetupName))
384     return false;
385
386   // Prepare the input to OnResourceFound method that needs a location where
387   // it will write all the resources.
388   Context context = {
389     base_path,
390     archive_path,
391     setup_path,
392   };
393
394   // Get the resources of type 'B7' (7zip archive).
395   // We need a chrome archive to do the installation. So if there
396   // is a problem in fetching B7 resource, just return an error.
397   if (!::EnumResourceNames(module, kLZMAResourceType, OnResourceFound,
398                            reinterpret_cast<LONG_PTR>(&context)) ||
399       archive_path->length() == 0)
400     return false;
401
402   // If we found setup 'B7' resource, handle it.
403   if (setup_path->length() > 0) {
404     CommandString cmd_line;
405     // Get the path to setup.exe first.
406     bool success = true;
407     if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(),
408                                      cmd_line.capacity()) ||
409         !cmd_line.append(kCmdUpdateSetupExe) ||
410         !cmd_line.append(L"=\"") ||
411         !cmd_line.append(setup_path->get()) ||
412         !cmd_line.append(L"\"") ||
413         !cmd_line.append(kCmdNewSetupExe) ||
414         !cmd_line.append(L"=\"") ||
415         !cmd_line.append(setup_dest_path.get()) ||
416         !cmd_line.append(L"\"")) {
417       success = false;
418     }
419
420     // Get any command line option specified for mini_installer and pass them
421     // on to setup.exe.  This is important since switches such as
422     // --multi-install and --chrome-frame affect where setup.exe will write
423     // installer results for consumption by Google Update.
424     AppendCommandLineFlags(configuration, &cmd_line);
425
426     int exit_code = 0;
427     if (success &&
428         (!RunProcessAndWait(NULL, cmd_line.get(), &exit_code) ||
429          exit_code != ERROR_SUCCESS)) {
430       success = false;
431     }
432
433     if (!success)
434       DeleteFile(setup_path->get());
435
436     return success && setup_path->assign(setup_dest_path.get());
437   }
438
439   // setup.exe wasn't sent as 'B7', lets see if it was sent as 'BL'
440   // (compressed setup).
441   if (!::EnumResourceNames(module, kLZCResourceType, OnResourceFound,
442                            reinterpret_cast<LONG_PTR>(&context)) &&
443       ::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND)
444     return false;
445
446   if (setup_path->length() > 0) {
447     // Uncompress LZ compressed resource. Setup is packed with 'MSCF'
448     // as opposed to old DOS way of 'SZDD'. Hence we don't use LZCopy.
449     bool success = mini_installer::Expand(setup_path->get(),
450                                           setup_dest_path.get());
451     ::DeleteFile(setup_path->get());
452     if (success) {
453       if (!setup_path->assign(setup_dest_path.get())) {
454         ::DeleteFile(setup_dest_path.get());
455         success = false;
456       }
457     }
458
459     return success;
460   }
461
462   // setup.exe still not found. So finally check if it was sent as 'BN'
463   // (uncompressed setup).
464   // TODO(tommi): We don't need BN anymore so let's remove it (and remove
465   // it from create_installer_archive.py).
466   if (!::EnumResourceNames(module, kBinResourceType, OnResourceFound,
467                            reinterpret_cast<LONG_PTR>(&context)) &&
468       ::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND)
469     return false;
470
471   if (setup_path->length() > 0) {
472     if (setup_path->comparei(setup_dest_path.get()) != 0) {
473       if (!::MoveFileEx(setup_path->get(), setup_dest_path.get(),
474                         MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) {
475         ::DeleteFile(setup_path->get());
476         setup_path->clear();
477       } else if (!setup_path->assign(setup_dest_path.get())) {
478         ::DeleteFile(setup_dest_path.get());
479       }
480     }
481   }
482
483   return setup_path->length() > 0;
484 }
485
486 // Executes setup.exe, waits for it to finish and returns the exit code.
487 bool RunSetup(const Configuration& configuration, const wchar_t* archive_path,
488               const wchar_t* setup_path, int* exit_code) {
489   // There could be three full paths in the command line for setup.exe (path
490   // to exe itself, path to archive and path to log file), so we declare
491   // total size as three + one additional to hold command line options.
492   CommandString cmd_line;
493
494   // Get the path to setup.exe first.
495   if (::lstrlen(setup_path) > 0) {
496     if (!cmd_line.assign(L"\"") ||
497         !cmd_line.append(setup_path) ||
498         !cmd_line.append(L"\""))
499       return false;
500   } else if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(),
501                                           cmd_line.capacity())) {
502     return false;
503   }
504
505   // Append the command line param for chrome archive file
506   if (!cmd_line.append(kCmdInstallArchive) ||
507       !cmd_line.append(L"=\"") ||
508       !cmd_line.append(archive_path) ||
509       !cmd_line.append(L"\""))
510     return false;
511
512   // Get any command line option specified for mini_installer and pass them
513   // on to setup.exe
514   AppendCommandLineFlags(configuration, &cmd_line);
515
516   return RunProcessAndWait(NULL, cmd_line.get(), exit_code);
517 }
518
519 // Deletes given files and working dir.
520 void DeleteExtractedFiles(const wchar_t* base_path,
521                           const wchar_t* archive_path,
522                           const wchar_t* setup_path) {
523   ::DeleteFile(archive_path);
524   ::DeleteFile(setup_path);
525   // Delete the temp dir (if it is empty, otherwise fail).
526   ::RemoveDirectory(base_path);
527 }
528
529 // Creates a temporary directory under |base_path| and returns the full path
530 // of created directory in |work_dir|. If successful return true, otherwise
531 // false.  When successful, the returned |work_dir| will always have a trailing
532 // backslash and this function requires that |base_path| always includes a
533 // trailing backslash as well.
534 // We do not use GetTempFileName here to avoid running into AV software that
535 // might hold on to the temp file as soon as we create it and then we can't
536 // delete it and create a directory in its place.  So, we use our own mechanism
537 // for creating a directory with a hopefully-unique name.  In the case of a
538 // collision, we retry a few times with a new name before failing.
539 bool CreateWorkDir(const wchar_t* base_path, PathString* work_dir) {
540   if (!work_dir->assign(base_path) || !work_dir->append(kTempPrefix))
541     return false;
542
543   // Store the location where we'll append the id.
544   size_t end = work_dir->length();
545
546   // Check if we'll have enough buffer space to continue.
547   // The name of the directory will use up 11 chars and then we need to append
548   // the trailing backslash and a terminator.  We've already added the prefix
549   // to the buffer, so let's just make sure we've got enough space for the rest.
550   if ((work_dir->capacity() - end) < (arraysize("fffff.tmp") + 1))
551     return false;
552
553   // Generate a unique id.  We only use the lowest 20 bits, so take the top
554   // 12 bits and xor them with the lower bits.
555   DWORD id = ::GetTickCount();
556   id ^= (id >> 12);
557
558   int max_attempts = 10;
559   while (max_attempts--) {
560     // This converts 'id' to a string in the format "78563412" on windows
561     // because of little endianness, but we don't care since it's just
562     // a name.
563     if (!HexEncode(&id, sizeof(id), work_dir->get() + end,
564                    work_dir->capacity() - end)) {
565       return false;
566     }
567
568     // We only want the first 5 digits to remain within the 8.3 file name
569     // format (compliant with previous implementation).
570     work_dir->truncate_at(end + 5);
571
572     // for consistency with the previous implementation which relied on
573     // GetTempFileName, we append the .tmp extension.
574     work_dir->append(L".tmp");
575     if (::CreateDirectory(work_dir->get(), NULL)) {
576       // Yay!  Now let's just append the backslash and we're done.
577       return work_dir->append(L"\\");
578     }
579     ++id;  // Try a different name.
580   }
581
582   return false;
583 }
584
585 // Creates and returns a temporary directory that can be used to extract
586 // mini_installer payload.
587 bool GetWorkDir(HMODULE module, PathString* work_dir) {
588   PathString base_path;
589   DWORD len = ::GetTempPath(base_path.capacity(), base_path.get());
590   if (!len || len >= base_path.capacity() ||
591       !CreateWorkDir(base_path.get(), work_dir)) {
592     // Problem creating the work dir under TEMP path, so try using the
593     // current directory as the base path.
594     len = ::GetModuleFileName(module, base_path.get(), base_path.capacity());
595     if (len >= base_path.capacity() || !len)
596       return false;  // Can't even get current directory? Return an error.
597
598     wchar_t* name = GetNameFromPathExt(base_path.get(), len);
599     if (!name)
600       return false;
601
602     *name = L'\0';
603
604     return CreateWorkDir(base_path.get(), work_dir);
605   }
606   return true;
607 }
608
609 // Returns true for ".." and "." directories.
610 bool IsCurrentOrParentDirectory(const wchar_t* dir) {
611   return dir &&
612          dir[0] == L'.' &&
613          (dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0'));
614 }
615
616 // Best effort directory tree deletion including the directory specified
617 // by |path|, which must not end in a separator.
618 // The |path| argument is writable so that each recursion can use the same
619 // buffer as was originally allocated for the path.  The path will be unchanged
620 // upon return.
621 void RecursivelyDeleteDirectory(PathString* path) {
622   // |path| will never have a trailing backslash.
623   size_t end = path->length();
624   if (!path->append(L"\\*.*"))
625     return;
626
627   WIN32_FIND_DATA find_data = {0};
628   HANDLE find = ::FindFirstFile(path->get(), &find_data);
629   if (find != INVALID_HANDLE_VALUE) {
630     do {
631       // Use the short name if available to make the most of our buffer.
632       const wchar_t* name = find_data.cAlternateFileName[0] ?
633           find_data.cAlternateFileName : find_data.cFileName;
634       if (IsCurrentOrParentDirectory(name))
635         continue;
636
637       path->truncate_at(end + 1);  // Keep the trailing backslash.
638       if (!path->append(name))
639         continue;  // Continue in spite of too long names.
640
641       if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
642         RecursivelyDeleteDirectory(path);
643       } else {
644         ::DeleteFile(path->get());
645       }
646     } while (::FindNextFile(find, &find_data));
647     ::FindClose(find);
648   }
649
650   // Restore the path and delete the directory before we return.
651   path->truncate_at(end);
652   ::RemoveDirectory(path->get());
653 }
654
655 // Enumerates subdirectories of |parent_dir| and deletes all subdirectories
656 // that match with a given |prefix|.  |parent_dir| must have a trailing
657 // backslash.
658 // The process is done on a best effort basis, so conceivably there might
659 // still be matches left when the function returns.
660 void DeleteDirectoriesWithPrefix(const wchar_t* parent_dir,
661                                  const wchar_t* prefix) {
662   // |parent_dir| is guaranteed to always have a trailing backslash.
663   PathString spec;
664   if (!spec.assign(parent_dir) || !spec.append(prefix) || !spec.append(L"*.*"))
665     return;
666
667   WIN32_FIND_DATA find_data = {0};
668   HANDLE find = ::FindFirstFileEx(spec.get(), FindExInfoStandard, &find_data,
669                                   FindExSearchLimitToDirectories, NULL, 0);
670   if (find == INVALID_HANDLE_VALUE)
671     return;
672
673   PathString path;
674   do {
675     if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
676       // Use the short name if available to make the most of our buffer.
677       const wchar_t* name = find_data.cAlternateFileName[0] ?
678           find_data.cAlternateFileName : find_data.cFileName;
679       if (IsCurrentOrParentDirectory(name))
680         continue;
681       if (path.assign(parent_dir) && path.append(name))
682         RecursivelyDeleteDirectory(&path);
683     }
684   } while (::FindNextFile(find, &find_data));
685   ::FindClose(find);
686 }
687
688 // Attempts to free up space by deleting temp directories that previous
689 // installer runs have failed to clean up.
690 void DeleteOldChromeTempDirectories() {
691   static const wchar_t* const kDirectoryPrefixes[] = {
692     kTempPrefix,
693     L"chrome_"  // Previous installers created directories with this prefix
694                 // and there are still some lying around.
695   };
696
697   PathString temp;
698   // GetTempPath always returns a path with a trailing backslash.
699   DWORD len = ::GetTempPath(temp.capacity(), temp.get());
700   // GetTempPath returns 0 or number of chars copied, not including the
701   // terminating '\0'.
702   if (!len || len >= temp.capacity())
703     return;
704
705   for (int i = 0; i < arraysize(kDirectoryPrefixes); ++i) {
706     DeleteDirectoriesWithPrefix(temp.get(), kDirectoryPrefixes[i]);
707   }
708 }
709
710 // Checks the command line for specific mini installer flags.
711 // If the function returns true, the command line has been processed and all
712 // required actions taken.  The installer must exit and return the returned
713 // |exit_code|.
714 bool ProcessNonInstallOperations(const Configuration& configuration,
715                                  int* exit_code) {
716   bool ret = false;
717
718   switch (configuration.operation()) {
719     case Configuration::CLEANUP:
720       // Cleanup has already taken place in DeleteOldChromeTempDirectories at
721       // this point, so just tell our caller to exit early.
722       *exit_code = 0;
723       ret = true;
724       break;
725
726     default: break;
727   }
728
729   return ret;
730 }
731
732 // Returns true if we should delete the temp files we create (default).
733 // Returns false iff the user has manually created a ChromeInstallerCleanup
734 // string value in the registry under HKCU\\Software\\[Google|Chromium]
735 // and set its value to "0".  That explicitly forbids the mini installer from
736 // deleting these files.
737 // Support for this has been publicly mentioned in troubleshooting tips so
738 // we continue to support it.
739 bool ShouldDeleteExtractedFiles() {
740   wchar_t value[2] = {0};
741   if (ReadValueFromRegistry(HKEY_CURRENT_USER, kCleanupRegistryKey,
742                             kCleanupRegistryValueName, value,
743                             arraysize(value)) &&
744       value[0] == L'0') {
745     return false;
746   }
747
748   return true;
749 }
750
751 // Main function. First gets a working dir, unpacks the resources and finally
752 // executes setup.exe to do the install/upgrade.
753 int WMain(HMODULE module) {
754 #if defined(COMPONENT_BUILD)
755   static const wchar_t kComponentBuildIncompatibleMessage[] =
756       L"mini_installer.exe is incompatible with the component build, please run"
757       L" setup.exe with the same command line instead. See"
758       L" http://crbug.com/127233#c17 for details.";
759   ::MessageBox(NULL, kComponentBuildIncompatibleMessage, NULL, MB_ICONERROR);
760   return 1;
761 #endif
762
763   // Always start with deleting potential leftovers from previous installations.
764   // This can make the difference between success and failure.  We've seen
765   // many installations out in the field fail due to out of disk space problems
766   // so this could buy us some space.
767   DeleteOldChromeTempDirectories();
768
769   // TODO(grt): Make the exit codes more granular so we know where the popular
770   // errors truly are.
771   int exit_code = 101;
772
773   // Parse the command line.
774   Configuration configuration;
775   if (!configuration.Initialize())
776     return exit_code;
777
778   // If the --cleanup switch was specified on the command line, then that means
779   // we should only do the cleanup and then exit.
780   if (ProcessNonInstallOperations(configuration, &exit_code))
781     return exit_code;
782
783   // First get a path where we can extract payload
784   PathString base_path;
785   if (!GetWorkDir(module, &base_path))
786     return 101;
787
788 #if defined(GOOGLE_CHROME_BUILD)
789   // Set the magic suffix in registry to try full installer next time. We ignore
790   // any errors here and we try to set the suffix for user level unless
791   // --system-level is on the command line in which case we set it for system
792   // level instead. This only applies to the Google Chrome distribution.
793   SetInstallerFlags(configuration);
794 #endif
795
796   PathString archive_path;
797   PathString setup_path;
798   if (!UnpackBinaryResources(configuration, module, base_path.get(),
799                              &archive_path, &setup_path)) {
800     exit_code = 102;
801   } else {
802     // While unpacking the binaries, we paged in a whole bunch of memory that
803     // we don't need anymore.  Let's give it back to the pool before running
804     // setup.
805     ::SetProcessWorkingSetSize(::GetCurrentProcess(), -1, -1);
806     if (!RunSetup(configuration, archive_path.get(), setup_path.get(),
807                   &exit_code)) {
808       exit_code = 103;
809     }
810   }
811
812   if (ShouldDeleteExtractedFiles())
813     DeleteExtractedFiles(base_path.get(), archive_path.get(), setup_path.get());
814
815   return exit_code;
816 }
817
818 }  // namespace mini_installer
819
820 int MainEntryPoint() {
821   int result = mini_installer::WMain(::GetModuleHandle(NULL));
822   ::ExitProcess(result);
823 }
824
825 // VC Express editions don't come with the memset CRT obj file and linking to
826 // the obj files between versions becomes a bit problematic. Therefore,
827 // simply implement memset.
828 //
829 // This also avoids having to explicitly set the __sse2_available hack when
830 // linking with both the x64 and x86 obj files which is required when not
831 // linking with the std C lib in certain instances (including Chromium) with
832 // MSVC.  __sse2_available determines whether to use SSE2 intructions with
833 // std C lib routines, and is set by MSVC's std C lib implementation normally.
834 extern "C" {
835 #pragma function(memset)
836 void* memset(void* dest, int c, size_t count) {
837   // Simplistic 32-bit memset C implementation which assumes properly aligned
838   // memory; performance hit on memory that isn't properly aligned, but still
839   // better overall then a 8-bit implementation.
840   size_t adjcount = count >> 2;
841   UINT32 fill = (c << 24 | c << 16 | c << 8 | c);
842   UINT32* dest32 = reinterpret_cast<UINT32*>(dest);
843   UINT8* dest8 = reinterpret_cast<UINT8*>(dest);
844
845   // Take care of the ending 0-3 bytes (binary 11 = 3).  The lack of breaks is
846   // deliberate; it falls through for each byte. Think of it a simplified for
847   // loop.
848   switch (count - (adjcount << 2)) {
849     case 3:
850       dest8[count - 3] = c;
851     case 2:
852       dest8[count - 2] = c;
853     case 1:
854       dest8[count - 1] = c;
855   }
856
857   while (adjcount-- > 0)  // Copy the rest, 4 bytes/32 bits at a time
858     *(dest32++) = fill;
859
860   return dest;
861 }
862 }  // extern "C"