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

Commit 5a97641d authored by Tianjie Xu's avatar Tianjie Xu Committed by Automerger Merge Worker
Browse files

Merge "Add definition for zip64 struct" am: 18c00858

Change-Id: I0a01a7fa85d5c8f689380eaf284fe715e4264913
parents d1ad6bed 18c00858
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);
    }
  }
}