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

Commit 18c00858 authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Add definition for zip64 struct"

parents 74601257 69ee4b70
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ struct ZipArchiveInfo {
  /** The size in bytes of the archive itself. Used by zipinfo. */
  off64_t archive_size;
  /** The number of entries in the archive. */
  size_t entry_count;
  uint64_t entry_count;
};

/**
+116 −57
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@ static const bool kCrcChecksEnabled = false;
// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);

// Set a reasonable cap (256 GiB) for the zip file size. So the data is always valid when
// we parse the fields in cd or local headers as 64 bits signed integers.
static constexpr uint64_t kMaxFileLength = 256 * static_cast<uint64_t>(1u << 30u);

/*
 * A Read-only Zip archive.
 *
@@ -125,12 +129,27 @@ ZipArchive::~ZipArchive() {
  }
}

static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
struct CentralDirectoryInfo {
  uint64_t num_records;
  // The size of the central directory (in bytes).
  uint64_t cd_size;
  // The offset of the start of the central directory, relative
  // to the start of the file.
  uint64_t cd_start_offset;
};

static ZipError FindCentralDirectoryInfoForZip64(CentralDirectoryInfo* /* cdInfo */) {
  ALOGW("Zip: Parsing zip64 EOCD isn't supported yet.");
  return kInvalidFile;
}

static ZipError FindCentralDirectoryInfo(const char* debug_file_name, ZipArchive* archive,
                                         off64_t file_length, uint32_t read_amount,
                                    uint8_t* scan_buffer) {
                                         CentralDirectoryInfo* cdInfo) {
  std::vector<uint8_t> scan_buffer(read_amount);
  const off64_t search_start = file_length - read_amount;

  if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
  if (!archive->mapped_zip.ReadAtOffset(scan_buffer.data(), 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;
@@ -159,7 +178,7 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc
  }

  const off64_t eocd_offset = search_start + i;
  const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
  auto eocd = reinterpret_cast<const EocdRecord*>(scan_buffer.data() + i);
  /*
   * Verify that there's no trailing space at the end of the central directory
   * and its comment.
@@ -171,6 +190,13 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc
    return kInvalidFile;
  }

  // One of the field is 0xFFFFFFFF, look for the zip64 EOCD instead.
  if (eocd->cd_size == UINT32_MAX || eocd->cd_start_offset == UINT32_MAX) {
    ALOGV("Looking for the zip64 EOCD, cd_size: %" PRIu32 "cd_start_offset: %" PRId32,
          eocd->cd_size, eocd->cd_start_offset);
    return FindCentralDirectoryInfoForZip64(cdInfo);
  }

  /*
   * Grab the CD offset and size, and the number of entries in the
   * archive and verify that they look reasonable.
@@ -180,47 +206,29 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc
          eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
    return kInvalidOffset;
  }
  if (eocd->num_records == 0) {
#if defined(__ANDROID__)
    ALOGW("Zip: empty archive?");
#endif
    return kEmptyArchive;
  }

  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
        eocd->cd_size, eocd->cd_start_offset);

  // It all looks good.  Create a mapping for the CD, and set the fields
  // in archive.
  if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
                                           static_cast<size_t>(eocd->cd_size))) {
    return kMmapFailed;
  }

  archive->num_entries = eocd->num_records;
  archive->directory_offset = eocd->cd_start_offset;

  return 0;
  *cdInfo = {.num_records = eocd->num_records,
             .cd_size = eocd->cd_size,
             .cd_start_offset = eocd->cd_start_offset};
  return kSuccess;
}

/*
 * Find the zip Central Directory and memory-map it.
 *
 * On success, returns 0 after populating fields from the EOCD area:
 * On success, returns kSuccess after populating fields from the EOCD area:
 *   directory_offset
 *   directory_ptr
 *   num_entries
 */
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).
static ZipError 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.
  off64_t file_length = archive->mapped_zip.GetFileLength();
  if (file_length == -1) {
    return kInvalidFile;
  }

  if (file_length > static_cast<off64_t>(0xffffffff)) {
  if (file_length > kMaxFileLength) {
    ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
    return kInvalidFile;
  }
@@ -247,12 +255,41 @@ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* arch
    read_amount = static_cast<uint32_t>(file_length);
  }

  std::vector<uint8_t> scan_buffer(read_amount);
  int32_t result =
      MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
  CentralDirectoryInfo cdInfo = {};
  if (auto result =
          FindCentralDirectoryInfo(debug_file_name, archive, file_length, read_amount, &cdInfo);
      result != kSuccess) {
    return result;
  }

  if (cdInfo.num_records == 0) {
#if defined(__ANDROID__)
    ALOGW("Zip: empty archive?");
#endif
    return kEmptyArchive;
  }

  if (cdInfo.cd_size >= SIZE_MAX) {
    ALOGW("Zip: The size of central directory doesn't fit in range of size_t: %" PRIu64,
          cdInfo.cd_size);
    return kInvalidFile;
  }

  ALOGV("+++ num_entries=%" PRIu64 " dir_size=%" PRIu64 " dir_offset=%" PRIu64, cdInfo.num_records,
        cdInfo.cd_size, cdInfo.cd_start_offset);

  // It all looks good.  Create a mapping for the CD, and set the fields in archive.
  if (!archive->InitializeCentralDirectory(static_cast<off64_t>(cdInfo.cd_start_offset),
                                           static_cast<size_t>(cdInfo.cd_size))) {
    return kMmapFailed;
  }

  archive->num_entries = cdInfo.num_records;
  archive->directory_offset = cdInfo.cd_start_offset;

  return kSuccess;
}

