Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: mi_exe_stub/mi.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « mi_exe_stub/build.scons ('k') | mi_exe_stub/mi.grh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2006-2009 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15 //
16 // Implementation of the metainstaller logic.
17 // Untars a tarball and executes the extracted executable.
18 // If no command line is specified, "/install" is passed to the executable
19 // along with a .gup file if one is extracted.
20 // If found, the contents of the signature tag are also passed to the
21 // executable unmodified.
22
23 #include <tchar.h>
24 #include <atlsimpcoll.h>
25 #include <atlstr.h>
26 #include <shellapi.h>
27 #include <shlwapi.h>
28 #include <windows.h>
29
30 #pragma warning(push)
31 // C4310: cast truncates constant value
32 #pragma warning(disable : 4310)
33 #include "base/basictypes.h"
34 #pragma warning(pop)
35 #include "base/scoped_ptr.h"
36 #include "omaha/base/constants.h"
37 #include "omaha/base/error.h"
38 #include "omaha/base/extractor.h"
39 #include "omaha/base/scoped_any.h"
40 #include "omaha/base/system_info.h"
41 #include "omaha/common/const_cmd_line.h"
42 #include "omaha/mi_exe_stub/process.h"
43 #include "omaha/mi_exe_stub/mi.grh"
44 #include "omaha/mi_exe_stub/tar.h"
45 extern "C" {
46 #include "third_party/lzma/v4_65/files/C/Bcj2.h"
47 #include "third_party/lzma/v4_65/files/C/LzmaDec.h"
48 }
49
50 namespace omaha {
51
52 // Resource ID of the goopdate payload inside the meta-installer.
53 #define IDR_PAYLOAD 102
54
55 namespace {
56
57 HRESULT HandleError(HRESULT hr);
58
59 // The function assumes that the extractor has already been opened.
60 // The buffer must be deleted by the caller.
61 char* ReadTag(TagExtractor* extractor) {
62 const int kMaxTagLength = 0x10000; // 64KB
63
64 int tag_buffer_size = 0;
65 if (!extractor->ExtractTag(NULL, &tag_buffer_size)) {
66 return NULL;
67 }
68 if (!tag_buffer_size || (tag_buffer_size >= kMaxTagLength)) {
69 return NULL;
70 }
71
72 scoped_array<char> tag_buffer(new char[tag_buffer_size]);
73 if (!tag_buffer.get()) {
74 return NULL;
75 }
76
77 if (!extractor->ExtractTag(tag_buffer.get(), &tag_buffer_size)) {
78 _ASSERTE(false);
79 return NULL;
80 }
81
82 // Do a sanity check of the tag string. The double quote '"'
83 // is a special character that should not be included in the tag string.
84 for (const char* tag_char = tag_buffer.get(); *tag_char; ++tag_char) {
85 if (*tag_char == '"') {
86 _ASSERTE(false);
87 return NULL;
88 }
89 }
90
91 return tag_buffer.release();
92 }
93
94 // Extract the tag containing the extra information written by the server.
95 // The memory returned by the function will have to be freed using delete[]
96 // operator.
97 char* ExtractTag(const TCHAR* module_file_name) {
98 if (!module_file_name) {
99 return NULL;
100 }
101
102 TagExtractor extractor;
103 if (!extractor.OpenFile(module_file_name)) {
104 return NULL;
105 }
106 char* ret = ReadTag(&extractor);
107 extractor.CloseFile();
108
109 return ret;
110 }
111
112 class MetaInstaller {
113 public:
114 MetaInstaller(HINSTANCE instance, LPCSTR cmd_line)
115 : instance_(instance),
116 cmd_line_(cmd_line),
117 exit_code_(0) {
118 }
119
120 ~MetaInstaller() {
121 // When a crash happens while running GoogleUpdate and breakpad gets it
122 // GooogleUpdate.exe is started with the /report to report the crash.
123 // In a crash, the temp directory and the contained files can't be deleted.
124 if (exit_code_ != GOOPDATE_E_CRASH) {
125 CleanUpTempDirectory();
126 }
127 }
128
129 int ExtractAndRun() {
130 if (CreateUniqueTempDirectory() != 0) {
131 return -1;
132 }
133 scoped_hfile tarball_file(ExtractTarballToTempLocation());
134 if (!valid(tarball_file)) {
135 return -1;
136 }
137
138 // Extract files from the archive and run the first EXE we find in it.
139 Tar tar(temp_dir_, get(tarball_file), true);
140 tar.SetCallback(TarFileCallback, this);
141 if (!tar.ExtractToDir()) {
142 return -1;
143 }
144
145 exit_code_ = ULONG_MAX;
146 if (!exe_path_.IsEmpty()) {
147 // Build the command line. There are three scenarios we consider:
148 // 1. Run by the user, in which case the MI does not receive any
149 // argument on its command line. In this case the command line
150 // to run is: "exe_path" /install [["]manifest["]]
151 // 2. Run with command line arguments. The tag, if present, will be
152 // appended to the command line.
153 // The command line is: "exe_path" args <tag>
154 // For example, pass "/silent /install" to the metainstaller to
155 // initiate a silent install using the extra args in the tag.
156 // If a command line does not take a tag or a custom tag is needed,
157 // use an untagged file.
158 CString command_line(exe_path_);
159 ::PathQuoteSpaces(CStrBuf(command_line, MAX_PATH));
160
161 scoped_array<char> tag(GetTag());
162 if (cmd_line_.IsEmpty()) {
163 // Run-by-user case.
164 if (!tag.get()) {
165 _ASSERTE(!_T("Must provide arguments with untagged metainstaller."));
166 HRESULT hr = GOOPDATE_E_UNTAGGED_METAINSTALLER;
167 HandleError(hr);
168 return hr;
169 }
170 command_line.AppendFormat(_T(" /%s %s /%s"),
171 kCmdLineInstallSource,
172 kCmdLineInstallSource_TaggedMetainstaller,
173 kCmdLineInstall);
174 } else {
175 command_line.AppendFormat(_T(" %s"), cmd_line_);
176
177 CheckAndHandleRecoveryCase(&command_line);
178 }
179
180 if (tag.get()) {
181 command_line.AppendFormat(_T(" \"%s\""), CString(tag.get()));
182 }
183
184 RunAndWait(command_line, &exit_code_);
185 }
186 // Propagate up the exit code of the program we have run.
187 return exit_code_;
188 }
189
190 private:
191 void CleanUpTempDirectory() {
192 // Delete our temp directory and its contents.
193 for (int i = 0; i != files_to_delete_.GetSize(); ++i) {
194 DeleteFile(files_to_delete_[i]);
195 }
196 files_to_delete_.RemoveAll();
197
198 ::RemoveDirectory(temp_dir_);
199 temp_dir_.Empty();
200 }
201
202 // Determines whether this is a silent install.
203 bool IsSilentInstall() {
204 CString silent_argument;
205 silent_argument.Format(_T("/%s"), kCmdLineSilent);
206
207 return silent_argument == cmd_line_;
208 }
209
210 // Determines whether the MI is being invoked for recovery purposes, and,
211 // if so, appends the MI's full path to the command line.
212 // cmd_line_ must begin with "/recover" in order for the recovery case to be
213 // detected.
214 void CheckAndHandleRecoveryCase(CString* command_line) {
215 _ASSERTE(command_line);
216
217 CString recover_argument;
218 recover_argument.Format(_T("/%s"), kCmdLineRecover);
219
220 if (cmd_line_.Left(recover_argument.GetLength()) == recover_argument) {
221 TCHAR current_path[MAX_PATH] = {};
222 if (::GetModuleFileName(NULL, current_path, arraysize(current_path))) {
223 command_line->AppendFormat(_T(" \"%s\""), current_path);
224 }
225 }
226 }
227
228 // Create a temp directory to hold the embedded setup files.
229 // This is a bit of a hack: we ask the system to create a temporary
230 // filename for us, and instead we use that name for a subdirectory name.
231 int CreateUniqueTempDirectory() {
232 ::GetTempPath(MAX_PATH, CStrBuf(temp_root_dir_, MAX_PATH));
233 if (::CreateDirectory(temp_root_dir_, NULL) != 0 ||
234 ::GetLastError() == ERROR_ALREADY_EXISTS) {
235 if (!::GetTempFileName(temp_root_dir_,
236 _T("GUM"),
237 0, // form a unique filename
238 CStrBuf(temp_dir_, MAX_PATH))) {
239 return -1;
240 }
241 // GetTempFileName() actually creates the temp file, so delete it.
242 ::DeleteFile(temp_dir_);
243 ::CreateDirectory(temp_dir_, NULL);
244 } else {
245 return -1;
246 }
247 return 0;
248 }
249
250 HANDLE ExtractTarballToTempLocation() {
251 HANDLE tarball_file = INVALID_HANDLE_VALUE;
252 TCHAR tarball_filename[MAX_PATH] = {0};
253 if (::GetTempFileName(temp_root_dir_,
254 _T("GUT"),
255 0, // form a unique filename
256 tarball_filename)) {
257 files_to_delete_.Add(tarball_filename);
258 HRSRC res_info = ::FindResource(NULL,
259 MAKEINTRESOURCE(IDR_PAYLOAD),
260 _T("B"));
261 if (NULL != res_info) {
262 HGLOBAL resource = ::LoadResource(NULL, res_info);
263 if (NULL != resource) {
264 LPVOID resource_pointer = ::LockResource(resource);
265 if (NULL != resource_pointer) {
266 tarball_file = ::CreateFile(tarball_filename,
267 GENERIC_READ | GENERIC_WRITE,
268 0,
269 NULL,
270 OPEN_ALWAYS,
271 0,
272 NULL);
273 if (INVALID_HANDLE_VALUE != tarball_file) {
274 LARGE_INTEGER file_position = {};
275 if (0 != DecompressBufferToFile(
276 static_cast<const uint8*>(resource_pointer),
277 ::SizeofResource(NULL, res_info),
278 tarball_file) ||
279 !::SetFilePointerEx(tarball_file, file_position, NULL,
280 FILE_BEGIN)) {
281 ::CloseHandle(tarball_file);
282 tarball_file = INVALID_HANDLE_VALUE;
283 }
284 }
285 }
286 }
287 }
288 }
289 return tarball_file;
290 }
291
292 char* GetTag() const {
293 // Get this module file name.
294 TCHAR module_file_name[MAX_PATH] = {};
295 if (!::GetModuleFileName(instance_, module_file_name,
296 arraysize(module_file_name))) {
297 _ASSERTE(false);
298 return NULL;
299 }
300
301 return ExtractTag(module_file_name);
302 }
303
304 static CString GetFilespec(const CString& path) {
305 int pos = path.ReverseFind('\\');
306 if (pos >= 0) {
307 return path.Mid(pos + 1);
308 }
309 return path;
310 }
311
312 void HandleTarFile(const TCHAR* filename) {
313 CString new_filename(filename);
314 files_to_delete_.Add(new_filename);
315 CString filespec(GetFilespec(new_filename));
316 filespec.MakeLower();
317
318 if (filespec.GetLength() > 4) {
319 CString extension(filespec.Mid(filespec.GetLength() - 4));
320
321 if (extension == _T(".exe")) {
322 // We're interested in remembering only the first exe in the tarball.
323 if (exe_path_.IsEmpty()) {
324 exe_path_ = new_filename;
325 }
326 }
327 }
328 }
329
330 static void TarFileCallback(void* context, const TCHAR* filename) {
331 MetaInstaller* mi = reinterpret_cast<MetaInstaller*>(context);
332 mi->HandleTarFile(filename);
333 }
334
335 // TODO(omaha): reimplement the relevant files in the LZMA SDK to optimize
336 // for size. We'll have to release the modifications (LZMA SDK is CDDL/CDL),
337 // which shouldn't be a problem.
338 static void* MyAlloc(void* p, size_t size) {
339 UNREFERENCED_PARAMETER(p);
340 return new uint8[size];
341 }
342
343 static void MyFree(void* p, void* address) {
344 UNREFERENCED_PARAMETER(p);
345 delete[] address;
346 }
347
348 // Decompress the content of the memory buffer into the file
349 static int DecompressBufferToFile(const uint8* packed_buffer,
350 size_t packed_size,
351 HANDLE file) {
352 // need header and len minimally
353 if (packed_size < LZMA_PROPS_SIZE + 8) {
354 return -1;
355 }
356
357 // Note this code won't properly handle decoding large files, since uint32
358 // is used in several places to count size.
359 ISzAlloc allocators = { &MyAlloc, &MyFree };
360 CLzmaDec lzma_state;
361 LzmaDec_Construct(&lzma_state);
362 LzmaDec_Allocate(&lzma_state, packed_buffer, LZMA_PROPS_SIZE, &allocators);
363 LzmaDec_Init(&lzma_state);
364 packed_buffer += LZMA_PROPS_SIZE;
365 packed_size -= LZMA_PROPS_SIZE;
366
367 // TODO(omaha): make this independent of endianness.
368 uint64 unpacked_size_64 = *reinterpret_cast<const uint64*>(packed_buffer);
369 size_t unpacked_size = static_cast<size_t>(unpacked_size_64);
370 packed_buffer += sizeof(unpacked_size_64);
371 packed_size -= sizeof(unpacked_size_64);
372
373 scoped_array<uint8> unpacked_buffer(new uint8[unpacked_size]);
374
375 ELzmaStatus status = static_cast<ELzmaStatus>(0);
376 SRes result = LzmaDec_DecodeToBuf(
377 &lzma_state,
378 unpacked_buffer.get(),
379 &unpacked_size,
380 packed_buffer,
381 &packed_size,
382 LZMA_FINISH_END,
383 &status);
384 LzmaDec_Free(&lzma_state, &allocators);
385 if (SZ_OK != result) {
386 return -1;
387 }
388
389 #if 0
390 // Reverse BCJ coding.
391 uint32 x86_conversion_state;
392 x86_Convert_Init(x86_conversion_state);
393 x86_Convert(unpacked_buffer.get(), unpacked_size, 0, &x86_conversion_state,
394 0 /* decoding */);
395 #else
396 // Reverse BCJ2 coding.
397 const uint8* p = unpacked_buffer.get();
398 uint32 original_size = *reinterpret_cast<const uint32*>(p);
399 p += sizeof(uint32); // NOLINT
400 uint32 stream0_size = *reinterpret_cast<const uint32*>(p);
401 p += sizeof(uint32); // NOLINT
402 uint32 stream1_size = *reinterpret_cast<const uint32*>(p);
403 p += sizeof(uint32); // NOLINT
404 uint32 stream2_size = *reinterpret_cast<const uint32*>(p);
405 p += sizeof(uint32); // NOLINT
406 uint32 stream3_size = *reinterpret_cast<const uint32*>(p);
407 p += sizeof(uint32); // NOLINT
408
409 scoped_array<uint8> output_buffer(new uint8[original_size]);
410 if (SZ_OK != Bcj2_Decode(p,
411 stream0_size,
412 p + stream0_size,
413 stream1_size,
414 p + stream0_size + stream1_size,
415 stream2_size,
416 p + stream0_size + stream1_size + stream2_size,
417 stream3_size,
418 output_buffer.get(), original_size)) {
419 return 1;
420 }
421 #endif
422
423 DWORD written;
424 if (!::WriteFile(file, output_buffer.get(), original_size, &written,
425 NULL) ||
426 written != original_size) {
427 return -1;
428 }
429
430 return 0;
431 }
432
433 HINSTANCE instance_;
434 CString cmd_line_;
435 CString exe_path_;
436 DWORD exit_code_;
437 CSimpleArray<CString> files_to_delete_;
438 CString temp_dir_;
439 CString temp_root_dir_;
440 };
441
442 HRESULT CheckOSRequirements() {
443 return SystemInfo::OSWin2KSP4OrLater() ? S_OK :
444 GOOPDATE_E_RUNNING_INFERIOR_WINDOWS;
445 }
446
447 CString GetCompanyDisplayName() {
448 CString company_name;
449 company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME);
450 _ASSERTE(!company_name.IsEmpty());
451 return company_name;
452 }
453
454 CString GetUiTitle() {
455 CString title;
456 title.FormatMessage(IDS_INSTALLER_DISPLAY_NAME, GetCompanyDisplayName());
457 return title;
458 }
459
460 HRESULT HandleError(HRESULT result) {
461 _ASSERTE(FAILED(result));
462 CString msg_box_text;
463
464 switch (result) {
465 case GOOPDATE_E_RUNNING_INFERIOR_WINDOWS:
466 msg_box_text.FormatMessage(IDS_RUNNING_INFERIOR_WINDOWS,
467 GetCompanyDisplayName());
468 break;
469
470 case GOOPDATE_E_UNTAGGED_METAINSTALLER:
471 default:
472 msg_box_text.LoadString(IDS_GENERIC_ERROR);
473 _ASSERTE(!msg_box_text.IsEmpty());
474 break;
475 }
476
477 ::MessageBox(NULL, msg_box_text, GetUiTitle(), MB_OK);
478 return result;
479 }
480
481 } // namespace
482
483 } // namespace omaha
484
485 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int) {
486 scoped_co_init init_com_apt;
487 HRESULT hr(init_com_apt.hresult());
488 if (FAILED(hr)) {
489 return omaha::HandleError(hr);
490 }
491
492 hr = omaha::CheckOSRequirements();
493 if (FAILED(hr)) {
494 return omaha::HandleError(hr);
495 }
496
497 omaha::MetaInstaller mi(hInstance, lpCmdLine);
498 int result = mi.ExtractAndRun();
499 return result;
500 }
501
OLDNEW
« no previous file with comments | « mi_exe_stub/build.scons ('k') | mi_exe_stub/mi.grh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698