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

Commit 291db047 authored by Adam Lesinski's avatar Adam Lesinski Committed by Android (Google) Code Review
Browse files

Merge "Implement ZipWriter for quickly writing ZipFiles."

parents ee6517ee 736bdcdf
Loading
Loading
Loading
Loading
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
#define LIBZIPARCHIVE_ZIPWRITER_H_

#include "base/macros.h"
#include <utils/Compat.h>

#include <cstdio>
#include <string>
#include <ctime>
#include <vector>

/**
 * Writes a Zip file via a stateful interface.
 *
 * Example:
 *
 *   FILE* file = fopen("path/to/zip.zip", "wb");
 *
 *   ZipWriter writer(file);
 *
 *   writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
 *   writer.WriteBytes(buffer, bufferLen);
 *   writer.WriteBytes(buffer2, bufferLen2);
 *   writer.FinishEntry();
 *
 *   writer.StartEntry("empty.txt", 0);
 *   writer.FinishEntry();
 *
 *   writer.Finish();
 *
 *   fclose(file);
 */
class ZipWriter {
public:
  enum {
    /**
     * Flag to compress the zip entry using deflate.
     */
    kCompress = 0x01,

    /**
     * Flag to align the zip entry data on a 32bit boundary. Useful for
     * mmapping the data at runtime.
     */
    kAlign32 = 0x02,
  };

  static const char* ErrorCodeString(int32_t error_code);

  /**
   * Create a ZipWriter that will write into a FILE stream. The file should be opened with
   * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
   * caller is responsible for closing the file.
   */
  explicit ZipWriter(FILE* f);

  // Move constructor.
  ZipWriter(ZipWriter&& zipWriter);

  // Move assignment.
  ZipWriter& operator=(ZipWriter&& zipWriter);

  /**
   * Starts a new zip entry with the given path and flags.
   * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
   * Returns 0 on success, and an error value < 0 on failure.
   */
  int32_t StartEntry(const char* path, size_t flags);

  /**
   * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
   */
  int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);

  /**
   * Writes bytes to the zip file for the previously started zip entry.
   * Returns 0 on success, and an error value < 0 on failure.
   */
  int32_t WriteBytes(const void* data, size_t len);

  /**
   * Finish a zip entry started with StartEntry(const char*, size_t) or
   * StartEntryWithTime(const char*, size_t, time_t). This must be called before
   * any new zip entries are started, or before Finish() is called.
   * Returns 0 on success, and an error value < 0 on failure.
   */
  int32_t FinishEntry();

  /**
   * Writes the Central Directory Headers and flushes the zip file stream.
   * Returns 0 on success, and an error value < 0 on failure.
   */
  int32_t Finish();

private:
  DISALLOW_COPY_AND_ASSIGN(ZipWriter);

  int32_t HandleError(int32_t error_code);

  struct FileInfo {
    std::string path;
    uint16_t compression_method;
    uint32_t crc32;
    uint32_t compressed_size;
    uint32_t uncompressed_size;
    uint16_t last_mod_time;
    uint16_t last_mod_date;
    uint32_t local_file_header_offset;
  };

  enum class State {
    kWritingZip,
    kWritingEntry,
    kDone,
    kError,
  };

  FILE* file_;
  off64_t current_offset_;
  State state_;
  std::vector<FileInfo> files_;
};

#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
+9 −3
Original line number Diff line number Diff line
@@ -15,7 +15,8 @@

LOCAL_PATH := $(call my-dir)

source_files := zip_archive.cc
source_files := zip_archive.cc zip_writer.cc
test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc

include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
@@ -34,6 +35,11 @@ LOCAL_STATIC_LIBRARIES := libz libutils libbase
LOCAL_MODULE:= libziparchive-host
LOCAL_CFLAGS := -Werror
LOCAL_CFLAGS_windows := -mno-ms-bitfields

# Incorrectly warns when C++11 empty brace {} initializer is used.
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
LOCAL_CFLAGS_windows += -Wno-missing-field-initializers

LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -53,7 +59,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := ziparchive-tests
LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS := -Werror
LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
LOCAL_SRC_FILES := $(test_files)
LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
include $(BUILD_NATIVE_TEST)
@@ -64,7 +70,7 @@ LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS += \
    -Werror \
    -Wno-unnamed-type-template-args
LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
LOCAL_SRC_FILES := $(test_files)
LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
LOCAL_STATIC_LIBRARIES := \
    libz \
+1 −155
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include "zlib.h"

#include "entry_name_utils-inl.h"
#include "zip_archive_common.h"
#include "ziparchive/zip_archive.h"

using android::base::get_unaligned;
@@ -49,161 +50,6 @@ using android::base::get_unaligned;
#define O_BINARY 0
#endif