/*
 * Parses the Zip archive's Central Directory.  Allocates and populates the
 * hash table.
@@ -262,13 +299,12 @@ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* arch
static int32_t ParseZipArchive(ZipArchive* archive) {
  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;
  const uint64_t num_entries = archive->num_entries;

  // TODO(xunchang) parse the zip64 Eocd
  if (num_entries > UINT16_MAX) {
    archive->cd_entry_map = CdEntryMapZip64::Create();
  if (num_entries <= UINT16_MAX) {
    archive->cd_entry_map = CdEntryMapZip32::Create(static_cast<uint16_t>(num_entries));
  } else {
    archive->cd_entry_map = CdEntryMapZip32::Create(num_entries);
    archive->cd_entry_map = CdEntryMapZip64::Create();
  }
  if (archive->cd_entry_map == nullptr) {
    return kAllocationFailed;
@@ -280,9 +316,9 @@ static int32_t ParseZipArchive(ZipArchive* archive) {
   */
  const uint8_t* const cd_end = cd_ptr + cd_length;
  const uint8_t* ptr = cd_ptr;
  for (uint16_t i = 0; i < num_entries; i++) {
  for (uint64_t i = 0; i < num_entries; i++) {
    if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
      ALOGW("Zip: ran off the end (item #%" PRIu16 ", %zu bytes of central directory)", i,
      ALOGW("Zip: ran off the end (item #%" PRIu64 ", %zu bytes of central directory)", i,
            cd_length);
#if defined(__ANDROID__)
      android_errorWriteLog(0x534e4554, "36392138");
@@ -292,14 +328,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) {

    const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
    if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
      ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
      return kInvalidFile;
    }

    const off64_t local_header_offset = cdr->local_file_header_offset;
    if (local_header_offset >= archive->directory_offset) {
      ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
            static_cast<int64_t>(local_header_offset), i);
      ALOGW("Zip: missed a central dir sig (at %" PRIu64 ")", i);
      return kInvalidFile;
    }

@@ -308,15 +337,37 @@ static int32_t ParseZipArchive(ZipArchive* archive) {
    const uint16_t comment_length = cdr->comment_length;
    const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);

    if (file_name + file_name_length > cd_end) {
      ALOGW("Zip: file name for entry %" PRIu16
    if (file_name_length >= cd_length || file_name > cd_end - file_name_length) {
      ALOGW("Zip: file name for entry %" PRIu64
            " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
            i, file_name_length, cd_length);
      return kInvalidEntryName;
    }

    const uint8_t* extra_field = file_name + file_name_length;
    if (extra_length >= cd_length || extra_field > cd_end - extra_length) {
      ALOGW("Zip: extra field for entry %" PRIu64
            " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
            i, extra_length, cd_length);
      return kInvalidFile;
    }

    off64_t local_header_offset = cdr->local_file_header_offset;
    if (local_header_offset == UINT32_MAX) {
      // TODO(xunchang) parse the zip64 eocd
      ALOGW("Zip: Parsing zip64 cd entry isn't supported yet");
      return kInvalidFile;
    }

    if (local_header_offset >= archive->directory_offset) {
      ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu64,
            static_cast<int64_t>(local_header_offset), i);
      return kInvalidFile;
    }

    // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
    if (!IsValidEntryName(file_name, file_name_length)) {
      ALOGW("Zip: invalid file name at entry %" PRIu16, i);
      ALOGW("Zip: invalid file name at entry %" PRIu64, i);
      return kInvalidEntryName;
    }

@@ -331,7 +382,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) {

    ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
    if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu64, ptr - cd_ptr, cd_length, i);
      return kInvalidFile;
    }
  }
@@ -351,14 +402,14 @@ static int32_t ParseZipArchive(ZipArchive* archive) {
    return kInvalidFile;
  }

  ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
  ALOGV("+++ zip good scan %" PRIu64 " entries", num_entries);

  return 0;
}

static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
  int32_t result = MapCentralDirectory(debug_file_name, archive);
  return result != 0 ? result : ParseZipArchive(archive);
  return result != kSuccess ? result : ParseZipArchive(archive);
}

