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 #include "chrome/installer/util/lzma_util.h"
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
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"
21 SRes
LzmaReadFile(HANDLE file
, void *data
, size_t *size
) {
25 size_t processedSize
= 0;
26 DWORD maxSize
= *size
;
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) {
39 } while (maxSize
> 0);
41 *size
= processedSize
;
45 SRes
SzFileSeekImp(void *object
, Int64
*pos
, ESzSeek origin
) {
46 CFileInStream
*s
= (CFileInStream
*) object
;
48 value
.LowPart
= (DWORD
) *pos
;
49 value
.HighPart
= (LONG
) ((UInt64
) *pos
>> 32);
53 moveMethod
= FILE_BEGIN
;
56 moveMethod
= FILE_CURRENT
;
59 moveMethod
= FILE_END
;
62 return SZ_ERROR_PARAM
;
64 value
.LowPart
= SetFilePointer(s
->file
.handle
, value
.LowPart
, &value
.HighPart
,
66 *pos
= ((Int64
)value
.HighPart
<< 32) | value
.LowPart
;
67 return ((value
.LowPart
== 0xFFFFFFFF) && (GetLastError() != NO_ERROR
)) ?
68 SZ_ERROR_FAIL
: SZ_OK
;
71 SRes
SzFileReadImp(void *object
, void *buffer
, size_t *size
) {
72 CFileInStream
*s
= (CFileInStream
*) object
;
73 return LzmaReadFile(s
->file
.handle
, buffer
, size
);
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
;
85 if ((ret
= lzma_util
.OpenArchive(archive
)) != NO_ERROR
) {
86 LOG(ERROR
) << "Unable to open install archive: " << archive
87 << ", error: " << ret
;
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
;
94 lzma_util
.CloseArchive();
100 LzmaUtil::LzmaUtil() : archive_handle_(NULL
) {}
102 LzmaUtil::~LzmaUtil() {
106 DWORD
LzmaUtil::OpenArchive(const std::wstring
& archivePath
) {
107 // Make sure file is not already open.
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();
120 DWORD
LzmaUtil::UnPack(const std::wstring
& location
) {
121 return UnPack(location
, NULL
);
124 DWORD
LzmaUtil::UnPack(const std::wstring
& location
,
125 std::wstring
* output_file
) {
126 if (!archive_handle_
)
127 return ERROR_INVALID_HANDLE
;
129 CFileInStream archiveStream
;
130 CLookToRead lookStream
;
133 ISzAlloc allocTempImp
;
134 DWORD ret
= NO_ERROR
;
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
;
142 allocImp
.Alloc
= SzAlloc
;
143 allocImp
.Free
= SzFree
;
144 allocTempImp
.Alloc
= SzAllocTemp
;
145 allocTempImp
.Free
= SzFreeTemp
;
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
;
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
159 for (unsigned int i
= 0; i
< db
.db
.NumFiles
; i
++) {
162 size_t outSizeProcessed
;
163 CSzFileItem
*f
= db
.db
.Files
+ i
;
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
;
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
;
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));
186 *output_file
= file_path
.value();
188 // If archive entry is directory create it and move on to the next entry.
190 CreateDirectory(file_path
);
194 CreateDirectory(file_path
.DirName());
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
;
205 if ((!WriteFile(hFile
, outBuffer
+ offset
, (DWORD
) outSizeProcessed
,
207 (written
!= outSizeProcessed
)) {
208 ret
= GetLastError();
210 LOG(ERROR
) << L
"Error returned by WriteFile: " << ret
;
214 if (f
->MTimeDefined
) {
215 if (!SetFileTime(hFile
, NULL
, NULL
,
216 (const FILETIME
*)&(f
->MTime
))) {
217 ret
= GetLastError();
219 LOG(ERROR
) << L
"Error returned by SetFileTime: " << ret
;
223 if (!CloseHandle(hFile
)) {
224 ret
= GetLastError();
225 LOG(ERROR
) << L
"Error returned by CloseHandle: " << ret
;
230 IAlloc_Free(&allocImp
, outBuffer
);
231 SzArEx_Free(&db
, &allocImp
);
235 void LzmaUtil::CloseArchive() {
236 if (archive_handle_
) {
237 CloseHandle(archive_handle_
);
238 archive_handle_
= NULL
;
242 bool LzmaUtil::CreateDirectory(const base::FilePath
& dir
) {
244 if (directories_created_
.find(dir
.value()) == directories_created_
.end()) {
245 ret
= base::CreateDirectory(dir
);
247 directories_created_
.insert(dir
.value());