2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
20 * @brief This file is the implementation file of zip input
28 #include <dpl/zip_input.h>
29 #include <dpl/scoped_close.h>
30 #include <dpl/binary_queue.h>
31 #include <dpl/scoped_free.h>
33 #include <dpl/scoped_array.h>
34 #include <dpl/foreach.h>
35 #include <dpl/log/log.h>
37 #include <minizip/unzip.h>
40 namespace // anonymous
42 const size_t EXTRACT_BUFFER_SIZE = 4096;
50 ScopedUnzClose(unzFile file) :
60 if (unzClose(m_file) != UNZ_OK) {
61 LogPedantic("Failed to close zip input file");
67 unzFile file = m_file;
74 } // namespace anonymous
77 * Seekable multiplexing device
80 * Minizip library lacks serious support for multithreaded
81 * access to zip files. Thus, they cannot be easily extracted
82 * simulateously. Here is introduced seekable device which does
83 * have a context with seek index for each file. File is mapped to
84 * memory and because of that no real synchronization is needed.
85 * Memory addresses can be indexed.
87 * About generalization:
88 * To achieve the same results on abstract input device, there must be
89 * provided a mechanism to read data from random address without
91 * In other words: stateless. As described above, stateless property can be
92 * achieved via memory mapping.
98 off64_t m_size; // file mapping size
99 unsigned char *m_address; // mapping base address
113 Device(const std::string &fileName)
115 LogPedantic("Creating file mapping");
116 // Open device and map it to user space
117 int file = TEMP_FAILURE_RETRY(open(fileName.c_str(), O_RDONLY));
121 ThrowMsg(ZipInput::Exception::OpenFailed,
122 "Failed to open file. errno = " << error);
125 // Scoped close on file
126 ScopedClose scopedClose(file);
128 // Calculate file size
129 off64_t size = lseek64(file, 0, SEEK_END);
131 if (size == static_cast<off64_t>(-1)) {
133 ThrowMsg(ZipInput::Exception::OpenFailed,
134 "Failed to seek file. errno = " << error);
137 // Map file to usespace
138 void *address = mmap(0, static_cast<size_t>(size),
139 PROT_READ, MAP_SHARED, file, 0);
141 if (address == MAP_FAILED) {
143 ThrowMsg(ZipInput::Exception::OpenFailed,
144 "Failed to map file. errno = " << error);
147 // Release scoped close
148 m_handle = scopedClose.Release();
150 // Save mapped up address
152 m_address = static_cast<unsigned char *>(address);
154 LogPedantic("Created file mapping: " << fileName <<
155 " of size: " << m_size <<
156 " at address: " << std::hex <<
157 static_cast<void *>(m_address));
163 if (munmap(m_address, static_cast<size_t>(m_size)) == -1) {
165 LogPedantic("Failed to munmap file. errno = " << error);
168 // Close file descriptor
169 if (close(m_handle) == -1) {
171 LogPedantic("Failed to close file. errno = " << error);
175 // zlib_filefunc64_def interface: files
176 static voidpf ZCALLBACK open64_file(voidpf opaque,
177 const void* /*filename*/,
180 Device *device = static_cast<Device *>(opaque);
182 // Open file for master device
183 return new File(device);
186 static uLong ZCALLBACK read_file(voidpf opaque,
191 Device *device = static_cast<Device *>(opaque);
192 File *deviceFile = static_cast<File *>(pstream);
194 // Check if offset is out of bounds
195 if (deviceFile->offset >= device->m_size) {
196 LogPedantic("Device: read offset out of bounds");
200 off64_t bytesLeft = device->m_size -
205 // Calculate bytes to read
206 if (static_cast<off64_t>(size) > bytesLeft) {
207 bytesToRead = bytesLeft;
209 bytesToRead = static_cast<off64_t>(size);
214 device->m_address + deviceFile->offset,
215 static_cast<size_t>(bytesToRead));
217 // Increment file offset
218 deviceFile->offset += bytesToRead;
220 // Return bytes that were actually read
221 return static_cast<uLong>(bytesToRead);
224 static uLong ZCALLBACK write_file(voidpf /*opaque*/,
229 // Not supported by device
230 LogPedantic("Unsupported function called!");
234 static int ZCALLBACK close_file(voidpf /*opaque*/, voidpf stream)
236 File *deviceFile = static_cast<File *>(stream);
245 static int ZCALLBACK testerror_file(voidpf /*opaque*/, voidpf /*stream*/)
251 static ZPOS64_T ZCALLBACK tell64_file(voidpf /*opaque*/, voidpf stream)
253 File *deviceFile = static_cast<File *>(stream);
255 return static_cast<ZPOS64_T>(deviceFile->offset);
258 static long ZCALLBACK seek64_file(voidpf opaque,
263 Device *device = static_cast<Device *>(opaque);
264 File *deviceFile = static_cast<File *>(stream);
267 case ZLIB_FILEFUNC_SEEK_SET:
268 deviceFile->offset = static_cast<off64_t>(offset);
272 case ZLIB_FILEFUNC_SEEK_CUR:
273 deviceFile->offset += static_cast<off64_t>(offset);
277 case ZLIB_FILEFUNC_SEEK_END:
280 static_cast<off64_t>(offset);
292 ZipInput::ZipInput(const std::string &fileName) :
297 m_totalUncompressedSize(0)
299 LogPedantic("Zip input file: " << fileName);
301 // Create master device
302 LogPedantic("Creating master device");
303 std::unique_ptr<Device> device(new Device(fileName));
306 zlib_filefunc64_def interface;
307 interface.zopen64_file = &Device::open64_file;
308 interface.zread_file = &Device::read_file;
309 interface.zwrite_file = &Device::write_file;
310 interface.ztell64_file = &Device::tell64_file;
311 interface.zseek64_file = &Device::seek64_file;
312 interface.zclose_file = &Device::close_file;
313 interface.zerror_file = &Device::testerror_file;
314 interface.opaque = device.get();
316 LogPedantic("Opening zip file");
317 unzFile file = unzOpen2_64(NULL, &interface);
320 LogPedantic("Failed to open zip file");
322 // Some errror occured
323 ThrowMsg(Exception::OpenFailed,
324 "Failed to open zip file: " << fileName);
328 ScopedUnzClose scopedUnzClose(file);
331 ReadGlobalInfo(file);
332 ReadGlobalComment(file);
335 // Release scoped unz close
336 m_masterFile = scopedUnzClose.Release();
337 m_device = device.release();
339 LogPedantic("Zip file opened");
342 ZipInput::~ZipInput()
345 if (unzClose(static_cast<unzFile>(m_masterFile)) != UNZ_OK) {
346 LogPedantic("Failed to close zip input file");
353 void ZipInput::ReadGlobalInfo(void *masterFile)
355 // Read number of entries and global comment
356 unz_global_info globalInfo;
358 if (unzGetGlobalInfo(static_cast<unzFile>(masterFile),
359 &globalInfo) != UNZ_OK)
361 LogPedantic("Failed to read zip global info");
363 ThrowMsg(Exception::ReadGlobalInfoFailed,
364 "Failed to read global info");
367 m_numberOfFiles = static_cast<size_t>(globalInfo.number_entry);
368 m_globalCommentSize = static_cast<size_t>(globalInfo.size_comment);
370 LogPedantic("Number of files: " << m_numberOfFiles);
371 LogPedantic("Global comment size: " << m_globalCommentSize);
374 void ZipInput::ReadGlobalComment(void *masterFile)
376 ScopedArray<char> comment(new char[m_globalCommentSize + 1]);
378 if (unzGetGlobalComment(static_cast<unzFile>(masterFile),
380 m_globalCommentSize + 1) != UNZ_OK)
382 LogPedantic("Failed to read zip global comment");
384 ThrowMsg(Exception::ReadGlobalCommentFailed,
385 "Failed to read global comment");
388 m_globalComment = comment.Get();
389 LogPedantic("Global comment: " << m_globalComment);
392 void ZipInput::ReadInfos(void *masterFile)
395 m_fileInfos.reserve(m_numberOfFiles);
397 if (unzGoToFirstFile(static_cast<unzFile>(masterFile)) != UNZ_OK) {
398 LogPedantic("Failed to go to first file");
399 ThrowMsg(Exception::SeekFileFailed, "Failed to seek first file");
402 for (size_t i = 0; i < m_numberOfFiles; ++i) {
403 unz_file_pos_s filePos;
405 if (unzGetFilePos(static_cast<unzFile>(masterFile),
408 LogPedantic("Failed to get file pos");
409 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
412 unz_file_info64 fileInfo;
414 if (unzGetCurrentFileInfo64(static_cast<unzFile>(masterFile),
423 LogPedantic("Failed to get file pos");
424 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
427 ScopedArray<char> fileName(new char[fileInfo.size_filename + 1]);
428 ScopedArray<char> fileComment(new char[fileInfo.size_file_comment + 1]);
430 if (unzGetCurrentFileInfo64(static_cast<unzFile>(masterFile),
433 fileInfo.size_filename + 1,
437 fileInfo.size_file_comment + 1) != UNZ_OK)
439 LogPedantic("Failed to get file pos");
440 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
443 m_fileInfos.push_back(
446 static_cast<size_t>(filePos.pos_in_zip_directory),
447 static_cast<size_t>(filePos.num_of_file)
449 std::string(fileName.Get()),
450 std::string(fileComment.Get()),
451 static_cast<off64_t>(fileInfo.compressed_size),
452 static_cast<off64_t>(fileInfo.uncompressed_size)
455 m_totalUncompressedSize += static_cast<size_t>(fileInfo.uncompressed_size);
457 // If this is not the last file, go to next one
458 if (i != m_numberOfFiles - 1) {
460 static_cast<unzFile>(masterFile)) != UNZ_OK)
462 LogPedantic("Failed to go to next file");
464 ThrowMsg(Exception::FileInfoFailed,
465 "Failed to go to next file");
471 ZipInput::const_iterator ZipInput::begin() const
473 return m_fileInfos.begin();
476 ZipInput::const_iterator ZipInput::end() const
478 return m_fileInfos.end();
481 ZipInput::const_reverse_iterator ZipInput::rbegin() const
483 return m_fileInfos.rbegin();
486 ZipInput::const_reverse_iterator ZipInput::rend() const
488 return m_fileInfos.rend();
491 ZipInput::size_type ZipInput::size() const
493 return m_fileInfos.size();
496 ZipInput::File *ZipInput::OpenFile(const std::string &fileName)
498 FOREACH(iterator, m_fileInfos)
500 if (iterator->name == fileName) {
501 return new File(m_device, iterator->handle);
505 ThrowMsg(Exception::OpenFileFailed,
506 "Failed to open zip file: " << fileName);
509 ZipInput::File::File(class Device *device, FileHandle handle)
512 zlib_filefunc64_def interface;
513 interface.zopen64_file = &Device::open64_file;
514 interface.zread_file = &Device::read_file;
515 interface.zwrite_file = &Device::write_file;
516 interface.ztell64_file = &Device::tell64_file;
517 interface.zseek64_file = &Device::seek64_file;
518 interface.zclose_file = &Device::close_file;
519 interface.zerror_file = &Device::testerror_file;
520 interface.opaque = device;
522 LogPedantic("Opening zip file");
523 unzFile file = unzOpen2_64(NULL, &interface);
526 LogPedantic("Failed to open zip file");
528 // Some errror occured
529 ThrowMsg(ZipInput::Exception::OpenFileFailed,
530 "Failed to open zip file");
534 ScopedUnzClose scopedUnzClose(file);
536 // Look up file handle
537 unz64_file_pos filePos = {
538 static_cast<ZPOS64_T>(handle.first),
539 static_cast<ZPOS64_T>(handle.second)
542 if (unzGoToFilePos64(file, &filePos) != UNZ_OK) {
543 LogPedantic("Failed to seek to zip file");
545 // Some errror occured
546 ThrowMsg(ZipInput::Exception::OpenFileFailed,
547 "Failed to seek into zip file");
550 // Open current file for reading
551 if (unzOpenCurrentFile(file) != UNZ_OK) {
552 LogPedantic("Failed to open current zip file");
554 // Some errror occured
555 ThrowMsg(ZipInput::Exception::OpenFileFailed,
556 "Failed to open current zip file");
559 // Release scoped unz close
560 m_file = scopedUnzClose.Release();
562 LogPedantic("Zip file opened");
565 ZipInput::File::~File()
567 // Close current file for reading
568 if (unzCloseCurrentFile(static_cast<unzFile>(m_file)) != UNZ_OK) {
569 LogPedantic("Failed to close current zip input file");
573 if (unzClose(static_cast<unzFile>(m_file)) != UNZ_OK) {
574 LogPedantic("Failed to close zip input file");
578 DPL::BinaryQueueAutoPtr ZipInput::File::Read(size_t size)
580 // Do not even try to unzip if requested zero bytes
582 return DPL::BinaryQueueAutoPtr(new DPL::BinaryQueue());
586 size_t sizeToRead = size > EXTRACT_BUFFER_SIZE ?
587 EXTRACT_BUFFER_SIZE :
590 // Extract zip file data (one-copy)
591 ScopedFree<void> rawBuffer(malloc(sizeToRead));
594 throw std::bad_alloc();
598 int bytes = unzReadCurrentFile(static_cast<unzFile>(m_file),
602 // Internal unzipper error
604 LogPedantic("Extract failed. Error: " << bytes);
606 ThrowMsg(ZipInput::Exception::ReadFileFailed,
607 "Failed to extract file with error: " << bytes);
610 // Data was read (may be zero bytes)
611 DPL::BinaryQueueAutoPtr buffer(new DPL::BinaryQueue());
613 buffer->AppendUnmanaged(rawBuffer.Get(),
614 static_cast<size_t>(bytes),
615 &DPL::BinaryQueue::BufferDeleterFree,
623 const std::string &ZipInput::GetGlobalComment() const
625 return m_globalComment;
628 bool ZipInput::empty() const
630 return m_fileInfos.empty();
633 size_t ZipInput::GetTotalUncompressedSize() const
635 return m_totalUncompressedSize;