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

Commit 25a1341f authored by Elliott Hughes's avatar Elliott Hughes Committed by Android (Google) Code Review
Browse files

Merge changes from topic 'fastboot' into mnc-dev

* changes:
  Don't say "update package missing" unless we mean it.
  'usb' doesn't need to be global in fastboot.
  Add "fastboot --version".
  libziparchive: fix fallocate failures
  Use base::WriteFully in zip_archive.
  libziparchive: fix clang build
  Avoid mapping output to memory while writing to a file.
parents 6f1457ad 966339b2
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@

LOCAL_PATH:= $(call my-dir)

fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
@@ -25,14 +27,15 @@ LOCAL_MODULE_TAGS := debug
LOCAL_CONLYFLAGS += -std=gnu99
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code

LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'

ifeq ($(HOST_OS),linux)
  LOCAL_SRC_FILES += usb_linux.c util_linux.c
endif

ifeq ($(HOST_OS),darwin)
  LOCAL_SRC_FILES += usb_osx.c util_osx.c
  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \
	-framework Carbon
  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
  LOCAL_CFLAGS += -Wno-unused-parameter
endif

@@ -58,7 +61,8 @@ LOCAL_STATIC_LIBRARIES := \
    libsparse_host \
    libutils \
    liblog \
    libz
    libz \
    libbase

ifneq ($(HOST_OS),windows)
LOCAL_STATIC_LIBRARIES += libselinux
+22 −17
Original line number Diff line number Diff line
@@ -59,7 +59,6 @@

char cur_product[FB_RESPONSE_SZ + 1];

static usb_handle *usb = 0;
static const char *serial = 0;
static const char *product = 0;
static const char *cmdline = 0;
@@ -609,7 +608,7 @@ static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
 * erase partitions of type ext4 before flashing a filesystem so no stale
 * inodes are left lying around.  Otherwise, e2fsck gets very upset.
 */
