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/free_deleter.h>
33 #include <dpl/foreach.h>
34 #include <dpl/log/wrt_log.h>
36 #include <minizip/unzip.h>
39 namespace // anonymous
41 const size_t EXTRACT_BUFFER_SIZE = 4096;
49 ScopedUnzClose(unzFile file) :
59 if (unzClose(m_file) != UNZ_OK) {
60 WrtLogD("Failed to close zip input file");
66 unzFile file = m_file;
73 } // namespace anonymous
76 * Seekable multiplexing device
79 * Minizip library lacks serious support for multithreaded
80 * access to zip files. Thus, they cannot be easily extracted
81 * simulateously. Here is introduced seekable device which does
82 * have a context with seek index for each file. File is mapped to
83 * memory and because of that no real synchronization is needed.
84 * Memory addresses can be indexed.
86 * About generalization:
87 * To achieve the same results on abstract input device, there must be
88 * provided a mechanism to read data from random address without
90 * In other words: stateless. As described above, stateless property can be
91 * achieved via memory mapping.
97 off64_t m_size; // file mapping size
98 unsigned char *m_address; // mapping base address
113 Device (const Device &) = delete;
114 Device & operator= (const Device &) = delete;
116 Device(const std::string &fileName) :
121 WrtLogD("Creating file mapping");
122 // Open device and map it to user space
123 int file = TEMP_FAILURE_RETRY(open(fileName.c_str(), O_RDONLY));
127 ThrowMsg(ZipInput::Exception::OpenFailed,
128 "Failed to open file. errno = " << error);
131 // Scoped close on file
132 ScopedClose scopedClose(file);
134 // Calculate file size
135 off64_t size = lseek64(file, 0, SEEK_END);
137 if (size == static_cast<off64_t>(-1)) {
139 ThrowMsg(ZipInput::Exception::OpenFailed,
140 "Failed to seek file. errno = " << error);
143 // Map file to usespace
144 void *address = mmap(0, static_cast<size_t>(size),
145 PROT_READ, MAP_SHARED, file, 0);
147 if (address == MAP_FAILED) {
149 ThrowMsg(ZipInput::Exception::OpenFailed,
150 "Failed to map file. errno = " << error);
153 // Release scoped close
154 m_handle = scopedClose.Release();
156 // Save mapped up address
158 m_address = static_cast<unsigned char *>(address);
160 WrtLogD("Created file mapping: %s of size: %lli at address: %p",
161 fileName.c_str(), m_size, m_address);
167 if (munmap(m_address, static_cast<size_t>(m_size)) == -1) {
169 WrtLogD("Failed to munmap file. errno = %i", error);
172 // Close file descriptor
173 if (close(m_handle) == -1) {
175 WrtLogD("Failed to close file. errno = %i", error);
180 // zlib_filefunc64_def interface: files
181 static voidpf ZCALLBACK open64_file(voidpf opaque,
182 const void* /*filename*/,
185 Device *device = static_cast<Device *>(opaque);
187 // Open file for master device
188 return new File(device);
191 static uLong ZCALLBACK read_file(voidpf opaque,
197 WrtLogE("target buffer is null");
201 Device *device = static_cast<Device *>(opaque);
202 File *deviceFile = static_cast<File *>(pstream);
204 if(device->m_address == NULL) {
205 WrtLogE("Device: already closed");
209 // Check if offset is out of bounds
210 if (deviceFile->offset >= device->m_size) {
211 WrtLogD("Device: read offset out of bounds");
215 off64_t bytesLeft = device->m_size -
220 // Calculate bytes to read
221 if (static_cast<off64_t>(size) > bytesLeft) {
222 bytesToRead = bytesLeft;
224 bytesToRead = static_cast<off64_t>(size);
227 if(device->m_size < deviceFile->offset + bytesToRead){
228 WrtLogE("Device : end offset out of bounds");
233 device->m_address + deviceFile->offset,
234 static_cast<size_t>(bytesToRead));
236 // Increment file offset
237 deviceFile->offset += bytesToRead;
239 // Return bytes that were actually read
240 return static_cast<uLong>(bytesToRead);
243 static uLong ZCALLBACK write_file(voidpf /*opaque*/,
248 // Not supported by device
249 WrtLogD("Unsupported function called!");
253 static int ZCALLBACK close_file(voidpf /*opaque*/, voidpf stream)
255 File *deviceFile = static_cast<File *>(stream);
264 static int ZCALLBACK testerror_file(voidpf /*opaque*/, voidpf /*stream*/)
270 static ZPOS64_T ZCALLBACK tell64_file(voidpf /*opaque*/, voidpf stream)
272 File *deviceFile = static_cast<File *>(stream);
274 return static_cast<ZPOS64_T>(deviceFile->offset);
277 static long ZCALLBACK seek64_file(voidpf opaque,
282 Device *device = static_cast<Device *>(opaque);
283 File *deviceFile = static_cast<File *>(stream);
286 case ZLIB_FILEFUNC_SEEK_SET:
287 deviceFile->offset = static_cast<off64_t>(offset);
291 case ZLIB_FILEFUNC_SEEK_CUR:
292 deviceFile->offset += static_cast<off64_t>(offset);
296 case ZLIB_FILEFUNC_SEEK_END:
299 static_cast<off64_t>(offset);
311 ZipInput::ZipInput(const std::string &fileName) :
315 m_totalUncompressedSize(0),
318 WrtLogD("Zip input file: %s", fileName.c_str());
320 // Create master device
321 WrtLogD("Creating master device");
322 std::unique_ptr<Device> device(new Device(fileName));
325 zlib_filefunc64_def interface;
326 interface.zopen64_file = &Device::open64_file;
327 interface.zread_file = &Device::read_file;
328 interface.zwrite_file = &Device::write_file;
329 interface.ztell64_file = &Device::tell64_file;
330 interface.zseek64_file = &Device::seek64_file;
331 interface.zclose_file = &Device::close_file;
332 interface.zerror_file = &Device::testerror_file;
333 interface.opaque = device.get();
335 WrtLogD("Opening zip file");
336 unzFile file = unzOpen2_64(NULL, &interface);
339 WrtLogD("Failed to open zip file");
341 // Some errror occured
342 ThrowMsg(Exception::OpenFailed,
343 "Failed to open zip file: " << fileName);
347 ScopedUnzClose scopedUnzClose(file);
350 ReadGlobalInfo(file);
351 ReadGlobalComment(file);
354 // Release scoped unz close
355 m_masterFile = scopedUnzClose.Release();
356 m_device = device.release();
358 WrtLogD("Zip file opened");
361 ZipInput::~ZipInput()
364 if (unzClose(static_cast<unzFile>(m_masterFile)) != UNZ_OK) {
365 WrtLogD("Failed to close zip input file");
372 void ZipInput::ReadGlobalInfo(void *masterFile)
374 // Read number of entries and global comment
375 unz_global_info globalInfo;
377 if (unzGetGlobalInfo(static_cast<unzFile>(masterFile),
378 &globalInfo) != UNZ_OK)
380 WrtLogD("Failed to read zip global info");
382 ThrowMsg(Exception::ReadGlobalInfoFailed,
383 "Failed to read global info");
386 m_numberOfFiles = static_cast<size_t>(globalInfo.number_entry);
387 m_globalCommentSize = static_cast<size_t>(globalInfo.size_comment);
389 WrtLogD("Number of files: %u", m_numberOfFiles);
390 WrtLogD("Global comment size: %u", m_globalCommentSize);
393 void ZipInput::ReadGlobalComment(void *masterFile)
395 std::unique_ptr<char[]> comment(new char[m_globalCommentSize + 1]);
397 if (unzGetGlobalComment(static_cast<unzFile>(masterFile),
399 m_globalCommentSize + 1) != UNZ_OK)
401 WrtLogD("Failed to read zip global comment");
403 ThrowMsg(Exception::ReadGlobalCommentFailed,
404 "Failed to read global comment");
407 m_globalComment = comment.get();
408 WrtLogD("Global comment: %s", m_globalComment.c_str());
411 void ZipInput::ReadInfos(void *masterFile)
414 m_fileInfos.reserve(m_numberOfFiles);
416 if (unzGoToFirstFile(static_cast<unzFile>(masterFile)) != UNZ_OK) {
417 WrtLogD("Failed to go to first file");
418 ThrowMsg(Exception::SeekFileFailed, "Failed to seek first file");
421 for (size_t i = 0; i < m_numberOfFiles; ++i) {
422 unz_file_pos_s filePos;
424 if (unzGetFilePos(static_cast<unzFile>(masterFile),
427 WrtLogD("Failed to get file pos");
428 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
431 unz_file_info64 fileInfo;
433 if (unzGetCurrentFileInfo64(static_cast<unzFile>(masterFile),
442 WrtLogD("Failed to get file pos");
443 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
446 std::unique_ptr<char[]> fileName(new char[fileInfo.size_filename + 1]);
447 std::unique_ptr<char[]> fileComment(new char[fileInfo.size_file_comment + 1]);
449 if (unzGetCurrentFileInfo64(static_cast<unzFile>(masterFile),
452 fileInfo.size_filename + 1,
456 fileInfo.size_file_comment + 1) != UNZ_OK)
458 WrtLogD("Failed to get file pos");
459 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
462 m_fileInfos.push_back(
465 static_cast<size_t>(filePos.pos_in_zip_directory),
466 static_cast<size_t>(filePos.num_of_file)
468 std::string(fileName.get()),
469 std::string(fileComment.get()),
470 static_cast<off64_t>(fileInfo.compressed_size),
471 static_cast<off64_t>(fileInfo.uncompressed_size)
474 m_totalUncompressedSize += static_cast<size_t>(fileInfo.uncompressed_size);
476 // If this is not the last file, go to next one
477 if (i != m_numberOfFiles - 1) {
479 static_cast<unzFile>(masterFile)) != UNZ_OK)
481 WrtLogD("Failed to go to next file");
483 ThrowMsg(Exception::FileInfoFailed,
484 "Failed to go to next file");
490 ZipInput::const_iterator ZipInput::begin() const
492 return m_fileInfos.begin();
495 ZipInput::const_iterator ZipInput::end() const
497 return m_fileInfos.end();
500 ZipInput::const_reverse_iterator ZipInput::rbegin() const
502 return m_fileInfos.rbegin();
505 ZipInput::const_reverse_iterator ZipInput::rend() const
507 return m_fileInfos.rend();
510 ZipInput::size_type ZipInput::size() const
512 return m_fileInfos.size();
515 ZipInput::File *ZipInput::OpenFile(const std::string &fileName)
517 FOREACH(iterator, m_fileInfos)
519 if (iterator->name == fileName) {
520 return new File(m_device, iterator->handle);
524 ThrowMsg(Exception::OpenFileFailed,
525 "Failed to open zip file: " << fileName);
528 ZipInput::File::File(class Device *device, FileHandle handle)
531 zlib_filefunc64_def interface;
532 interface.zopen64_file = &Device::open64_file;
533 interface.zread_file = &Device::read_file;
534 interface.zwrite_file = &Device::write_file;
535 interface.ztell64_file = &Device::tell64_file;
536 interface.zseek64_file = &Device::seek64_file;
537 interface.zclose_file = &Device::close_file;
538 interface.zerror_file = &Device::testerror_file;
539 interface.opaque = device;
541 WrtLogD("Opening zip file");
542 unzFile file = unzOpen2_64(NULL, &interface);
545 WrtLogD("Failed to open zip file");
547 // Some errror occured
548 ThrowMsg(ZipInput::Exception::OpenFileFailed,
549 "Failed to open zip file");
553 ScopedUnzClose scopedUnzClose(file);
555 // Look up file handle
556 unz64_file_pos filePos = {
557 static_cast<ZPOS64_T>(handle.first),
558 static_cast<ZPOS64_T>(handle.second)
561 if (unzGoToFilePos64(file, &filePos) != UNZ_OK) {
562 WrtLogD("Failed to seek to zip file");
564 // Some errror occured
565 ThrowMsg(ZipInput::Exception::OpenFileFailed,
566 "Failed to seek into zip file");
569 // Open current file for reading
570 if (unzOpenCurrentFile(file) != UNZ_OK) {
571 WrtLogD("Failed to open current zip file");
573 // Some errror occured
574 ThrowMsg(ZipInput::Exception::OpenFileFailed,
575 "Failed to open current zip file");
578 // Release scoped unz close
579 m_file = scopedUnzClose.Release();
581 WrtLogD("Zip file opened");
584 ZipInput::File::~File()
586 // Close current file for reading
587 if (unzCloseCurrentFile(static_cast<unzFile>(m_file)) != UNZ_OK) {
588 WrtLogD("Failed to close current zip input file");
592 if (unzClose(static_cast<unzFile>(m_file)) != UNZ_OK) {
593 WrtLogD("Failed to close zip input file");
597 DPL::BinaryQueueAutoPtr ZipInput::File::Read(size_t size)
599 // Do not even try to unzip if requested zero bytes
601 return DPL::BinaryQueueAutoPtr(new DPL::BinaryQueue());
605 size_t sizeToRead = size > EXTRACT_BUFFER_SIZE ?
606 EXTRACT_BUFFER_SIZE :
609 // Extract zip file data (one-copy)
610 std::unique_ptr<void,free_deleter> rawBuffer(malloc(sizeToRead));
613 throw std::bad_alloc();
617 int bytes = unzReadCurrentFile(static_cast<unzFile>(m_file),
621 // Internal unzipper error
623 WrtLogD("Extract failed. Error: %i", bytes);
625 ThrowMsg(ZipInput::Exception::ReadFileFailed,
626 "Failed to extract file with error: " << bytes);
629 // Data was read (may be zero bytes)
630 DPL::BinaryQueueAutoPtr buffer(new DPL::BinaryQueue());
632 buffer->AppendUnmanaged(rawBuffer.get(),
633 static_cast<size_t>(bytes),
634 &DPL::BinaryQueue::BufferDeleterFree,
642 const std::string &ZipInput::GetGlobalComment() const
644 return m_globalComment;
647 bool ZipInput::empty() const
649 return m_fileInfos.empty();
652 size_t ZipInput::GetTotalUncompressedSize() const
654 return m_totalUncompressedSize;