Merge "Show transfer progress in adb sync/pull/push."
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index a0e990a..63e8971 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,41 +75,35 @@
 
 
 
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
-{
-    char cmd[CMD_SIZE] = "getvar:";
-    int getvar_len = strlen(cmd);
-    va_list args;
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value) {
+    std::string cmd = "getvar:";
+    cmd += key;
 
-    response[FB_RESPONSE_SZ] = '\0';
-    va_start(args, fmt);
-    vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
-    va_end(args);
-    cmd[CMD_SIZE - 1] = '\0';
-    return fb_command_response(usb, cmd, response);
+    char buf[FB_RESPONSE_SZ + 1];
+    memset(buf, 0, sizeof(buf));
+    if (fb_command_response(usb, cmd.c_str(), buf)) {
+      return false;
+    }
+    *value = buf;
+    return true;
 }
 
 
-/* Return true if this partition is supported by the fastboot format command.
- * It is also used to determine if we should first erase a partition before
- * flashing it with an ext4 filesystem.  See needs_erase()
- *
- * Not all devices report the filesystem type, so don't report any errors,
- * just return false.
- */
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
-{
-    char fs_type[FB_RESPONSE_SZ + 1] = {0,};
-    int status;
-
+// Return true if this partition is supported by the fastboot format command.
+// It is also used to determine if we should first erase a partition before
+// flashing it with an ext4 filesystem.  See needs_erase()
+//
+// Not all devices report the filesystem type, so don't report any errors,
+// just return false.
+bool fb_format_supported(usb_handle *usb, const char *partition, const char *type_override) {
     if (type_override) {
-        return !!fs_get_generator(type_override);
+        return fs_get_generator(type_override) != nullptr;
     }
-    status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
-    if (status) {
-        return 0;
+    std::string partition_type;
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+        return false;
     }
-    return !!fs_get_generator(fs_type);
+    return fs_get_generator(partition_type.c_str()) != nullptr;
 }
 
 static int cb_default(Action* a, int status, const char* resp) {
@@ -394,8 +388,3 @@
     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
     return status;
 }
-
-int fb_queue_is_empty(void)
-{
-    return (action_list == nullptr);
-}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e2971f2..7bf4b31 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <base/parseint.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -567,25 +568,23 @@
     return out_s;
 }
 
-static int64_t get_target_sparse_limit(struct usb_handle *usb)
-{
-    int64_t limit = 0;
-    char response[FB_RESPONSE_SZ + 1];
-    int status = fb_getvar(usb, response, "max-download-size");
-
-    if (!status) {
-        limit = strtoul(response, nullptr, 0);
-        if (limit > 0) {
-            fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
-                    limit);
-        }
+static int64_t get_target_sparse_limit(usb_handle* usb) {
+    std::string max_download_size;
+    if (!fb_getvar(usb, "max-download-size", &max_download_size)) {
+        return 0;
     }
 
+    uint64_t limit;
+    if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
+        return 0;
+    }
+    if (limit > 0) {
+        fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+    }
     return limit;
 }
 
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
     int64_t limit;
 
     if (sparse_limit == 0) {
@@ -610,16 +609,11 @@
     return 0;
 }
 
-/* Until we get lazy inode table init working in make_ext4fs, we need to
- * 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(usb_handle* usb, const char *part)
-{
-    /* The function fb_format_supported() currently returns the value
-     * we want, so just call it.
-     */
-     return fb_format_supported(usb, part, nullptr);
+// Until we get lazy inode table init working in make_ext4fs, we need to
+// erase partitions of type ext4 before flashing a filesystem so no stale
+// inodes are left lying around.  Otherwise, e2fsck gets very upset.
+static bool needs_erase(usb_handle* usb, const char* part) {
+    return !fb_format_supported(usb, part, nullptr);
 }
 
 static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
@@ -852,87 +846,84 @@
 }
 
 static 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];
