普通文本  |  251行  |  7.12 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/installer/util/lzma_util.h"

#include "base/file_util.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"

extern "C" {
#include "third_party/lzma_sdk/7z.h"
#include "third_party/lzma_sdk/7zAlloc.h"
#include "third_party/lzma_sdk/7zCrc.h"
#include "third_party/lzma_sdk/7zFile.h"
}


namespace {

SRes LzmaReadFile(HANDLE file, void *data, size_t *size) {
  if (*size == 0)
    return SZ_OK;

  size_t processedSize = 0;
  DWORD maxSize = *size;
  do {
    DWORD processedLoc = 0;
    BOOL res = ReadFile(file, data, maxSize, &processedLoc, NULL);
    data = (void *)((unsigned char *) data + processedLoc);
    maxSize -= processedLoc;
    processedSize += processedLoc;
    if (processedLoc == 0) {
      if (res)
        return SZ_ERROR_READ;
      else
        break;
    }
  } while (maxSize > 0);

  *size = processedSize;
  return SZ_OK;
}

SRes SzFileSeekImp(void *object, Int64 *pos, ESzSeek origin) {
  CFileInStream *s = (CFileInStream *) object;
  LARGE_INTEGER value;
  value.LowPart = (DWORD) *pos;
  value.HighPart = (LONG) ((UInt64) *pos >> 32);
  DWORD moveMethod;
  switch (origin) {
    case SZ_SEEK_SET:
      moveMethod = FILE_BEGIN;
      break;
    case SZ_SEEK_CUR:
      moveMethod = FILE_CURRENT;
      break;
    case SZ_SEEK_END:
      moveMethod = FILE_END;
      break;
    default:
      return SZ_ERROR_PARAM;
  }
  value.LowPart = SetFilePointer(s->file.handle, value.LowPart, &value.HighPart,
                                 moveMethod);
  *pos = ((Int64)value.HighPart << 32) | value.LowPart;
  return ((value.LowPart == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) ?
      SZ_ERROR_FAIL : SZ_OK;
}

SRes SzFileReadImp(void *object, void *buffer, size_t *size) {
  CFileInStream *s = (CFileInStream *) object;
  return LzmaReadFile(s->file.handle, buffer, size);
}

}  // namespace

// static
int32 LzmaUtil::UnPackArchive(const std::wstring& archive,
                             const std::wstring& output_dir,
                             std::wstring* output_file) {
  VLOG(1) << "Opening archive " << archive;
  LzmaUtil lzma_util;
  DWORD ret;
  if ((ret = lzma_util.OpenArchive(archive)) != NO_ERROR) {
    LOG(ERROR) << "Unable to open install archive: " << archive
               << ", error: " << ret;
  } else {
    VLOG(1) << "Uncompressing archive to path " << output_dir;
    if ((ret = lzma_util.UnPack(output_dir, output_file)) != NO_ERROR) {
      LOG(ERROR) << "Unable to uncompress archive: " << archive
                 << ", error: " << ret;
    }
    lzma_util.CloseArchive();
  }

  return ret;
}

LzmaUtil::LzmaUtil() : archive_handle_(NULL) {}

LzmaUtil::~LzmaUtil() {
  CloseArchive();
}

DWORD LzmaUtil::OpenArchive(const std::wstring& archivePath) {
  // Make sure file is not already open.
  CloseArchive();

  DWORD ret = NO_ERROR;
  archive_handle_ = CreateFile(archivePath.c_str(), GENERIC_READ,
      FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (archive_handle_ == INVALID_HANDLE_VALUE) {
    archive_handle_ = NULL;  // The rest of the code only checks for NULL.
    ret = GetLastError();
  }
  return ret;
}

DWORD LzmaUtil::UnPack(const std::wstring& location) {
  return UnPack(location, NULL);
}

DWORD LzmaUtil::UnPack(const std::wstring& location,
                       std::wstring* output_file) {
  if (!archive_handle_)
    return ERROR_INVALID_HANDLE;

  CFileInStream archiveStream;
  CLookToRead lookStream;
  CSzArEx db;
  ISzAlloc allocImp;
  ISzAlloc allocTempImp;
  DWORD ret = NO_ERROR;

  archiveStream.file.handle = archive_handle_;
  archiveStream.s.Read = SzFileReadImp;
  archiveStream.s.Seek = SzFileSeekImp;
  LookToRead_CreateVTable(&lookStream, false);
  lookStream.realStream = &archiveStream.s;

  allocImp.Alloc = SzAlloc;
  allocImp.Free = SzFree;
  allocTempImp.Alloc = SzAllocTemp;
  allocTempImp.Free = SzFreeTemp;

  CrcGenerateTable();
  SzArEx_Init(&db);
  if ((ret = SzArEx_Open(&db, &lookStream.s,
                         &allocImp, &allocTempImp)) != SZ_OK) {
    LOG(ERROR) << L"Error returned by SzArchiveOpen: " << ret;
    return ERROR_INVALID_HANDLE;
  }

  Byte *outBuffer = 0; // it must be 0 before first call for each new archive
  UInt32 blockIndex = 0xFFFFFFFF; // can have any value if outBuffer = 0
  size_t outBufferSize = 0;  // can have any value if outBuffer = 0

  for (unsigned int i = 0; i < db.db.NumFiles; i++) {
    DWORD written;
    size_t offset;
    size_t outSizeProcessed;
    CSzFileItem *f = db.db.Files + i;

    if ((ret = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex,
                         &outBuffer, &outBufferSize, &offset, &outSizeProcessed,
                         &allocImp, &allocTempImp)) != SZ_OK) {
      LOG(ERROR) << L"Error returned by SzExtract: " << ret;
      ret = ERROR_INVALID_HANDLE;
      break;
    }

    size_t file_name_length = SzArEx_GetFileNameUtf16(&db, i, NULL);
    if (file_name_length < 1) {
      LOG(ERROR) << L"Couldn't get file name";
      ret = ERROR_INVALID_HANDLE;
      break;
    }
    std::vector<UInt16> file_name(file_name_length);
    SzArEx_GetFileNameUtf16(&db, i, &file_name[0]);
    // |file_name| is NULL-terminated.
    base::FilePath file_path = base::FilePath(location).Append(
        base::FilePath::StringType(file_name.begin(), file_name.end() - 1));

    if (output_file)
      *output_file = file_path.value();

    // If archive entry is directory create it and move on to the next entry.
    if (f->IsDir) {
      CreateDirectory(file_path);
      continue;
    }

    CreateDirectory(file_path.DirName());

    HANDLE hFile;
    hFile = CreateFile(file_path.value().c_str(), GENERIC_WRITE, 0, NULL,
                       CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)  {
      ret = GetLastError();
      LOG(ERROR) << L"Error returned by CreateFile: " << ret;
      break;
    }

    if ((!WriteFile(hFile, outBuffer + offset, (DWORD) outSizeProcessed,
                    &written, NULL)) ||
        (written != outSizeProcessed)) {
      ret = GetLastError();
      CloseHandle(hFile);
      LOG(ERROR) << L"Error returned by WriteFile: " << ret;
      break;
    }

    if (f->MTimeDefined) {
      if (!SetFileTime(hFile, NULL, NULL,
                       (const FILETIME *)&(f->MTime))) {
        ret = GetLastError();
        CloseHandle(hFile);
        LOG(ERROR) << L"Error returned by SetFileTime: " << ret;
        break;
      }
    }
    if (!CloseHandle(hFile)) {
      ret = GetLastError();
      LOG(ERROR) << L"Error returned by CloseHandle: " << ret;
      break;
    }
  }  // for loop

  IAlloc_Free(&allocImp, outBuffer);
  SzArEx_Free(&db, &allocImp);
  return ret;
}

void LzmaUtil::CloseArchive() {
  if (archive_handle_) {
    CloseHandle(archive_handle_);
    archive_handle_ = NULL;
  }
}

bool LzmaUtil::CreateDirectory(const base::FilePath& dir) {
  bool ret = true;
  if (directories_created_.find(dir.value()) == directories_created_.end()) {
    ret = base::CreateDirectory(dir);
    if (ret)
      directories_created_.insert(dir.value());
  }
  return ret;
}