static int needs_erase(const char *part)
static int needs_erase(usb_handle* usb, const char *part)
{
    /* The function fb_format_supported() currently returns the value
     * we want, so just call it.
@@ -725,19 +724,20 @@ void do_update(usb_handle *usb, const char *filename, int erase_first)

    setup_requirements(reinterpret_cast<char*>(data), sz);

    for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
    for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
        int fd = unzip_to_file(zip, images[i].img_name);
        if (fd < 0) {
            if (images[i].is_optional)
        if (fd == -1) {
            if (images[i].is_optional) {
                continue;
            }
            CloseArchive(zip);
            die("update package missing %s", images[i].img_name);
            exit(1); // unzip_to_file already explained why.
        }
        fastboot_buffer buf;
        int rc = load_buf_fd(usb, fd, &buf);
        if (rc) die("cannot load %s from flash", images[i].img_name);
        do_update_signature(zip, images[i].sig_name);
        if (erase_first && needs_erase(images[i].part_name)) {
        if (erase_first && needs_erase(usb, images[i].part_name)) {
            fb_queue_erase(images[i].part_name);
        }
        flash_buf(images[i].part_name, &buf);
@@ -792,7 +792,7 @@ void do_flashall(usb_handle *usb, int erase_first)
            die("could not load %s\n", images[i].img_name);
        }
        do_send_signature(fname);
        if (erase_first && needs_erase(images[i].part_name)) {
        if (erase_first && needs_erase(usb, images[i].part_name)) {
            fb_queue_erase(images[i].part_name);
        }
        flash_buf(images[i].part_name, &buf);
@@ -860,7 +860,8 @@ static int64_t parse_num(const char *arg)
    return num;
}

void fb_perform_format(const char *partition, int skip_if_not_supported,
void fb_perform_format(usb_handle* usb,
                       const char *partition, int skip_if_not_supported,
                       const char *type_override, const char *size_override)
{
    char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
@@ -964,8 +965,9 @@ int main(int argc, char **argv)
        {"page_size", required_argument, 0, 'n'},
        {"ramdisk_offset", required_argument, 0, 'r'},
        {"tags_offset", required_argument, 0, 't'},
        {"help", 0, 0, 'h'},
        {"unbuffered", 0, 0, 0},
        {"help", no_argument, 0, 'h'},
        {"unbuffered", no_argument, 0, 0},
        {"version", no_argument, 0, 0},
        {0, 0, 0, 0}
    };

@@ -1037,6 +1039,9 @@ int main(int argc, char **argv)
            if (strcmp("unbuffered", longopts[longindex].name) == 0) {
                setvbuf(stdout, NULL, _IONBF, 0);
                setvbuf(stderr, NULL, _IONBF, 0);
            } else if (strcmp("version", longopts[longindex].name) == 0) {
                fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
                return 0;
            }
            break;
        default:
@@ -1063,7 +1068,7 @@ int main(int argc, char **argv)
        return 0;
    }

    usb = open_device();
    usb_handle* usb = open_device();

    while (argc > 0) {
        if(!strcmp(*argv, "getvar")) {
@@ -1105,10 +1110,10 @@ int main(int argc, char **argv)
            }
            if (type_override && !type_override[0]) type_override = NULL;
            if (size_override && !size_override[0]) size_override = NULL;
            if (erase_first && needs_erase(argv[1])) {
            if (erase_first && needs_erase(usb, argv[1])) {
                fb_queue_erase(argv[1]);
            }
            fb_perform_format(argv[1], 0, type_override, size_override);
            fb_perform_format(usb, argv[1], 0, type_override, size_override);
            skip(2);
        } else if(!strcmp(*argv, "signature")) {
            require(2);
@@ -1163,7 +1168,7 @@ int main(int argc, char **argv)
                skip(2);
            }
            if (fname == 0) die("cannot determine image filename for '%s'", pname);
            if (erase_first && needs_erase(pname)) {
            if (erase_first && needs_erase(usb, pname)) {
                fb_queue_erase(pname);
            }
            do_flash(usb, pname, fname);
@@ -1214,9 +1219,9 @@ int main(int argc, char **argv)

    if (wants_wipe) {
        fb_queue_erase("userdata");
        fb_perform_format("userdata", 1, NULL, NULL);
        fb_perform_format(usb, "userdata", 1, NULL, NULL);
        fb_queue_erase("cache");
        fb_perform_format("cache", 1, NULL, NULL);
        fb_perform_format(usb, "cache", 1, NULL, NULL);
    }
    if (wants_reboot) {
        fb_queue_reboot();
+176 −93
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <memory>
#include <vector>

#include "base/file.h"
#include "base/macros.h"  // TEMP_FAILURE_RETRY may or may not be in unistd
#include "base/memory.h"
#include "log/log.h"
@@ -85,7 +86,8 @@ struct EocdRecord {
  // Length of the central directory comment.
  uint16_t comment_length;
 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord);
  EocdRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
} __attribute__((packed));

// A structure representing the fixed length fields for a single
@@ -138,7 +140,8 @@ struct CentralDirectoryRecord {
  // beginning of this archive.
  uint32_t local_file_header_offset;
 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord);
  CentralDirectoryRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
} __attribute__((packed));

// The local file header for a given entry. This duplicates information
@@ -175,7 +178,8 @@ struct LocalFileHeader {
  // will appear immediately after the entry file name.
  uint16_t extra_field_length;
 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader);
  LocalFileHeader() = default;
  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
} __attribute__((packed));

struct DataDescriptor {
@@ -189,10 +193,10 @@ struct DataDescriptor {
  // Uncompressed size of the entry.
  uint32_t uncompressed_size;
 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor);
  DataDescriptor() = default;
  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
} __attribute__((packed));

#undef DISALLOW_IMPLICIT_CONSTRUCTORS

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

@@ -265,8 +269,6 @@ static const int32_t kMmapFailed = -12;

static const int32_t kErrorMessageLowerBound = -13;

static const char kTempMappingFileName[] = "zip: ExtractFileToFile";

/*
 * A Read-only Zip archive.
 *
@@ -324,35 +326,6 @@ struct ZipArchive {
  }
};

static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) {
  static const uint32_t kBufSize = 32768;
  uint8_t buf[kBufSize];

  uint32_t count = 0;
  uint64_t crc = 0;
  while (count < length) {
    uint32_t remaining = length - count;

    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
    // value.
    ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining;
    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));

    if (actual != get_size) {
      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size);
      return kIoError;
    }

    memcpy(begin + count, buf, get_size);
    crc = crc32(crc, buf, get_size);
    count += get_size;
  }

  *crc_out = crc;

  return 0;
}

/*
 * Round up to the next highest power of 2.
 *
@@ -972,6 +945,122 @@ int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
  return kIterationEnd;
}

class Writer {
 public:
  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
  virtual ~Writer() {}
 protected:
  Writer() = default;
 private:
  DISALLOW_COPY_AND_ASSIGN(Writer);
};

// A Writer that writes data to a fixed size memory region.
// The size of the memory region must be equal to the total size of
// the data appended to it.
class MemoryWriter : public Writer {
 public:
  MemoryWriter(uint8_t* buf, size_t size) : Writer(),
      buf_(buf), size_(size), bytes_written_(0) {
  }

  virtual bool Append(uint8_t* buf, size_t buf_size) override {
    if (bytes_written_ + buf_size > size_) {
      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
            size_, bytes_written_ + buf_size);
      return false;
    }

    memcpy(buf_ + bytes_written_, buf, buf_size);
    bytes_written_ += buf_size;
    return true;
  }

 private:
  uint8_t* const buf_;
  const size_t size_;
  size_t bytes_written_;
};

// A Writer that appends data to a file |fd| at its current position.
// The file will be truncated to the end of the written data.
class FileWriter : public Writer {
 public:

  // Creates a FileWriter for |fd| and prepare to write |entry| to it,
  // guaranteeing that the file descriptor is valid and that there's enough
  // space on the volume to write out the entry completely and that the file
  // is truncated to the correct length.
  //
  // Returns a valid FileWriter on success, |nullptr| if an error occurred.
  static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
    const uint32_t declared_length = entry->uncompressed_length;
    const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
    if (current_offset == -1) {
      ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
      return nullptr;
    }

    int result = 0;
#if defined(__linux__)
    if (declared_length > 0) {
      // Make sure we have enough space on the volume to extract the compressed
      // entry. Note that the call to ftruncate below will change the file size but
      // will not allocate space on disk and this call to fallocate will not
      // change the file size.
      // Note: fallocate is only supported by the following filesystems -
      // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
      // EOPNOTSUPP error when issued in other filesystems.
      // Hence, check for the return error code before concluding that the
      // disk does not have enough space.
      result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
      if (result == -1 && errno == ENOSPC) {
        ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
              static_cast<int64_t>(declared_length + current_offset), strerror(errno));
        return std::unique_ptr<FileWriter>(nullptr);
      }
    }
#endif  // __linux__

    result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
    if (result == -1) {
      ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
      return std::unique_ptr<FileWriter>(nullptr);
    }

    return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
  }

  virtual bool Append(uint8_t* buf, size_t buf_size) override {
    if (total_bytes_written_ + buf_size > declared_length_) {
      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
            declared_length_, total_bytes_written_ + buf_size);
      return false;
    }

    const bool result = android::base::WriteFully(fd_, buf, buf_size);
    if (result) {
      total_bytes_written_ += buf_size;
    } else {
      ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
    }

    return result;
  }
 private:
  FileWriter(const int fd, const size_t declared_length) :
      Writer(),
      fd_(fd),
      declared_length_(declared_length),
      total_bytes_written_(0) {
  }

  const int fd_;
  const size_t declared_length_;
  size_t total_bytes_written_;
};

// This method is using libz macros with old-style-casts
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -980,9 +1069,8 @@ static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
}
#pragma GCC diagnostic pop

static int32_t InflateToFile(int fd, const ZipEntry* entry,
                             uint8_t* begin, uint32_t length,
                             uint64_t* crc_out) {
static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
                                    Writer* writer, uint64_t* crc_out) {
  const size_t kBufSize = 32768;
  std::vector<uint8_t> read_buf(kBufSize);
  std::vector<uint8_t> write_buf(kBufSize);
@@ -1027,7 +1115,6 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry,
  const uint32_t uncompressed_length = entry->uncompressed_length;

  uint32_t compressed_length = entry->compressed_length;
  uint32_t write_count = 0;
  do {
    /* read as much as we can */
    if (zstream.avail_in == 0) {
@@ -1057,12 +1144,10 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry,
    if (zstream.avail_out == 0 ||
      (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
      const size_t write_size = zstream.next_out - &write_buf[0];
      if (!writer->Append(&write_buf[0], write_size)) {
        // The file might have declared a bogus length.
      if (write_size + write_count > length) {
        return -1;
        return kInconsistentInformation;
      }
      memcpy(begin + write_count, &write_buf[0], write_size);
      write_count += write_size;

      zstream.next_out = &write_buf[0];
      zstream.avail_out = kBufSize;
@@ -1083,8 +1168,41 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry,
  return 0;
}

int32_t ExtractToMemory(ZipArchiveHandle handle,
                        ZipEntry* entry, uint8_t* begin, uint32_t size) {
static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
                                 uint64_t *crc_out) {
  static const uint32_t kBufSize = 32768;
  std::vector<uint8_t> buf(kBufSize);

  const uint32_t length = entry->uncompressed_length;
  uint32_t count = 0;
  uint64_t crc = 0;
  while (count < length) {
    uint32_t remaining = length - count;

    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
    // value.
    const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
    const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));

    if (actual != block_size) {
      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
      return kIoError;
    }

    if (!writer->Append(&buf[0], block_size)) {
      return kIoError;
    }
    crc = crc32(crc, &buf[0], block_size);
    count += block_size;
  }

  *crc_out = crc;

  return 0;
}

int32_t ExtractToWriter(ZipArchiveHandle handle,
                        ZipEntry* entry, Writer* writer) {
  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
  const uint16_t method = entry->method;
  off64_t data_offset = entry->offset;
@@ -1098,9 +1216,9 @@ int32_t ExtractToMemory(ZipArchiveHandle handle,
  int32_t return_value = -1;
  uint64_t crc = 0;
  if (method == kCompressStored) {
    return_value = CopyFileToFile(archive->fd, begin, size, &crc);
    return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
  } else if (method == kCompressDeflated) {
    return_value = InflateToFile(archive->fd, entry, begin, size, &crc);
    return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
  }

  if (!return_value && entry->has_data_descriptor) {
@@ -1120,55 +1238,20 @@ int32_t ExtractToMemory(ZipArchiveHandle handle,
  return return_value;
}

int32_t ExtractEntryToFile(ZipArchiveHandle handle,
                           ZipEntry* entry, int fd) {
  const uint32_t declared_length = entry->uncompressed_length;

  const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
  if (current_offset == -1) {
    ALOGW("Zip: unable to seek to current location on fd %d: %s", fd,
          strerror(errno));
    return kIoError;
  }

  int result = 0;
#if defined(__linux__)
  // Make sure we have enough space on the volume to extract the compressed
  // entry. Note that the call to ftruncate below will change the file size but
  // will not allocate space on disk.
  if (declared_length > 0) {
    result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
    if (result == -1) {
      ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
      return kIoError;
int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
                        uint8_t* begin, uint32_t size) {
  std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
  return ExtractToWriter(handle, entry, writer.get());
}
  }
#endif  // defined(__linux__)

  result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
  if (result == -1) {
    ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
          static_cast<int64_t>(declared_length + current_offset), strerror(errno));
int32_t ExtractEntryToFile(ZipArchiveHandle handle,
                           ZipEntry* entry, int fd) {
  std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
  if (writer.get() == nullptr) {
    return kIoError;
  }

  // Don't attempt to map a region of length 0. We still need the
  // ftruncate() though, since the API guarantees that we will truncate
  // the file to the end of the uncompressed output.
  if (declared_length == 0) {
      return 0;
  }

  android::FileMap map;
  if (!map.create(kTempMappingFileName, fd, current_offset, declared_length, false)) {
    return kMmapFailed;
  }

  const int32_t error = ExtractToMemory(handle, entry,
                                        reinterpret_cast<uint8_t*>(map.getDataPtr()),
                                        map.getDataLength());
  return error;
  return ExtractToWriter(handle, entry, writer.get());
}

const char* ErrorCodeString(int32_t error_code) {
+88 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <vector>

#include <base/file.h>
#include <gtest/gtest.h>

static std::string test_data_dir;
@@ -228,6 +229,44 @@ static const uint32_t kEmptyEntriesZip[] = {
      0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
      0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };

// This is a zip file containing a single entry (ab.txt) that contains
// 90072 repetitions of the string "ab\n" and has an uncompressed length
// of 270216 bytes.
static const uint16_t kAbZip[] = {
  0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
  0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
  0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
  0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
  0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
  0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
  0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
  0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
  0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
  0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
  0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
  0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
  0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
};

static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
static const size_t kAbUncompressedSize = 270216;

static int make_temporary_file(const char* file_name_pattern) {
  char full_path[1024];
  // Account for differences between the host and the target.
@@ -275,6 +314,55 @@ TEST(ziparchive, EmptyEntries) {
  close(output_fd);
}

TEST(ziparchive, EntryLargerThan32K) {
  char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
  int fd = make_temporary_file(temp_file_pattern);
  ASSERT_NE(-1, fd);
  ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
                         sizeof(kAbZip) - 1));
  ZipArchiveHandle handle;
  ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));

  ZipEntry entry;
  ZipEntryName ab_name;
  ab_name.name = kAbTxtName;
  ab_name.name_length = kAbTxtNameLength;
  ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
  ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);

  // Extract the entry to memory.
  std::vector<uint8_t> buffer(kAbUncompressedSize);
  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));

  // Extract the entry to a file.
  char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
  int output_fd = make_temporary_file(output_file_pattern);
  ASSERT_NE(-1, output_fd);
  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));

  // Make sure the extracted file size is as expected.
  struct stat stat_buf;
  ASSERT_EQ(0, fstat(output_fd, &stat_buf));
  ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));

  // Read the file back to a buffer and make sure the contents are
  // the same as the memory buffer we extracted directly to.
  std::vector<uint8_t> file_contents(kAbUncompressedSize);
  ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
  ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
  ASSERT_EQ(file_contents, buffer);

  for (int i = 0; i < 90072; ++i) {
    const uint8_t* line = &file_contents[0] + (3 * i);
    ASSERT_EQ('a', line[0]);
    ASSERT_EQ('b', line[1]);
    ASSERT_EQ('\n', line[2]);
  }

  close(fd);
  close(output_fd);
}

TEST(ziparchive, TrailerAfterEOCD) {
  char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
  int fd = make_temporary_file(temp_file_pattern);