int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
@@ -489,7 +540,15 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName,
  // Figure out the local header offset from the central directory. The
  // actual file data will begin after the local header and the name /
  // extra comments.
  const off64_t local_header_offset = cdr->local_file_header_offset;
  off64_t local_header_offset = cdr->local_file_header_offset;
  // One of the info field is UINT32_MAX, try to parse the real value in the zip64 extended info in
  // the extra field.
  if (cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX ||
      cdr->local_file_header_offset == UINT32_MAX) {
    ALOGW("Zip: Parsing zip64 local file header isn't supported yet");
    return kInvalidFile;
  }

  if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
    ALOGW("Zip: bad local hdr offset in zip");
    return kInvalidOffset;
+85 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@

#include <inttypes.h>

#include <optional>

// The "end of central directory" (EOCD) record. Each archive
// contains exactly once such record which appears at the end of
// the archive. It contains archive wide information like the
@@ -173,6 +175,89 @@ struct DataDescriptor {
  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
} __attribute__((packed));

// The zip64 end of central directory locator helps to find the zip64 EOCD.
struct Zip64EocdLocator {
  static constexpr uint32_t kSignature = 0x07064b50;

  // The signature of zip64 eocd locator, must be |kSignature|
  uint32_t locator_signature;
  // The start disk of the zip64 eocd. This implementation assumes that each
  // archive spans a single disk only.
  uint32_t eocd_start_disk;
  // The offset offset of the zip64 end of central directory record.
  uint64_t zip64_eocd_offset;
  // The total number of disks. This implementation assumes that each archive
  // spans a single disk only.
  uint32_t num_of_disks;

 private:
  Zip64EocdLocator() = default;
  DISALLOW_COPY_AND_ASSIGN(Zip64EocdLocator);
} __attribute__((packed));

// The optional zip64 EOCD. If one of the fields in the end of central directory
// record is too small to hold required data, the field SHOULD be  set to -1
// (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record SHOULD be created.
struct Zip64EocdRecord {
  static constexpr uint32_t kSignature = 0x06064b50;

  // The signature of zip64 eocd record, must be |kSignature|
  uint32_t record_signature;
  // Size of zip64 end of central directory record. It SHOULD be the size of the
  // remaining record and SHOULD NOT include the leading 12 bytes.
  uint64_t record_size;
  // The version of the tool that make this archive.
  uint16_t version_made_by;
  // Tool version needed to extract this archive.
  uint16_t version_needed;
  // Number of this disk.
  uint32_t disk_num;
  // Number of the disk with the start of the central directory.
  uint32_t cd_start_disk;
  // Total number of entries in the central directory on this disk.
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that num_records_on_disk == num_records.
  uint64_t num_records_on_disk;
  // The total number of central directory records.
  uint64_t num_records;
  // The size of the central directory in bytes.
  uint64_t cd_size;
  // The offset of the start of the central directory, relative to the start of
  // the file.
  uint64_t cd_start_offset;

 private:
  Zip64EocdRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(Zip64EocdRecord);
} __attribute__((packed));

// The possible contents of the Zip64 Extended Information Extra Field. It may appear in
// the 'extra' field of a central directory record or local file header. The order of
// the fields in the zip64 extended information record is fixed, but the fields MUST
// only appear if the corresponding local or central directory record field is set to
// 0xFFFF or 0xFFFFFFFF. And this entry in the Local header MUST include BOTH original
// and compressed file size fields.
struct Zip64ExtendedInfo {
  static constexpr uint16_t kHeaderId = 0x0001;
  // The header tag for this 'extra' block, should be |kHeaderId|.
  uint16_t header_id;
  // The size in bytes of the remaining data (excluding the top 4 bytes).
  uint16_t data_size;
  // Size in bytes of the uncompressed file.
  std::optional<uint64_t> uncompressed_file_size;
  // Size in bytes of the compressed file.
  std::optional<uint64_t> compressed_file_size;
  // Local file header offset relative to the start of the zip file.
  std::optional<uint64_t> local_header_offset;

  // This implementation assumes that each archive spans a single disk only. So
  // the disk_number is not used.
  // uint32_t disk_num;
 private:
  Zip64ExtendedInfo() = default;
  DISALLOW_COPY_AND_ASSIGN(Zip64ExtendedInfo);
};

// mask value that signifies that the entry has a DD
static const uint32_t kGPBDDFlagMask = 0x0008;

+1 −1
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ struct ZipArchive {
  std::unique_ptr<android::base::MappedFile> directory_map;

  // number of entries in the Zip archive
  uint16_t num_entries;
  uint64_t num_entries;
  std::unique_ptr<CdEntryMapInterface> cd_entry_map;

  ZipArchive(MappedZipFile&& map, bool assume_ownership);
+2 −2
Original line number Diff line number Diff line
@@ -133,8 +133,8 @@ static void MaybeShowHeader(ZipArchiveHandle zah) {
    if (!flag_1 && includes.empty() && excludes.empty()) {
      ZipArchiveInfo info{GetArchiveInfo(zah)};
      printf("Archive:  %s\n", archive_name);
      printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
             info.entry_count);
      printf("Zip file size: %" PRId64 " bytes, number of entries: %" PRIu64 "\n",
             info.archive_size, info.entry_count);
    }
  }
}