Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bc51d1e authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Add functions in recovery/minzip to libziparchive"

parents c1f74ca5 18c25920
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -130,6 +130,8 @@ int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
int32_t OpenArchiveFd(const int fd, const char* debugFileName,
                      ZipArchiveHandle *handle, bool assume_ownership = true);

int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
                              ZipArchiveHandle *handle);
/*
 * Close archive, releasing resources associated with it. This will
 * unmap the central directory of the zipfile and free all internal
@@ -214,6 +216,17 @@ int GetFileDescriptor(const ZipArchiveHandle handle);

const char* ErrorCodeString(int32_t error_code);

#if !defined(_WIN32)
typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);

/*
 * Stream the uncompressed data through the supplied function,
 * passing cookie to it each time it gets called.
*/
int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
        ProcessZipEntryFunction func, void* cookie);
#endif

__END_DECLS

#endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
+1.04 MiB

File added.

No diff preview for this file type.

+184 −66
Original line number Diff line number Diff line
@@ -215,19 +215,14 @@ static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
  return 0;
}

static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
                                    ZipArchive* archive, off64_t file_length,
                                    off64_t read_amount, uint8_t* scan_buffer) {
static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
                                    off64_t file_length, off64_t read_amount,
                                    uint8_t* scan_buffer) {
  const off64_t search_start = file_length - read_amount;

  if (lseek64(fd, search_start, SEEK_SET) != search_start) {
    ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
          strerror(errno));
    return kIoError;
  }
  if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
    ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
          strerror(errno));
  if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
    ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
          static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
    return kIoError;
  }

@@ -287,9 +282,11 @@ static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
   * It all looks good.  Create a mapping for the CD, and set the fields
   * in archive.
   */
  if (!archive->directory_map.create(debug_file_name, fd,

  if (!archive->InitializeCentralDirectory(debug_file_name,
                                           static_cast<off64_t>(eocd->cd_start_offset),
          static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
                                           static_cast<size_t>(eocd->cd_size))) {
    ALOGE("Zip: failed to intialize central directory.\n");
    return kMmapFailed;
  }

@@ -304,18 +301,16 @@ static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
 *
 * On success, returns 0 after populating fields from the EOCD area:
 *   directory_offset
 *   directory_map
 *   directory_ptr
 *   num_entries
 */
static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
                                   ZipArchive* archive) {
static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {

  // Test file length. We use lseek64 to make sure the file
  // is small enough to be a zip file (Its size must be less than
  // 0xffffffff bytes).
  off64_t file_length = lseek64(fd, 0, SEEK_END);
  off64_t file_length = archive->mapped_zip.GetFileLength();
  if (file_length == -1) {
    ALOGV("Zip: lseek on fd %d failed", fd);
    return kInvalidFile;
  }

@@ -346,11 +341,9 @@ static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
    read_amount = file_length;
  }

  uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
  int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
                                        file_length, read_amount, scan_buffer);

  free(scan_buffer);
  std::vector<uint8_t> scan_buffer(read_amount);
  int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
                                        scan_buffer.data());
  return result;
}

@@ -361,9 +354,8 @@ static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
 * Returns 0 on success.
 */
static int32_t ParseZipArchive(ZipArchive* archive) {
  const uint8_t* const cd_ptr =
      reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
  const size_t cd_length = archive->directory_map.getDataLength();
  const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
  const size_t cd_length = archive->central_directory.GetMapLength();
  const uint16_t num_entries = archive->num_entries;

  /*
@@ -437,7 +429,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) {
static int32_t OpenArchiveInternal(ZipArchive* archive,
                                   const char* debug_file_name) {
  int32_t result = -1;
  if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
  if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
    return result;
  }

@@ -468,6 +460,13 @@ int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
  return OpenArchiveInternal(archive, fileName);
}

int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
                              ZipArchiveHandle *handle) {
  ZipArchive* archive = new ZipArchive(address, length);
  *handle = archive;
  return OpenArchiveInternal(archive, debug_file_name);
}

/*
 * Close a ZipArchive, closing the file and freeing the contents.
 */
@@ -477,10 +476,10 @@ void CloseArchive(ZipArchiveHandle handle) {
  delete archive;
}

static int32_t UpdateEntryFromDataDescriptor(int fd,
static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
                                             ZipEntry *entry) {
  uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
  if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
  if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
    return kIoError;
  }

@@ -495,23 +494,6 @@ static int32_t UpdateEntryFromDataDescriptor(int fd,
  return 0;
}

// Attempts to read |len| bytes into |buf| at offset |off|.
// On non-Windows platforms, callers are guaranteed that the |fd|
// offset is unchanged and there is no side effect to this call.
//
// On Windows platforms this is not thread-safe.
static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
#if !defined(_WIN32)
  return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
#else
  if (lseek64(fd, off, SEEK_SET) != off) {
    ALOGW("Zip: failed seek to offset %" PRId64, off);
    return false;
  }
  return android::base::ReadFully(fd, buf, len);
#endif
}

