- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / installer / util / lzma_util.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 #include "chrome/installer/util/lzma_util.h"
6
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
10
11 extern "C" {
12 #include "third_party/lzma_sdk/7z.h"
13 #include "third_party/lzma_sdk/7zAlloc.h"
14 #include "third_party/lzma_sdk/7zCrc.h"
15 #include "third_party/lzma_sdk/7zFile.h"
16 }
17
18
19 namespace {
20
21 SRes LzmaReadFile(HANDLE file, void *data, size_t *size) {
22   if (*size == 0)
23     return SZ_OK;
24
25   size_t processedSize = 0;
26   DWORD maxSize = *size;
27   do {
28     DWORD processedLoc = 0;
29     BOOL res = ReadFile(file, data, maxSize, &processedLoc, NULL);
30     data = (void *)((unsigned char *) data + processedLoc);
31     maxSize -= processedLoc;
32     processedSize += processedLoc;
33     if (processedLoc == 0) {
34       if (res)
35         return SZ_ERROR_READ;
36       else
37         break;
38     }
39   } while (maxSize > 0);
40
41   *size = processedSize;
42   return SZ_OK;
43 }
44
45 SRes SzFileSeekImp(void *object, Int64 *pos, ESzSeek origin) {
46   CFileInStream *s = (CFileInStream *) object;
47   LARGE_INTEGER value;
48   value.LowPart = (DWORD) *pos;
49   value.HighPart = (LONG) ((UInt64) *pos >> 32);
50   DWORD moveMethod;
51   switch (origin) {
52     case SZ_SEEK_SET:
53       moveMethod = FILE_BEGIN;
54       break;
55     case SZ_SEEK_CUR:
56       moveMethod = FILE_CURRENT;
57       break;
58     case SZ_SEEK_END:
59       moveMethod = FILE_END;
60       break;
61     default:
62       return SZ_ERROR_PARAM;
63   }
64   value.LowPart = SetFilePointer(s->file.handle, value.LowPart, &value.HighPart,
65                                  moveMethod);
66   *pos = ((Int64)value.HighPart << 32) | value.LowPart;
67   return ((value.LowPart == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) ?
68       SZ_ERROR_FAIL : SZ_OK;
69 }
70
71 SRes SzFileReadImp(void *object, void *buffer, size_t *size) {
72   CFileInStream *s = (CFileInStream *) object;
73   return LzmaReadFile(s->file.handle, buffer, size);
74 }
75
76 }  // namespace
77
78 // static
79 int32 LzmaUtil::UnPackArchive(const std::wstring& archive,
80                              const std::wstring& output_dir,
81                              std::wstring* output_file) {
82   VLOG(1) << "Opening archive " << archive;
83   LzmaUtil lzma_util;
84   DWORD ret;
85   if ((ret = lzma_util.OpenArchive(archive)) != NO_ERROR) {
86     LOG(ERROR) << "Unable to open install archive: " << archive
87                << ", error: " << ret;
88   } else {
89     VLOG(1) << "Uncompressing archive to path " << output_dir;
90     if ((ret = lzma_util.UnPack(output_dir, output_file)) != NO_ERROR) {
91       LOG(ERROR) << "Unable to uncompress archive: " << archive
92                  << ", error: " << ret;
93     }
94     lzma_util.CloseArchive();
95   }
96
97   return ret;
98 }
99
100 LzmaUtil::LzmaUtil() : archive_handle_(NULL) {}
101
102 LzmaUtil::~LzmaUtil() {
103   CloseArchive();
104 }
105
106 DWORD LzmaUtil::OpenArchive(const std::wstring& archivePath) {
107   // Make sure file is not already open.
108   CloseArchive();
109
110   DWORD ret = NO_ERROR;
111   archive_handle_ = CreateFile(archivePath.c_str(), GENERIC_READ,
112       FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
113   if (archive_handle_ == INVALID_HANDLE_VALUE) {
114     archive_handle_ = NULL;  // The rest of the code only checks for NULL.
115     ret = GetLastError();
116   }
117   return ret;
118 }
119
120 DWORD LzmaUtil::UnPack(const std::wstring& location) {
121   return UnPack(location, NULL);
122 }
123
124 DWORD LzmaUtil::UnPack(const std::wstring& location,
125                        std::wstring* output_file) {
126   if (!archive_handle_)
127     return ERROR_INVALID_HANDLE;
128
129   CFileInStream archiveStream;
130   CLookToRead lookStream;
131   CSzArEx db;
132   ISzAlloc allocImp;
133   ISzAlloc allocTempImp;
134   DWORD ret = NO_ERROR;
135
136   archiveStream.file.handle = archive_handle_;
137   archiveStream.s.Read = SzFileReadImp;
138   archiveStream.s.Seek = SzFileSeekImp;
139   LookToRead_CreateVTable(&lookStream, false);
140   lookStream.realStream = &archiveStream.s;
141
142   allocImp.Alloc = SzAlloc;
143   allocImp.Free = SzFree;
144   allocTempImp.Alloc = SzAllocTemp;
145   allocTempImp.Free = SzFreeTemp;
146
147   CrcGenerateTable();
148   SzArEx_Init(&db);
149   if ((ret = SzArEx_Open(&db, &lookStream.s,
150                          &allocImp, &allocTempImp)) != SZ_OK) {
151     LOG(ERROR) << L"Error returned by SzArchiveOpen: " << ret;
152     return ERROR_INVALID_HANDLE;
153   }
154
155   Byte *outBuffer = 0; // it must be 0 before first call for each new archive
156   UInt32 blockIndex = 0xFFFFFFFF; // can have any value if outBuffer = 0
157   size_t outBufferSize = 0;  // can have any value if outBuffer = 0
158
159   for (unsigned int i = 0; i < db.db.NumFiles; i++) {
160     DWORD written;
161     size_t offset;
162     size_t outSizeProcessed;
163     CSzFileItem *f = db.db.Files + i;
164
165     if ((ret = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex,
166                          &outBuffer, &outBufferSize, &offset, &outSizeProcessed,
167                          &allocImp, &allocTempImp)) != SZ_OK) {
168       LOG(ERROR) << L"Error returned by SzExtract: " << ret;
169       ret = ERROR_INVALID_HANDLE;
170       break;
171     }
172
173     size_t file_name_length = SzArEx_GetFileNameUtf16(&db, i, NULL);
174     if (file_name_length < 1) {
175       LOG(ERROR) << L"Couldn't get file name";
176       ret = ERROR_INVALID_HANDLE;
177       break;
178     }
179     std::vector<UInt16> file_name(file_name_length);
180     SzArEx_GetFileNameUtf16(&db, i, &file_name[0]);
181     // |file_name| is NULL-terminated.
182     base::FilePath file_path = base::FilePath(location).Append(
183         base::FilePath::StringType(file_name.begin(), file_name.end() - 1));
184
185     if (output_file)
186       *output_file = file_path.value();
187
188     // If archive entry is directory create it and move on to the next entry.
189     if (f->IsDir) {
190       CreateDirectory(file_path);
191       continue;
192     }
193
194     CreateDirectory(file_path.DirName());
195
196     HANDLE hFile;
197     hFile = CreateFile(file_path.value().c_str(), GENERIC_WRITE, 0, NULL,
198                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
199     if (hFile == INVALID_HANDLE_VALUE)  {
200       ret = GetLastError();
201       LOG(ERROR) << L"Error returned by CreateFile: " << ret;
202       break;
203     }
204
205     if ((!WriteFile(hFile, outBuffer + offset, (DWORD) outSizeProcessed,
206                     &written, NULL)) ||
207         (written != outSizeProcessed)) {
208       ret = GetLastError();
209       CloseHandle(hFile);
210       LOG(ERROR) << L"Error returned by WriteFile: " << ret;
211       break;
212     }
213
214     if (f->MTimeDefined) {
215       if (!SetFileTime(hFile, NULL, NULL,
216                        (const FILETIME *)&(f->MTime))) {
217         ret = GetLastError();
218         CloseHandle(hFile);
219         LOG(ERROR) << L"Error returned by SetFileTime: " << ret;
220         break;
221       }
222     }
223     if (!CloseHandle(hFile)) {
224       ret = GetLastError();
225       LOG(ERROR) << L"Error returned by CloseHandle: " << ret;
226       break;
227     }
228   }  // for loop
229
230   IAlloc_Free(&allocImp, outBuffer);
231   SzArEx_Free(&db, &allocImp);
232   return ret;
233 }
234
235 void LzmaUtil::CloseArchive() {
236   if (archive_handle_) {
237     CloseHandle(archive_handle_);
238     archive_handle_ = NULL;
239   }
240 }
241
242 bool LzmaUtil::CreateDirectory(const base::FilePath& dir) {
243   bool ret = true;
244   if (directories_created_.find(dir.value()) == directories_created_.end()) {
245     ret = file_util::CreateDirectory(dir);
246     if (ret)
247       directories_created_.insert(dir.value());
248   }
249   return ret;
250 }