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 = base::CreateDirectory(dir); 246 if (ret) 247 directories_created_.insert(dir.value()); 248 } 249 return ret; 250 } 251