// 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
// number of entries in the archive and the offset to the central
// directory of the offset.
struct EocdRecord {
  static const uint32_t kSignature = 0x06054b50;

  // End of central directory signature, should always be
  // |kSignature|.
  uint32_t eocd_signature;
  // The number of the current "disk", i.e, the "disk" that this
  // central directory is on.
  //
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that disk_num == 1.
  uint16_t disk_num;
  // The disk where the central directory starts.
  //
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that cd_start_disk == 1.
  uint16_t cd_start_disk;
  // The number of central directory records on this disk.
  //
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that num_records_on_disk == num_records.
  uint16_t num_records_on_disk;
  // The total number of central directory records.
  uint16_t num_records;
  // The size of the central directory (in bytes).
  uint32_t cd_size;
  // The offset of the start of the central directory, relative
  // to the start of the file.
  uint32_t cd_start_offset;
  // Length of the central directory comment.
  uint16_t comment_length;
 private:
  EocdRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
} __attribute__((packed));

// A structure representing the fixed length fields for a single
// record in the central directory of the archive. In addition to
// the fixed length fields listed here, each central directory
// record contains a variable length "file_name" and "extra_field"
// whose lengths are given by |file_name_length| and |extra_field_length|
// respectively.
struct CentralDirectoryRecord {
  static const uint32_t kSignature = 0x02014b50;

  // The start of record signature. Must be |kSignature|.
  uint32_t record_signature;
  // Tool version. Ignored by this implementation.
  uint16_t version_made_by;
  // Tool version. Ignored by this implementation.
  uint16_t version_needed;
  // The "general purpose bit flags" for this entry. The only
  // flag value that we currently check for is the "data descriptor"
  // flag.
  uint16_t gpb_flags;
  // The compression method for this entry, one of |kCompressStored|
  // and |kCompressDeflated|.
  uint16_t compression_method;
  // The file modification time and date for this entry.
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  // The CRC-32 checksum for this entry.
  uint32_t crc32;
  // The compressed size (in bytes) of this entry.
  uint32_t compressed_size;
  // The uncompressed size (in bytes) of this entry.
  uint32_t uncompressed_size;
  // The length of the entry file name in bytes. The file name
  // will appear immediately after this record.
  uint16_t file_name_length;
  // The length of the extra field info (in bytes). This data
  // will appear immediately after the entry file name.
  uint16_t extra_field_length;
  // The length of the entry comment (in bytes). This data will
  // appear immediately after the extra field.
  uint16_t comment_length;
  // The start disk for this entry. Ignored by this implementation).
  uint16_t file_start_disk;
  // File attributes. Ignored by this implementation.
  uint16_t internal_file_attributes;
  // File attributes. Ignored by this implementation.
  uint32_t external_file_attributes;
  // The offset to the local file header for this entry, from the
  // beginning of this archive.
  uint32_t local_file_header_offset;
 private:
  CentralDirectoryRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
} __attribute__((packed));

// The local file header for a given entry. This duplicates information
// present in the central directory of the archive. It is an error for
// the information here to be different from the central directory
// information for a given entry.
struct LocalFileHeader {
  static const uint32_t kSignature = 0x04034b50;

  // The local file header signature, must be |kSignature|.
  uint32_t lfh_signature;
  // Tool version. Ignored by this implementation.
  uint16_t version_needed;
  // The "general purpose bit flags" for this entry. The only
  // flag value that we currently check for is the "data descriptor"
  // flag.
  uint16_t gpb_flags;
  // The compression method for this entry, one of |kCompressStored|
  // and |kCompressDeflated|.
  uint16_t compression_method;
  // The file modification time and date for this entry.
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  // The CRC-32 checksum for this entry.
  uint32_t crc32;
  // The compressed size (in bytes) of this entry.
  uint32_t compressed_size;
  // The uncompressed size (in bytes) of this entry.
  uint32_t uncompressed_size;
  // The length of the entry file name in bytes. The file name
  // will appear immediately after this record.
  uint16_t file_name_length;
  // The length of the extra field info (in bytes). This data
  // will appear immediately after the entry file name.
  uint16_t extra_field_length;
 private:
  LocalFileHeader() = default;
  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
} __attribute__((packed));

struct DataDescriptor {
  // The *optional* data descriptor start signature.
  static const uint32_t kOptSignature = 0x08074b50;

  // CRC-32 checksum of the entry.
  uint32_t crc32;
  // Compressed size of the entry.
  uint32_t compressed_size;
  // Uncompressed size of the entry.
  uint32_t uncompressed_size;
 private:
  DataDescriptor() = default;
  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
} __attribute__((packed));


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

// The maximum size of a central directory or a file
// comment in bytes.
static const uint32_t kMaxCommentLen = 65535;

// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);

+179 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_

#include "base/macros.h"

#include <inttypes.h>