static int32_t FindEntry(const ZipArchive* archive, const int ent,
                         ZipEntry* data) {
  const uint16_t nameLen = archive->hash_table[ent].name_length;
@@ -525,9 +507,8 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent,
  // This is the base of our mmapped region, we have to sanity check that
  // the name that's in the hash table is a pointer to a location within
  // this mapped region.
  const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
    archive->directory_map.getDataPtr());
  if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
  const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
  if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
    ALOGW("Zip: Invalid entry pointer");
    return kInvalidOffset;
  }
@@ -559,7 +540,7 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent,
  }

  uint8_t lfh_buf[sizeof(LocalFileHeader)];
  if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
  if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
    ALOGW("Zip: failed reading lfh name from offset %" PRId64,
        static_cast<int64_t>(local_header_offset));
    return kIoError;
@@ -599,19 +580,16 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent,
      return kInvalidOffset;
    }

    uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
    if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
    std::vector<uint8_t> name_buf(nameLen);
    if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
      ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
      free(name_buf);
      return kIoError;
    }

    if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
      free(name_buf);
    if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
      return kInconsistentInformation;
    }

    free(name_buf);
  } else {
    ALOGW("Zip: lfh name did not match central directory.");
    return kInconsistentInformation;
@@ -881,7 +859,7 @@ static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
}
#pragma GCC diagnostic pop

static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
                                    Writer* writer, uint64_t* crc_out) {
  const size_t kBufSize = 32768;
  std::vector<uint8_t> read_buf(kBufSize);
@@ -931,7 +909,7 @@ static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
    /* read as much as we can */
    if (zstream.avail_in == 0) {
      const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
      if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
      if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
        ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
        return kIoError;
      }
@@ -979,7 +957,7 @@ static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
  return 0;
}

static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
                                 uint64_t *crc_out) {
  static const uint32_t kBufSize = 32768;
  std::vector<uint8_t> buf(kBufSize);
@@ -993,7 +971,7 @@ static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
    // value.
    const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
    if (!android::base::ReadFully(fd, buf.data(), block_size)) {
    if (!mapped_zip.ReadData(buf.data(), block_size)) {
      ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
      return kIoError;
    }
@@ -1016,7 +994,7 @@ int32_t ExtractToWriter(ZipArchiveHandle handle,
  const uint16_t method = entry->method;
  off64_t data_offset = entry->offset;

  if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
  if (!archive->mapped_zip.SeekToOffset(data_offset)) {
    ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
    return kIoError;
  }
@@ -1025,13 +1003,13 @@ int32_t ExtractToWriter(ZipArchiveHandle handle,
  int32_t return_value = -1;
  uint64_t crc = 0;
  if (method == kCompressStored) {
    return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
    return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
  } else if (method == kCompressDeflated) {
    return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
    return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
  }

  if (!return_value && entry->has_data_descriptor) {
    return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
    return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
    if (return_value) {
      return return_value;
    }
@@ -1072,7 +1050,7 @@ const char* ErrorCodeString(int32_t error_code) {
}

int GetFileDescriptor(const ZipArchiveHandle handle) {
  return reinterpret_cast<ZipArchive*>(handle)->fd;
  return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
}

ZipString::ZipString(const char* entry_name)
@@ -1081,3 +1059,143 @@ ZipString::ZipString(const char* entry_name)
  CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
  name_length = static_cast<uint16_t>(len);
}

#if !defined(_WIN32)
class ProcessWriter : public Writer {
 public:
  ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
    proc_function_(func),
    cookie_(cookie) {
  }

  virtual bool Append(uint8_t* buf, size_t buf_size) override {
    return proc_function_(buf, buf_size, cookie_);
  }

 private:
  ProcessZipEntryFunction proc_function_;
  void* cookie_;
};

int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
                                ProcessZipEntryFunction func, void* cookie) {
  ProcessWriter writer(func, cookie);
  return ExtractToWriter(handle, entry, &writer);
}

#endif //!defined(_WIN32)

int MappedZipFile::GetFileDescriptor() const {
  if (!has_fd_) {
    ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
    return -1;
  }
  return fd_;
}

void* MappedZipFile::GetBasePtr() const {
  if (has_fd_) {
    ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
    return nullptr;
  }
  return base_ptr_;
}

off64_t MappedZipFile::GetFileLength() const {
  if (has_fd_) {
    off64_t result = lseek64(fd_, 0, SEEK_END);
    if (result == -1) {
      ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
    }
    return result;
  } else {
    if (base_ptr_ == nullptr) {
      ALOGE("Zip: invalid file map\n");
      return -1;
    }
    return static_cast<off64_t>(data_length_);
  }
}

bool MappedZipFile::SeekToOffset(off64_t offset) {
  if (has_fd_) {
    if (lseek64(fd_, offset, SEEK_SET) != offset) {
      ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
      return false;
    }
    return true;
  } else {
    if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
            data_length_);
      return false;
    }

    read_pos_ = offset;
    return true;
  }
}

bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
  if (has_fd_) {
    if(!android::base::ReadFully(fd_, buffer, read_amount)) {
      ALOGE("Zip: read from %d failed\n", fd_);
      return false;
    }
  } else {
    memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
    read_pos_ += read_amount;
  }
  return true;
}

