Chromium Code Reviews| Index: ui/file_manager/zip_archiver/cpp/compressor_archive_libarchive.cc |
| diff --git a/ui/file_manager/zip_archiver/cpp/compressor_archive_libarchive.cc b/ui/file_manager/zip_archiver/cpp/compressor_archive_libarchive.cc |
| index 92ac415c33ed5aca13d3606d0bba7c6fbe51bb56..371c94d34ae7e891b3c6f0263be0446f1aba8752 100644 |
| --- a/ui/file_manager/zip_archiver/cpp/compressor_archive_libarchive.cc |
| +++ b/ui/file_manager/zip_archiver/cpp/compressor_archive_libarchive.cc |
| @@ -7,141 +7,232 @@ |
| #include <cerrno> |
| #include <cstring> |
| -#include "archive_entry.h" |
| +#include "base/time/time.h" |
| #include "ppapi/cpp/logging.h" |
| -namespace { |
| - // Nothing to do here because JavaScript takes care of file open operations. |
| - int CustomArchiveOpen(archive* archive_object, void* client_data) { |
| - return ARCHIVE_OK; |
| - } |
| +namespace compressor_archive_functions { |
| +// Called when minizip tries to open a zip archive file. We do nothing here |
|
mtomasz
2017/04/10 07:15:08
nit: Let's add \n after the namespace line for bet
takise
2017/04/11 06:00:51
Done.
|
| +// because JavaScript takes care of file opening operation. |
| +void* CustomArchiveOpen(void* opaque, const char* filename, int mode) { |
| + return opaque; |
| +} |
| - // Called when any data chunk must be written on the archive. It copies data |
| - // from the given buffer processed by libarchive to an array buffer and passes |
| - // it to compressor_stream. |
| - ssize_t CustomArchiveWrite(archive* archive_object, void* client_data, |
| - const void* buffer, size_t length) { |
| - CompressorArchiveLibarchive* compressor_libarchive = |
| - static_cast<CompressorArchiveLibarchive*>(client_data); |
| - |
| - const char* char_buffer = static_cast<const char*>(buffer); |
| - |
| - // Copy the data in buffer to array_buffer. |
| - PP_DCHECK(length > 0); |
| - pp::VarArrayBuffer array_buffer(length); |
| - char* array_buffer_data = static_cast<char*>(array_buffer.Map()); |
| - memcpy(array_buffer_data, char_buffer, length); |
| - array_buffer.Unmap(); |
| - |
| - ssize_t written_bytes = |
| - compressor_libarchive->compressor_stream()->Write(length, array_buffer); |
| - |
| - // Negative written_bytes represents an error. |
| - if (written_bytes < 0) { |
| - // When writing fails, archive_set_error() should be called and -1 should |
| - // be returned. |
| - archive_set_error( |
| - compressor_libarchive->archive(), EIO, "Failed to write a chunk."); |
| - return -1; |
| - } |
| - return written_bytes; |
| - } |
| +// This function is not called because we don't unpack zip files here. |
| +uLong CustomArchiveRead(void* opaque, void* /*stream*/, void* buf, uLong size) { |
|
mtomasz
2017/04/10 07:15:08
nit: We usually comment out unused arguments. So l
takise
2017/04/11 06:00:51
Done.
|
| + return 0 /* Success */; |
| +} |
| + |
| +// Called when data chunk must be written on the archive. It copies data |
| +// from the given buffer processed by minizip to an array buffer and passes |
| +// it to compressor_stream. |
| +uLong CustomArchiveWrite(void* opaque, |
|
mtomasz
2017/04/10 07:15:08
Why is this argument called opaque? Can we rename
takise
2017/04/11 06:00:51
Done.
|
| + void* /*stream*/, |
| + const void* zip_buffer, |
| + uLong zip_length) { |
| + CompressorArchiveLibarchive* compressor_libarchive = |
| + static_cast<CompressorArchiveLibarchive*>(opaque); |
| + |
| + int64_t written_bytes = compressor_libarchive->compressor_stream()->Write( |
| + compressor_libarchive->offset_, zip_length, zip_buffer); |
| + |
| + if (written_bytes != zip_length) |
| + return 0 /* Error */; |
| + |
| + // Update offset_ and length_. |
| + compressor_libarchive->offset_ += written_bytes; |
| + if (compressor_libarchive->offset_ > compressor_libarchive->length_) |
| + compressor_libarchive->length_ = compressor_libarchive->offset_; |
| + return static_cast<uLong>(written_bytes); |
| +} |
| - // Nothing to do here because JavaScript takes care of file close operations. |
| - int CustomArchiveClose(archive* archive_object, void* client_data) { |
| - return ARCHIVE_OK; |
| +// Returns the offset from the beginning of the data. |
| +long CustomArchiveTell(void* opaque, void* /*stream*/) { |
| + CompressorArchiveLibarchive* compressor_libarchive = |
|
mtomasz
2017/04/10 07:15:08
Can we rename libarchive from variable/class/metho
|
| + static_cast<CompressorArchiveLibarchive*>(opaque); |
| + return static_cast<long>(compressor_libarchive->offset_); |
| +} |
| + |
| +// Moves the current offset to the specified position. |
| +long CustomArchiveSeek(void* opaque, |
| + void* /*stream*/, |
| + uLong offset, |
| + int origin) { |
| + CompressorArchiveLibarchive* compressor_libarchive = |
| + static_cast<CompressorArchiveLibarchive*>(opaque); |
| + |
| + if (origin == ZLIB_FILEFUNC_SEEK_CUR) { |
| + compressor_libarchive->offset_ = |
| + std::min(compressor_libarchive->offset_ + static_cast<int64_t>(offset), |
| + compressor_libarchive->length_); |
| + return 0 /* Success */; |
| + } |
| + if (origin == ZLIB_FILEFUNC_SEEK_END) { |
| + compressor_libarchive->offset_ = |
| + std::max(compressor_libarchive->length_ - static_cast<int64_t>(offset), |
| + 0); |
| + return 0 /* Success */; |
| } |
| + if (origin == ZLIB_FILEFUNC_SEEK_SET) { |
| + compressor_libarchive->offset_ = |
| + std::min(static_cast<int64_t>(offset), compressor_libarchive->length_); |
| + return 0 /* Success */; |
| + } |
| + return -1 /* Error */; |
| +} |
| + |
| +// Releases all used resources. opaque points to compressor_libarchive and |
| +// it is deleted elsewhere, so we don't need to delete it here. |
|
mtomasz
2017/04/10 07:15:08
nit: elsewhere -> ...
Can we write a method name i
takise
2017/04/11 06:00:51
Done.
|
| +int CustomArchiveClose(void* opaque, void* /*stream*/) { |
| + return 0 /* Success */; |
| } |
| +// Returns the last error that happened when writing data. This function always |
| +// returns zero, which means there are no errors. |
| +int CustomArchiveError(void* /*opaque*/, void* /*stream*/) { |
| + return 0 /* Success */; |
| +} |
| + |
| +} // compressor_archive_functions |
| + |
| CompressorArchiveLibarchive::CompressorArchiveLibarchive( |
| CompressorStream* compressor_stream) |
| : CompressorArchive(compressor_stream), |
| - compressor_stream_(compressor_stream) { |
| + compressor_stream_(compressor_stream), |
| + zip_file_(NULL), |
| + offset_(0), |
| + length_(0) { |
| destination_buffer_ = |
| - new char[compressor_archive_constants::kMaximumDataChunkSize]; |
| + new char[compressor_stream_constants::kMaximumDataChunkSize]; |
| } |
| CompressorArchiveLibarchive::~CompressorArchiveLibarchive() { |
| delete destination_buffer_; |
| } |
| -void CompressorArchiveLibarchive::CreateArchive() { |
| - archive_ = archive_write_new(); |
| - archive_write_set_format_zip(archive_); |
| - |
| - // Passing 1 as the second argument causes the final chunk not to be padded. |
| - archive_write_set_bytes_in_last_block(archive_, 1); |
| - archive_write_set_bytes_per_block( |
| - archive_, compressor_archive_constants::kMaximumDataChunkSize); |
| - archive_write_open(archive_, this, CustomArchiveOpen, |
| - CustomArchiveWrite, CustomArchiveClose); |
| +bool CompressorArchiveLibarchive::CreateArchive() { |
| + // Set up archive object. |
| + zlib_filefunc_def zip_funcs; |
| + zip_funcs.zopen_file = compressor_archive_functions::CustomArchiveOpen; |
| + zip_funcs.zread_file = compressor_archive_functions::CustomArchiveRead; |
| + zip_funcs.zwrite_file = compressor_archive_functions::CustomArchiveWrite; |
| + zip_funcs.ztell_file = compressor_archive_functions::CustomArchiveTell; |
| + zip_funcs.zseek_file = compressor_archive_functions::CustomArchiveSeek; |
| + zip_funcs.zclose_file = compressor_archive_functions::CustomArchiveClose; |
| + zip_funcs.zerror_file = compressor_archive_functions::CustomArchiveError; |
| + zip_funcs.opaque = this; |
| + |
| + zip_file_ = zipOpen2(NULL /* pathname */, |
|
mtomasz
2017/04/10 07:15:08
NULL -> nullptr here and in other places
takise
2017/04/11 06:00:51
Done.
|
| + APPEND_STATUS_CREATE, |
| + NULL /* globalcomment */, |
| + &zip_funcs); |
| + if (!zip_file_) { |
| + set_error_message(compressor_archive_constants::kCreateArchiveError); |
| + return false /* Error */; |
| + } |
| + return true /* Success */; |
| } |
| -void CompressorArchiveLibarchive::AddToArchive( |
| - const std::string& filename, |
| - int64_t file_size, |
| - time_t modification_time, |
| - bool is_directory) { |
| - entry = archive_entry_new(); |
| - |
| - archive_entry_set_pathname(entry, filename.c_str()); |
| - archive_entry_set_size(entry, file_size); |
| - archive_entry_set_mtime(entry, modification_time, 0 /* millisecond */); |
| - |
| - if (is_directory) { |
| - archive_entry_set_filetype(entry, AE_IFDIR); |
| - archive_entry_set_perm( |
| - entry, compressor_archive_constants::kDirectoryPermission); |
| - } else { |
| - archive_entry_set_filetype(entry, AE_IFREG); |
| - archive_entry_set_perm( |
| - entry, compressor_archive_constants::kFilePermission); |
| - } |
| - archive_write_header(archive_, entry); |
| - // If archive_errno() returns 0, the header was written correctly. |
| - if (archive_errno(archive_) != 0) { |
| - CloseArchive(true /* hasError */); |
| - return; |
| +bool CompressorArchiveLibarchive::AddToArchive(const std::string& _filename, |
|
mtomasz
2017/04/10 07:15:08
nit: We never prefix with _. Can we keep filename
takise
2017/04/11 06:00:51
Done.
|
| + int64_t file_size, |
| + int64_t modification_time, |
| + bool is_directory) { |
| + // Minizip takes filenames that end with '/' as directories. |
| + std::string filename = _filename; |
| + if (is_directory) |
| + filename += "/"; |
|
mtomasz
2017/04/10 07:15:08
Does it work with Windows paths? If not, did it wo
|
| + |
| + // Fill zipfileMetadata with modification_time. |
| + zip_fileinfo zipfileMetadata; |
| + // modification_time is millisecond-based, while FromTimeT takes seconds. |
| + base::Time tm = base::Time::FromTimeT((int64_t)modification_time / 1000); |
| + base::Time::Exploded exploded_time = {}; |
| + tm.LocalExplode(&exploded_time); |
| + zipfileMetadata.tmz_date.tm_sec = exploded_time.second; |
| + zipfileMetadata.tmz_date.tm_min = exploded_time.minute; |
| + zipfileMetadata.tmz_date.tm_hour = exploded_time.hour; |
| + zipfileMetadata.tmz_date.tm_year = exploded_time.year; |
| + zipfileMetadata.tmz_date.tm_mday = exploded_time.day_of_month; |
| + // Convert from 1-based to 0-based. |
| + zipfileMetadata.tmz_date.tm_mon = exploded_time.month - 1; |
| + |
| + // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT |
| + // Setting the Language encoding flag so the file is told to be in utf-8. |
| + const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11; |
| + |
| + int open_result = zipOpenNewFileInZip4(zip_file_, // file |
| + filename.c_str(), // filename |
| + &zipfileMetadata, // zipfi |
| + NULL, // extrafield_local |
| + 0u, // size_extrafield_local |
| + NULL, // extrafield_global |
| + 0u, // size_extrafield_global |
| + NULL, // comment |
| + Z_DEFLATED, // method |
| + Z_DEFAULT_COMPRESSION, // level |
| + 0, // raw |
| + -MAX_WBITS, // windowBits |
| + DEF_MEM_LEVEL, // memLevel |
| + Z_DEFAULT_STRATEGY, // strategy |
| + NULL, // password |
| + 0, // crcForCrypting |
| + 0, // versionMadeBy |
| + LANGUAGE_ENCODING_FLAG); // flagBase |
| + if (open_result != ZIP_OK) { |
| + CloseArchive(true /* has_error */); |
| + set_error_message(compressor_archive_constants::kAddtoArchiveError); |
| + return false /* Error */; |
| } |
| + bool has_error = false; |
| if (!is_directory) { |
| int64_t remaining_size = file_size; |
| while (remaining_size > 0) { |
| int64_t chunk_size = std::min(remaining_size, |
| - compressor_archive_constants::kMaximumDataChunkSize); |
| + compressor_stream_constants::kMaximumDataChunkSize); |
| PP_DCHECK(chunk_size > 0); |
| int64_t read_bytes = |
| compressor_stream_->Read(chunk_size, destination_buffer_); |
| + |
| // Negative read_bytes indicates an error occurred when reading chunks. |
| - if (read_bytes < 0) { |
| - CloseArchive(true /* hasError */); |
| + // 0 just means there is no more data available, but here we need positive |
| + // length of bytes, so this is also an error here. |
| + if (read_bytes <= 0) { |
| + has_error = true; |
| break; |
| } |
| - int64_t written_bytes = |
| - archive_write_data(archive_, destination_buffer_, read_bytes); |
| - // If archive_errno() returns 0, the buffer was written correctly. |
| - if (archive_errno(archive_) != 0) { |
| - CloseArchive(true /* hasError */); |
| + if (zipWriteInFileInZip(zip_file_, destination_buffer_, read_bytes) != |
| + ZIP_OK) { |
| + has_error = true; |
| break; |
| } |
| - PP_DCHECK(written_bytes > 0); |
| - |
| - remaining_size -= written_bytes; |
| + remaining_size -= read_bytes; |
| } |
| } |
| - archive_entry_free(entry); |
| + if (!has_error && zipCloseFileInZip(zip_file_) != ZIP_OK) |
| + has_error = true; |
| + |
| + if (has_error) { |
| + CloseArchive(true /* has_error */); |
| + set_error_message(compressor_archive_constants::kAddtoArchiveError); |
| + return false /* Error */; |
| + } |
| + |
| + return true /* Success */; |
| } |
| -void CompressorArchiveLibarchive::CloseArchive(bool has_error) { |
| - // If has_error is true, mark the archive object as being unusable and |
| - // release resources without writing no more data on the archive. |
| - if (has_error) |
| - archive_write_fail(archive_); |
| - if (archive_) { |
| - archive_write_free(archive_); |
| - archive_ = NULL; |
| +bool CompressorArchiveLibarchive::CloseArchive(bool has_error) { |
| + if (zipClose(zip_file_, NULL /* global_comment */) != ZIP_OK) { |
| + set_error_message(compressor_archive_constants::kCloseArchiveError); |
| + return false /* Error */; |
| + } |
| + if (!has_error) { |
| + if (compressor_stream()->Flush() < 0) { |
| + set_error_message(compressor_archive_constants::kCloseArchiveError); |
| + return false /* Error */; |
| + } |
| } |
| + return true /* Success */; |
| } |