// 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
// number of entries in the archive and the offset to the central
// directory of the offset.
struct EocdRecord {
  static const uint32_t kSignature = 0x06054b50;

  // End of central directory signature, should always be
  // |kSignature|.
  uint32_t eocd_signature;
  // The number of the current "disk", i.e, the "disk" that this
  // central directory is on.
  //
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that disk_num == 1.
  uint16_t disk_num;
  // The disk where the central directory starts.
  //
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that cd_start_disk == 1.
  uint16_t cd_start_disk;
  // The number of central directory records on this disk.
  //
  // This implementation assumes that each archive spans a single
  // disk only. i.e, that num_records_on_disk == num_records.
  uint16_t num_records_on_disk;
  // The total number of central directory records.
  uint16_t num_records;
  // The size of the central directory (in bytes).
  uint32_t cd_size;
  // The offset of the start of the central directory, relative
  // to the start of the file.
  uint32_t cd_start_offset;
  // Length of the central directory comment.
  uint16_t comment_length;
 private:
  EocdRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
} __attribute__((packed));

// A structure representing the fixed length fields for a single
// record in the central directory of the archive. In addition to
// the fixed length fields listed here, each central directory
// record contains a variable length "file_name" and "extra_field"
// whose lengths are given by |file_name_length| and |extra_field_length|
// respectively.
struct CentralDirectoryRecord {
  static const uint32_t kSignature = 0x02014b50;

  // The start of record signature. Must be |kSignature|.
  uint32_t record_signature;
  // Tool version. Ignored by this implementation.
  uint16_t version_made_by;
  // Tool version. Ignored by this implementation.
  uint16_t version_needed;
  // The "general purpose bit flags" for this entry. The only
  // flag value that we currently check for is the "data descriptor"
  // flag.
  uint16_t gpb_flags;
  // The compression method for this entry, one of |kCompressStored|
  // and |kCompressDeflated|.
  uint16_t compression_method;
  // The file modification time and date for this entry.
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  // The CRC-32 checksum for this entry.
  uint32_t crc32;
  // The compressed size (in bytes) of this entry.
  uint32_t compressed_size;
  // The uncompressed size (in bytes) of this entry.
  uint32_t uncompressed_size;
  // The length of the entry file name in bytes. The file name
  // will appear immediately after this record.
  uint16_t file_name_length;
  // The length of the extra field info (in bytes). This data
  // will appear immediately after the entry file name.
  uint16_t extra_field_length;
  // The length of the entry comment (in bytes). This data will
  // appear immediately after the extra field.
  uint16_t comment_length;
  // The start disk for this entry. Ignored by this implementation).
  uint16_t file_start_disk;
  // File attributes. Ignored by this implementation.
  uint16_t internal_file_attributes;
  // File attributes. Ignored by this implementation.
  uint32_t external_file_attributes;
  // The offset to the local file header for this entry, from the
  // beginning of this archive.
  uint32_t local_file_header_offset;
 private:
  CentralDirectoryRecord() = default;
  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
} __attribute__((packed));

// The local file header for a given entry. This duplicates information
// present in the central directory of the archive. It is an error for
// the information here to be different from the central directory
// information for a given entry.
struct LocalFileHeader {
  static const uint32_t kSignature = 0x04034b50;

  // The local file header signature, must be |kSignature|.
  uint32_t lfh_signature;
  // Tool version. Ignored by this implementation.
  uint16_t version_needed;
  // The "general purpose bit flags" for this entry. The only
  // flag value that we currently check for is the "data descriptor"
  // flag.
  uint16_t gpb_flags;
  // The compression method for this entry, one of |kCompressStored|
  // and |kCompressDeflated|.
  uint16_t compression_method;
  // The file modification time and date for this entry.
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  // The CRC-32 checksum for this entry.
  uint32_t crc32;
  // The compressed size (in bytes) of this entry.
  uint32_t compressed_size;
  // The uncompressed size (in bytes) of this entry.
  uint32_t uncompressed_size;
  // The length of the entry file name in bytes. The file name
  // will appear immediately after this record.
  uint16_t file_name_length;
  // The length of the extra field info (in bytes). This data
  // will appear immediately after the entry file name.
  uint16_t extra_field_length;
 private:
  LocalFileHeader() = default;
  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
} __attribute__((packed));

struct DataDescriptor {
  // The *optional* data descriptor start signature.
  static const uint32_t kOptSignature = 0x08074b50;

  // CRC-32 checksum of the entry.
  uint32_t crc32;
  // Compressed size of the entry.
  uint32_t compressed_size;
  // Uncompressed size of the entry.
  uint32_t uncompressed_size;
 private:
  DataDescriptor() = default;
  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
} __attribute__((packed));

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

// The maximum size of a central directory or a file
// comment in bytes.
static const uint32_t kMaxCommentLen = 65535;

#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
+262 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading