Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: ui/file_manager/zip_archiver/cpp/compressor_archive_libarchive.cc

Issue 2807063002: Replace Libarchive with MiniZip. (Closed)
Patch Set: Fix the code according to the comments. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "compressor_archive_libarchive.h" 5 #include "compressor_archive_libarchive.h"
6 6
7 #include <cerrno> 7 #include <cerrno>
8 #include <cstring> 8 #include <cstring>
9 9
10 #include "archive_entry.h" 10 #include "base/time/time.h"
11 #include "ppapi/cpp/logging.h" 11 #include "ppapi/cpp/logging.h"
12 12
13 namespace { 13 namespace compressor_archive_functions {
14 // Nothing to do here because JavaScript takes care of file open operations. 14
15 int CustomArchiveOpen(archive* archive_object, void* client_data) { 15 // Called when minizip tries to open a zip archive file. We do nothing here
16 return ARCHIVE_OK; 16 // because JavaScript takes care of file opening operation.
17 } 17 void* CustomArchiveOpen(void* compressor,
18 18 const char* /*filename*/, int /*mode*/) {
19 // Called when any data chunk must be written on the archive. It copies data 19 return compressor;
20 // from the given buffer processed by libarchive to an array buffer and passes 20 }
21 // it to compressor_stream. 21
22 ssize_t CustomArchiveWrite(archive* archive_object, void* client_data, 22 // This function is not called because we don't unpack zip files here.
23 const void* buffer, size_t length) { 23 uLong CustomArchiveRead(void* /*compressor*/, void* /*stream*/,
24 CompressorArchiveLibarchive* compressor_libarchive = 24 void* /*buffuer*/, uLong /*size*/) {
mtomasz 2017/04/11 06:36:45 typo: buffer
25 static_cast<CompressorArchiveLibarchive*>(client_data); 25 return 0 /* Success */;
26 26 }
27 const char* char_buffer = static_cast<const char*>(buffer); 27
28 28 // Called when data chunk must be written on the archive. It copies data
29 // Copy the data in buffer to array_buffer. 29 // from the given buffer processed by minizip to an array buffer and passes
30 PP_DCHECK(length > 0); 30 // it to compressor_stream.
31 pp::VarArrayBuffer array_buffer(length); 31 uLong CustomArchiveWrite(void* compressor,
32 char* array_buffer_data = static_cast<char*>(array_buffer.Map()); 32 void* /*stream*/,
33 memcpy(array_buffer_data, char_buffer, length); 33 const void* zip_buffer,
34 array_buffer.Unmap(); 34 uLong zip_length) {
35 35 CompressorArchiveLibarchive* compressor_libarchive =
36 ssize_t written_bytes = 36 static_cast<CompressorArchiveLibarchive*>(compressor);
37 compressor_libarchive->compressor_stream()->Write(length, array_buffer); 37
38 38 int64_t written_bytes = compressor_libarchive->compressor_stream()->Write(
39 // Negative written_bytes represents an error. 39 compressor_libarchive->offset_, zip_length,
40 if (written_bytes < 0) { 40 static_cast<const char*>(zip_buffer));
41 // When writing fails, archive_set_error() should be called and -1 should 41
42 // be returned. 42 if (written_bytes != zip_length)
43 archive_set_error( 43 return 0 /* Error */;
44 compressor_libarchive->archive(), EIO, "Failed to write a chunk."); 44
45 return -1; 45 // Update offset_ and length_.
46 } 46 compressor_libarchive->offset_ += written_bytes;
47 return written_bytes; 47 if (compressor_libarchive->offset_ > compressor_libarchive->length_)
48 } 48 compressor_libarchive->length_ = compressor_libarchive->offset_;
49 49 return static_cast<uLong>(written_bytes);
50 // Nothing to do here because JavaScript takes care of file close operations. 50 }
51 int CustomArchiveClose(archive* archive_object, void* client_data) { 51
52 return ARCHIVE_OK; 52 // Returns the offset from the beginning of the data.
53 } 53 long CustomArchiveTell(void* compressor, void* /*stream*/) {
54 } 54 CompressorArchiveLibarchive* compressor_libarchive =
55 static_cast<CompressorArchiveLibarchive*>(compressor);
56 return static_cast<long>(compressor_libarchive->offset_);
57 }
58
59 // Moves the current offset to the specified position.
60 long CustomArchiveSeek(void* compressor,
61 void* /*stream*/,
62 uLong offset,
63 int origin) {
64 CompressorArchiveLibarchive* compressor_libarchive =
65 static_cast<CompressorArchiveLibarchive*>(compressor);
66
67 if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
68 compressor_libarchive->offset_ =
69 std::min(compressor_libarchive->offset_ + static_cast<int64_t>(offset),
70 compressor_libarchive->length_);
71 return 0 /* Success */;
72 }
73 if (origin == ZLIB_FILEFUNC_SEEK_END) {
74 compressor_libarchive->offset_ =
75 std::max(compressor_libarchive->length_ - static_cast<int64_t>(offset),
76 0LL);
77 return 0 /* Success */;
78 }
79 if (origin == ZLIB_FILEFUNC_SEEK_SET) {
80 compressor_libarchive->offset_ =
81 std::min(static_cast<int64_t>(offset), compressor_libarchive->length_);
82 return 0 /* Success */;
83 }
84 return -1 /* Error */;
mtomasz 2017/04/11 06:36:45 nit: We could introduce an enum and avoid these co
85 }
86
87 // Releases all used resources. compressor points to compressor_libarchive and
88 // it is deleted in the destructor of Compressor, so we don't need to delete
89 // it here.
90 int CustomArchiveClose(void* /*compressor*/, void* /*stream*/) {
91 return 0 /* Success */;
92 }
93
94 // Returns the last error that happened when writing data. This function always
95 // returns zero, which means there are no errors.
96 int CustomArchiveError(void* /*compressor*/, void* /*stream*/) {
97 return 0 /* Success */;
98 }
99
100 } // compressor_archive_functions
55 101
56 CompressorArchiveLibarchive::CompressorArchiveLibarchive( 102 CompressorArchiveLibarchive::CompressorArchiveLibarchive(
57 CompressorStream* compressor_stream) 103 CompressorStream* compressor_stream)
58 : CompressorArchive(compressor_stream), 104 : CompressorArchive(compressor_stream),
59 compressor_stream_(compressor_stream) { 105 compressor_stream_(compressor_stream),
106 zip_file_(nullptr),
107 offset_(0),
108 length_(0) {
60 destination_buffer_ = 109 destination_buffer_ =
61 new char[compressor_archive_constants::kMaximumDataChunkSize]; 110 new char[compressor_stream_constants::kMaximumDataChunkSize];
62 } 111 }
63 112
64 CompressorArchiveLibarchive::~CompressorArchiveLibarchive() { 113 CompressorArchiveLibarchive::~CompressorArchiveLibarchive() {
65 delete destination_buffer_; 114 delete destination_buffer_;
66 } 115 }
67 116
68 void CompressorArchiveLibarchive::CreateArchive() { 117 bool CompressorArchiveLibarchive::CreateArchive() {
69 archive_ = archive_write_new(); 118 // Set up archive object.
70 archive_write_set_format_zip(archive_); 119 zlib_filefunc_def zip_funcs;
71 120 zip_funcs.zopen_file = compressor_archive_functions::CustomArchiveOpen;
72 // Passing 1 as the second argument causes the final chunk not to be padded. 121 zip_funcs.zread_file = compressor_archive_functions::CustomArchiveRead;
73 archive_write_set_bytes_in_last_block(archive_, 1); 122 zip_funcs.zwrite_file = compressor_archive_functions::CustomArchiveWrite;
74 archive_write_set_bytes_per_block( 123 zip_funcs.ztell_file = compressor_archive_functions::CustomArchiveTell;
75 archive_, compressor_archive_constants::kMaximumDataChunkSize); 124 zip_funcs.zseek_file = compressor_archive_functions::CustomArchiveSeek;
76 archive_write_open(archive_, this, CustomArchiveOpen, 125 zip_funcs.zclose_file = compressor_archive_functions::CustomArchiveClose;
77 CustomArchiveWrite, CustomArchiveClose); 126 zip_funcs.zerror_file = compressor_archive_functions::CustomArchiveError;
78 } 127 zip_funcs.opaque = this;
79 128
80 void CompressorArchiveLibarchive::AddToArchive( 129 zip_file_ = zipOpen2(nullptr /* pathname */,
81 const std::string& filename, 130 APPEND_STATUS_CREATE,
82 int64_t file_size, 131 nullptr /* globalcomment */,
83 time_t modification_time, 132 &zip_funcs);
84 bool is_directory) { 133 if (!zip_file_) {
85 entry = archive_entry_new(); 134 set_error_message(compressor_archive_constants::kCreateArchiveError);
86 135 return false /* Error */;
87 archive_entry_set_pathname(entry, filename.c_str()); 136 }
88 archive_entry_set_size(entry, file_size); 137 return true /* Success */;
89 archive_entry_set_mtime(entry, modification_time, 0 /* millisecond */); 138 }
90 139
91 if (is_directory) { 140 bool CompressorArchiveLibarchive::AddToArchive(const std::string& filename,
92 archive_entry_set_filetype(entry, AE_IFDIR); 141 int64_t file_size,
93 archive_entry_set_perm( 142 int64_t modification_time,
94 entry, compressor_archive_constants::kDirectoryPermission); 143 bool is_directory) {
95 } else { 144 // Minizip takes filenames that end with '/' as directories.
96 archive_entry_set_filetype(entry, AE_IFREG); 145 std::string normalized_filename = filename;
97 archive_entry_set_perm( 146 if (is_directory)
98 entry, compressor_archive_constants::kFilePermission); 147 normalized_filename += "/";
99 } 148
100 archive_write_header(archive_, entry); 149 // Fill zipfileMetadata with modification_time.
101 // If archive_errno() returns 0, the header was written correctly. 150 zip_fileinfo zipfileMetadata;
102 if (archive_errno(archive_) != 0) { 151 // modification_time is millisecond-based, while FromTimeT takes seconds.
103 CloseArchive(true /* hasError */); 152 base::Time tm = base::Time::FromTimeT((int64_t)modification_time / 1000);
104 return; 153 base::Time::Exploded exploded_time = {};
105 } 154 tm.LocalExplode(&exploded_time);
106 155 zipfileMetadata.tmz_date.tm_sec = exploded_time.second;
156 zipfileMetadata.tmz_date.tm_min = exploded_time.minute;
157 zipfileMetadata.tmz_date.tm_hour = exploded_time.hour;
158 zipfileMetadata.tmz_date.tm_year = exploded_time.year;
159 zipfileMetadata.tmz_date.tm_mday = exploded_time.day_of_month;
160 // Convert from 1-based to 0-based.
161 zipfileMetadata.tmz_date.tm_mon = exploded_time.month - 1;
162
163 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
164 // Setting the Language encoding flag so the file is told to be in utf-8.
165 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
166
167 int open_result = zipOpenNewFileInZip4(zip_file_, // file
168 normalized_filename.c_str(),// filename
169 &zipfileMetadata, // zipfi
170 nullptr, // extrafield_loc al
171 0u, // size_extrafield_local
172 nullptr, // extrafield_global
mtomasz 2017/04/11 06:36:45 nit: Please fix indentation of comments.
173 0u, // size_extrafield_global
174 nullptr, // comment
175 Z_DEFLATED, // method
176 Z_DEFAULT_COMPRESSION, // level
177 0, // raw
178 -MAX_WBITS, // windowBits
179 DEF_MEM_LEVEL, // memLevel
180 Z_DEFAULT_STRATEGY, // strategy
181 nullptr, // password
182 0, // crcForCrypting
183 0, // versionMadeBy
184 LANGUAGE_ENCODING_FLAG); // flagBase
185 if (open_result != ZIP_OK) {
186 CloseArchive(true /* has_error */);
187 set_error_message(compressor_archive_constants::kAddToArchiveError);
188 return false /* Error */;
189 }
190
191 bool has_error = false;
107 if (!is_directory) { 192 if (!is_directory) {
108 int64_t remaining_size = file_size; 193 int64_t remaining_size = file_size;
109 while (remaining_size > 0) { 194 while (remaining_size > 0) {
110 int64_t chunk_size = std::min(remaining_size, 195 int64_t chunk_size = std::min(remaining_size,
111 compressor_archive_constants::kMaximumDataChunkSize); 196 compressor_stream_constants::kMaximumDataChunkSize);
112 PP_DCHECK(chunk_size > 0); 197 PP_DCHECK(chunk_size > 0);
113 198
114 int64_t read_bytes = 199 int64_t read_bytes =
115 compressor_stream_->Read(chunk_size, destination_buffer_); 200 compressor_stream_->Read(chunk_size, destination_buffer_);
201
116 // Negative read_bytes indicates an error occurred when reading chunks. 202 // Negative read_bytes indicates an error occurred when reading chunks.
117 if (read_bytes < 0) { 203 // 0 just means there is no more data available, but here we need positive
118 CloseArchive(true /* hasError */); 204 // length of bytes, so this is also an error here.
205 if (read_bytes <= 0) {
206 has_error = true;
119 break; 207 break;
120 } 208 }
121 209
122 int64_t written_bytes = 210 if (zipWriteInFileInZip(zip_file_, destination_buffer_, read_bytes) !=
123 archive_write_data(archive_, destination_buffer_, read_bytes); 211 ZIP_OK) {
124 // If archive_errno() returns 0, the buffer was written correctly. 212 has_error = true;
125 if (archive_errno(archive_) != 0) {
126 CloseArchive(true /* hasError */);
127 break; 213 break;
128 } 214 }
129 PP_DCHECK(written_bytes > 0); 215 remaining_size -= read_bytes;
130
131 remaining_size -= written_bytes;
132 } 216 }
133 } 217 }
134 218
135 archive_entry_free(entry); 219 if (!has_error && zipCloseFileInZip(zip_file_) != ZIP_OK)
136 } 220 has_error = true;
137 221
138 void CompressorArchiveLibarchive::CloseArchive(bool has_error) { 222 if (has_error) {
139 // If has_error is true, mark the archive object as being unusable and 223 CloseArchive(true /* has_error */);
140 // release resources without writing no more data on the archive. 224 set_error_message(compressor_archive_constants::kAddToArchiveError);
141 if (has_error) 225 return false /* Error */;
142 archive_write_fail(archive_); 226 }
143 if (archive_) { 227
144 archive_write_free(archive_); 228 return true /* Success */;
145 archive_ = NULL; 229 }
146 } 230
147 } 231 bool CompressorArchiveLibarchive::CloseArchive(bool has_error) {
232 if (zipClose(zip_file_, nullptr /* global_comment */) != ZIP_OK) {
233 set_error_message(compressor_archive_constants::kCloseArchiveError);
234 return false /* Error */;
235 }
236 if (!has_error) {
237 if (compressor_stream()->Flush() < 0) {
238 set_error_message(compressor_archive_constants::kCloseArchiveError);
239 return false /* Error */;
240 }
241 }
242 return true /* Success */;
243 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698