-    char *pType = pTypeBuff;
-    char *pSize = pSizeBuff;
-    unsigned int limit = INT_MAX;
+                              const char* partition, int skip_if_not_supported,
+                              const char* type_override, const char* size_override) {
+    std::string partition_type, partition_size;
+
     struct fastboot_buffer buf;
-    const char *errMsg = nullptr;
-    const struct fs_generator *gen;
-    uint64_t pSz;
-    int status;
+    const char* errMsg = nullptr;
+    const struct fs_generator* gen = nullptr;
     int fd;
 
-    if (target_sparse_limit > 0 && target_sparse_limit < limit)
+    unsigned int limit = INT_MAX;
+    if (target_sparse_limit > 0 && target_sparse_limit < limit) {
         limit = target_sparse_limit;
-    if (sparse_limit > 0 && sparse_limit < limit)
+    }
+    if (sparse_limit > 0 && sparse_limit < limit) {
         limit = sparse_limit;
+    }
 
-    status = fb_getvar(usb, pType, "partition-type:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
     if (type_override) {
-        if (strcmp(type_override, pType)) {
-            fprintf(stderr,
-                    "Warning: %s type is %s, but %s was requested for formating.\n",
-                    partition, pType, type_override);
+        if (partition_type != type_override) {
+            fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+                    partition, partition_type.c_str(), type_override);
         }
-        pType = (char *)type_override;
+        partition_type = type_override;
     }
 
-    status = fb_getvar(usb, pSize, "partition-size:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
     if (size_override) {
-        if (strcmp(size_override, pSize)) {
-            fprintf(stderr,
-                    "Warning: %s size is %s, but %s was requested for formating.\n",
-                    partition, pSize, size_override);
+        if (partition_size != size_override) {
+            fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+                    partition, partition_size.c_str(), size_override);
         }
-        pSize = (char *)size_override;
+        partition_size = size_override;
     }
 
-    gen = fs_get_generator(pType);
+    gen = fs_get_generator(partition_type.c_str());
     if (!gen) {
         if (skip_if_not_supported) {
             fprintf(stderr, "Erase successful, but not automatically formatting.\n");
-            fprintf(stderr, "File system type %s not supported.\n", pType);
+            fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+        fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
+                partition_type.c_str());
         return;
     }
 
-    pSz = strtoll(pSize, (char **)nullptr, 16);
+    int64_t size;
+    if (!android::base::ParseInt(partition_size.c_str(), &size)) {
+        fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
+        return;
+    }
 
     fd = fileno(tmpfile());
-    if (fs_generator_generate(gen, fd, pSz)) {
+    if (fs_generator_generate(gen, fd, size)) {
+        fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
         close(fd);
-        fprintf(stderr, "Cannot generate image.\n");
         return;
     }
 
     if (load_buf_fd(usb, fd, &buf)) {
-        fprintf(stderr, "Cannot read image.\n");
+        fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
         close(fd);
         return;
     }
     flash_buf(partition, &buf);
-
     return;
 
-
 failed:
     if (skip_if_not_supported) {
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
-        if (errMsg)
-            fprintf(stderr, "%s", errMsg);
+        if (errMsg) fprintf(stderr, "%s", errMsg);
     }
     fprintf(stderr,"FAILED (%s)\n", fb_get_error());
 }
@@ -945,8 +936,6 @@
     bool erase_first = true;
     void *data;
     int64_t sz;
-    int status;
-    int c;
     int longindex;
 
     const struct option longopts[] = {
@@ -964,7 +953,7 @@
     serial = getenv("ANDROID_SERIAL");
 
     while (1) {
-        c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
         if (c < 0) {
             break;
         }
@@ -1061,14 +1050,14 @@
     usb_handle* usb = open_device();
 
     while (argc > 0) {
-        if(!strcmp(*argv, "getvar")) {
+        if (!strcmp(*argv, "getvar")) {
             require(2);
             fb_queue_display(argv[1], argv[1]);
             skip(2);
         } else if(!strcmp(*argv, "erase")) {
             require(2);
 
-            if (fb_format_supported(usb, argv[1], nullptr)) {
+            if (!fb_format_supported(usb, argv[1], nullptr)) {
                 fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
             }
 
@@ -1217,10 +1206,16 @@
     }
 
     if (wants_wipe) {
+        fprintf(stderr, "wiping userdata...\n");
         fb_queue_erase("userdata");
         fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
-        fb_queue_erase("cache");
-        fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+
+        std::string cache_type;
+        if (fb_getvar(usb, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+            fprintf(stderr, "wiping cache...\n");
+            fb_queue_erase("cache");
+            fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+        }
     }
     if (wants_reboot) {
         fb_queue_reboot();
@@ -1230,9 +1225,5 @@
         fb_queue_wait_for_disconnect();
     }
 
-    if (fb_queue_is_empty())
-        return 0;
-
-    status = fb_execute_queue(usb);
-    return (status) ? 1 : 0;
+    return fb_execute_queue(usb) ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 091a70f..a66c211 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -32,6 +32,8 @@
 #include <inttypes.h>
 #include <stdlib.h>
 
+#include <string>
+
 #include "usb.h"
 
 struct sparse_file;
@@ -47,8 +49,8 @@
 #define FB_RESPONSE_SZ 64
 
 /* engine.c - high level command queue engine */
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value);
+bool fb_format_supported(usb_handle* usb, const char* partition, const char* type_override);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
 void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
 void fb_queue_erase(const char *ptn);
@@ -63,7 +65,6 @@
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
 int fb_execute_queue(usb_handle *usb);
-int fb_queue_is_empty(void);
 
 /* util stuff */
 double now();
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index c358982..6983b72 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -765,8 +765,9 @@
             continue;
         }
 
-        if (current == VERITY_MODE_LOGGING) {
+        if (current != VERITY_MODE_DEFAULT) {
             *mode = current;
+            break;
         }
     }
 
@@ -784,7 +785,6 @@
 {
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     bool use_state = true;
-    bool use_state_for_device = true;
     char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
     char *mount_point;
     char propbuf[PROPERTY_VALUE_MAX];
@@ -801,12 +801,13 @@
     property_get("ro.boot.veritymode", propbuf, "");
 
     if (*propbuf != '\0') {
-        if (fs_mgr_load_verity_state(&mode) == -1) {
-            return -1;
-        }
         use_state = false; /* state is kept by the bootloader */
     }
 
+    if (fs_mgr_load_verity_state(&mode) == -1) {
+        return -1;
+    }
+
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
 
     if (fd == -1) {
@@ -829,15 +830,6 @@
             continue;
         }
 
-        use_state_for_device = use_state;
-
-        if (use_state) {
-            if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
-                read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
-                use_state_for_device = false;
-            }
-        }
-
         mount_point = basename(fstab->recs[i].mount_point);
         verity_ioctl_init(io, mount_point, 0);
 
@@ -849,7 +841,7 @@
 
         status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
 
-        if (use_state_for_device && *status == 'C') {
+        if (use_state && *status == 'C') {
             if (write_verity_state(fstab->recs[i].verity_loc, offset,
                     VERITY_MODE_LOGGING) < 0) {
                 continue;
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 9ee4abe..d996c4a 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -21,9 +21,11 @@
 #include <utils/Compat.h>
 
 #include <cstdio>
-#include <string>
 #include <ctime>
+#include <memory>
+#include <string>
 #include <vector>
+#include <zlib.h>
 
 /**
  * Writes a Zip file via a stateful interface.
@@ -112,8 +114,6 @@
 private:
   DISALLOW_COPY_AND_ASSIGN(ZipWriter);
 
-  int32_t HandleError(int32_t error_code);
-
   struct FileInfo {
     std::string path;
     uint16_t compression_method;
@@ -125,6 +125,12 @@
     uint32_t local_file_header_offset;
   };
 
+  int32_t HandleError(int32_t error_code);
+  int32_t PrepareDeflate();
+  int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
+  int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
+  int32_t FlushCompressedBytes(FileInfo* file);
+
   enum class State {
     kWritingZip,
     kWritingEntry,
@@ -136,6 +142,9 @@
   off64_t current_offset_;
   State state_;
   std::vector<FileInfo> files_;
+
+  std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+  std::vector<uint8_t> buffer_;
 };
 
 #endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 8ff94d4..8a4921f 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -18,9 +18,12 @@
 source_files := zip_archive.cc zip_writer.cc
 test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
 
+# ZLIB_CONST turns on const for input buffers, which is pretty standard.
+common_c_flags := -Werror -Wall -DZLIB_CONST
+
 # Incorrectly warns when C++11 empty brace {} initializer is used.
 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-common_cpp_flags := -Wno-missing-field-initializers
+common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
@@ -28,8 +31,8 @@
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_SHARED_LIBRARIES := libutils libbase
 LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast $(common_cpp_flags)
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -37,7 +40,7 @@
 LOCAL_SRC_FILES := ${source_files}
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CFLAGS_windows := -mno-ms-bitfields
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 
@@ -51,7 +54,7 @@
 LOCAL_STATIC_LIBRARIES := libutils
 LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
@@ -60,7 +63,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := liblog libbase
@@ -70,7 +73,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests-host
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
 LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f1e13a7..3d18f7c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -588,7 +588,7 @@
   // and other interesting attributes from the central directory. These
   // will later be compared against values from the local file header.
   data->method = cdr->compression_method;
-  data->mod_time = cdr->last_mod_time;
+  data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
   data->crc32 = cdr->crc32;
   data->compressed_length = cdr->compressed_size;
   data->uncompressed_length = cdr->uncompressed_size;
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9a3cdb4..32b1a38 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -272,6 +272,7 @@
   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
   ASSERT_EQ(0x950821c5, data.crc32);
+  ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
   ZipString absent_name;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index de75d1e..22a7c53 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,10 +18,13 @@
 #include "zip_archive_common.h"
 #include "ziparchive/zip_writer.h"
 
+#include <utils/Log.h>
+
 #include <cassert>
 #include <cstdio>
 #include <memory>
 #include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
 /* Zip compression methods we support */
 enum {
@@ -29,6 +32,9 @@
   kCompressDeflated   = 8,        // standard deflate
 };
 
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
 // No error, operation completed successfully.
 static const int32_t kNoError = 0;
 
@@ -41,10 +47,14 @@
 // The zip entry name was invalid.
 static const int32_t kInvalidEntryName = -3;
 
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
 static const char* sErrorCodes[] = {
     "Invalid state",
     "IO error",
     "Invalid entry name",
+    "Zlib error",
 };
 
 const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -54,13 +64,21 @@
   return nullptr;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip) {
+static void DeleteZStream(z_stream* stream) {
+  deflateEnd(stream);
+  delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
+                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
 }
 
 ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
                                            current_offset_(writer.current_offset_),
                                            state_(writer.state_),
-                                           files_(std::move(writer.files_)) {
+                                           files_(std::move(writer.files_)),
+                                           z_stream_(std::move(writer.z_stream_)),
+                                           buffer_(std::move(writer.buffer_)){
   writer.file_ = nullptr;
   writer.state_ = State::kError;
 }
@@ -70,6 +88,8 @@
   current_offset_ = writer.current_offset_;
   state_ = writer.state_;
   files_ = std::move(writer.files_);
+  z_stream_ = std::move(writer.z_stream_);
+  buffer_ = std::move(writer.buffer_);
   writer.file_ = nullptr;
   writer.state_ = State::kError;
   return *this;
@@ -77,6 +97,7 @@
 
 int32_t ZipWriter::HandleError(int32_t error_code) {
   state_ = State::kError;
+  z_stream_.reset();
   return error_code;
 }
 
@@ -126,8 +147,16 @@
   // containing the crc and size fields.
   header.gpb_flags |= kGPBDDFlagMask;
 
-  // For now, ignore the ZipWriter::kCompress flag.
-  fileInfo.compression_method = kCompressStored;
+  if (flags & ZipWriter::kCompress) {
+    fileInfo.compression_method = kCompressDeflated;
+
+    int32_t result = PrepareDeflate();
+    if (result != kNoError) {
+      return result;
+    }
+  } else {
+    fileInfo.compression_method = kCompressStored;
+  }
   header.compression_method = fileInfo.compression_method;
 
   ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
@@ -163,40 +192,138 @@
   return kNoError;
 }
 
+int32_t ZipWriter::PrepareDeflate() {
+  assert(state_ == State::kWritingZip);
+
+  // Initialize the z_stream for compression.
+  z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+  int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+  if (zerr != Z_OK) {
+    if (zerr == Z_VERSION_ERROR) {
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      return HandleError(kZlibError);
+    } else {
+      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      return HandleError(kZlibError);
+    }
+  }
+
+  z_stream_->next_out = buffer_.data();
+  z_stream_->avail_out = buffer_.size();
+  return kNoError;
+}
+
 int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
   if (state_ != State::kWritingEntry) {
     return HandleError(kInvalidState);
   }
 
   FileInfo& currentFile = files_.back();
+  int32_t result = kNoError;
   if (currentFile.compression_method & kCompressDeflated) {
-    // TODO(adamlesinski): Implement compression using zlib deflate.
-    assert(false);
+    result = CompressBytes(&currentFile, data, len);
   } else {
-    if (fwrite(data, 1, len, file_) != len) {
-      return HandleError(kIoError);
-    }
-    currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
-    currentFile.compressed_size += len;
-    current_offset_ += len;
+    result = StoreBytes(&currentFile, data, len);
   }
 
+  if (result != kNoError) {
+    return result;
+  }
+
+  currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
   currentFile.uncompressed_size += len;
   return kNoError;
 }
 
+int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+
+  if (fwrite(data, 1, len, file_) != len) {
+    return HandleError(kIoError);
+  }
+  file->compressed_size += len;
+  current_offset_ += len;
+  return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  // Prepare the input.
+  z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+  z_stream_->avail_in = len;
+
+  while (z_stream_->avail_in > 0) {
+    // We have more data to compress.
+    int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+    if (zerr != Z_OK) {
+      return HandleError(kZlibError);
+    }
+
+    if (z_stream_->avail_out == 0) {
+      // The output is full, let's write it to disk.
+      size_t dataToWrite = z_stream_->next_out - buffer_.data();
+      if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+        return HandleError(kIoError);
+      }
+      file->compressed_size += dataToWrite;
+      current_offset_ += dataToWrite;
+
+      // Reset the output buffer for the next input.
+      z_stream_->next_out = buffer_.data();
+      z_stream_->avail_out = buffer_.size();
+    }
+  }
+  return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  int zerr = deflate(z_stream_.get(), Z_FINISH);
+  if (zerr != Z_STREAM_END) {
+    return HandleError(kZlibError);
+  }
+
+  size_t dataToWrite = z_stream_->next_out - buffer_.data();
+  if (dataToWrite != 0) {
+    if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += dataToWrite;
+    current_offset_ += dataToWrite;
+  }
+  z_stream_.reset();
+  return kNoError;
+}
+
 int32_t ZipWriter::FinishEntry() {
   if (state_ != State::kWritingEntry) {
     return kInvalidState;
   }
 
+  FileInfo& currentFile = files_.back();
+  if (currentFile.compression_method & kCompressDeflated) {
+    int32_t result = FlushCompressedBytes(&currentFile);
+    if (result != kNoError) {
+      return result;
+    }
+  }
+
   const uint32_t sig = DataDescriptor::kOptSignature;
   if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
     state_ = State::kError;
     return kIoError;
   }
 
-  FileInfo& currentFile = files_.back();
   DataDescriptor dd = {};
   dd.crc32 = currentFile.crc32;
   dd.compressed_size = currentFile.compressed_size;
@@ -241,8 +368,8 @@
 
   EocdRecord er = {};
   er.eocd_signature = EocdRecord::kSignature;
-  er.disk_num = 1;
-  er.cd_start_disk = 1;
+  er.disk_num = 0;
+  er.cd_start_disk = 0;
   er.num_records_on_disk = files_.size();
   er.num_records = files_.size();
   er.cd_size = current_offset_ - startOfCdr;
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5269730..046f195 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -44,26 +44,26 @@
 
   const char* expected = "hello";
 
-  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
-  EXPECT_EQ(data.compressed_length, strlen(expected));
-  EXPECT_EQ(data.uncompressed_length, strlen(expected));
-  EXPECT_EQ(data.method, kCompressStored);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(strlen(expected), data.compressed_length);
+  EXPECT_EQ(strlen(expected), data.uncompressed_length);
+  EXPECT_EQ(kCompressStored, data.method);
 
   char buffer[6];
-  EXPECT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)),
-            0);
+  EXPECT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
   buffer[5] = 0;
 
   EXPECT_STREQ(expected, buffer);
@@ -74,49 +74,49 @@
 TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
   ZipWriter writer(file_);
 
-  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.StartEntry("file/file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.StartEntry("file/file2.txt", 0), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   char buffer[4];
   ZipEntry data;
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 2u);
-  EXPECT_EQ(data.uncompressed_length, 2u);
-  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
-            0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(2u, data.compressed_length);
+  EXPECT_EQ(2u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
   buffer[2] = 0;
   EXPECT_STREQ("he", buffer);
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file/file.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 3u);
-  EXPECT_EQ(data.uncompressed_length, 3u);
-  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
-            0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(3u, data.compressed_length);
+  EXPECT_EQ(3u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
   buffer[3] = 0;
   EXPECT_STREQ("llo", buffer);
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file/file2.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 0u);
-  EXPECT_EQ(data.uncompressed_length, 0u);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.compressed_length);
+  EXPECT_EQ(0u, data.uncompressed_length);
 
   CloseArchive(handle);
 }
@@ -124,17 +124,47 @@
 TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
   ZipWriter writer(file_);
 
-  ASSERT_EQ(writer.StartEntry("align.txt", ZipWriter::kAlign32), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(FindEntry(handle, ZipString("align.txt"), &data), 0);
-  EXPECT_EQ(data.offset & 0x03, 0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0x03);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+  ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressDeflated, data.method);
+  EXPECT_EQ(4u, data.uncompressed_length);
+
+  char buffer[5];
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+  buffer[4] = 0;
+
+  EXPECT_STREQ("helo", buffer);
+
+  CloseArchive(handle);
 }