// Attempts to read |len| bytes into |buf| at offset |off|.
bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
#if !defined(_WIN32)
  if (has_fd_) {
    if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
      ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
      return false;
    }
    return true;
  }
#endif
  if (!SeekToOffset(off)) {
    return false;
  }
  return ReadData(buf, len);

}

void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
  base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
  length_ = cd_size;
}

bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                            size_t cd_size) {
  if (mapped_zip.HasFd()) {
    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
                               cd_start_offset, cd_size, true /* read only */)) {
      return false;
    }

    CHECK_EQ(directory_map->getDataLength(), cd_size);
    central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
  } else {
    if (mapped_zip.GetBasePtr() == nullptr) {
      ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
      return false;
    }
    if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
        mapped_zip.GetFileLength()) {
      ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
            "start_offset %"  PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
            static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
      return false;
    }

    central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
  }
  return true;
}
+92 −10
Original line number Diff line number Diff line
@@ -21,17 +21,83 @@
#include <stdlib.h>
#include <unistd.h>

#include <memory>
#include <vector>

#include <utils/FileMap.h>
#include <ziparchive/zip_archive.h>

class MappedZipFile {
 public:
  explicit MappedZipFile(const int fd) :
    has_fd_(true),
    fd_(fd),
    base_ptr_(nullptr),
    data_length_(0),
    read_pos_(0) {}

  explicit MappedZipFile(void* address, size_t length) :
    has_fd_(false),
    fd_(-1),
    base_ptr_(address),
    data_length_(static_cast<off64_t>(length)),
    read_pos_(0) {}

  bool HasFd() const {return has_fd_;}

  int GetFileDescriptor() const;

  void* GetBasePtr() const;

  off64_t GetFileLength() const;

  bool SeekToOffset(off64_t offset);

  bool ReadData(uint8_t* buffer, size_t read_amount);

  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);

 private:
  // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
  // from the file. Otherwise, we're opening the archive from a memory mapped
  // file. In that case, base_ptr_ points to the start of the memory region and
  // data_length_ defines the file length.
  const bool has_fd_;

  const int fd_;

  void* const base_ptr_;
  const off64_t data_length_;
  // read_pos_ is the offset to the base_ptr_ where we read data from.
  size_t read_pos_;
};

class CentralDirectory {
 public:
  CentralDirectory(void) :
    base_ptr_(nullptr),
    length_(0) {}

  const uint8_t* GetBasePtr() const {return base_ptr_;}

  size_t GetMapLength() const {return length_;}

  void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);

 private:
  const uint8_t* base_ptr_;
  size_t length_;
};

struct ZipArchive {
  // open Zip archive
  const int fd;
  mutable MappedZipFile mapped_zip;
  const bool close_file;

  // mapped central directory area
  off64_t directory_offset;
  android::FileMap directory_map;
  CentralDirectory central_directory;
  std::unique_ptr<android::FileMap> directory_map;

  // number of entries in the Zip archive
  uint16_t num_entries;
@@ -44,20 +110,36 @@ struct ZipArchive {
  ZipString* hash_table;

  ZipArchive(const int fd, bool assume_ownership) :
      fd(fd),
    mapped_zip(fd),
    close_file(assume_ownership),
    directory_offset(0),
    central_directory(),
    directory_map(new android::FileMap()),
    num_entries(0),
    hash_table_size(0),
      hash_table(NULL) {}
    hash_table(nullptr) {}

  ZipArchive(void* address, size_t length) :
    mapped_zip(address, length),
    close_file(false),
    directory_offset(0),
    central_directory(),
    directory_map(new android::FileMap()),
    num_entries(0),
    hash_table_size(0),
    hash_table(nullptr) {}

  ~ZipArchive() {
    if (close_file && fd >= 0) {
      close(fd);
    if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
      close(mapped_zip.GetFileDescriptor());
    }

    free(hash_table);
  }

  bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                  size_t cd_size);

};

#endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+3 −3
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ static constexpr size_t kBufSize = 65535;
bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
  off64_t data_offset = entry.offset;
  if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
  if (!archive->mapped_zip.SeekToOffset(data_offset)) {
    ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
    return false;
  }
@@ -88,7 +88,7 @@ const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
  size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
  errno = 0;
  if (!android::base::ReadFully(archive->fd, data_.data(), bytes)) {
  if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
    if (errno != 0) {
      ALOGE("Error reading from archive fd: %s", strerror(errno));
    } else {
@@ -209,7 +209,7 @@ const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
      size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
      ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
      errno = 0;
      if (!android::base::ReadFully(archive->fd, in_.data(), bytes)) {
      if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
        if (errno != 0) {
          ALOGE("Error reading from archive fd: %s", strerror(errno));
        } else {
Loading