Home | History | Annotate | Download | only in util
      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/files/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