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
22 #include <dpl/zip_input.h>
23 #include <dpl/file_input_mapping.h>
24 #include <dpl/binary_queue.h>
25 #include <dpl/scoped_free.h>
26 #include <dpl/scoped_ptr.h>
27 #include <dpl/scoped_array.h>
28 #include <dpl/foreach.h>
29 #include <dpl/log/log.h>
30 #include <minizip/framework_minizip.h>
35 namespace // anonymous
37 const size_t EXTRACT_BUFFER_SIZE = 4096;
45 ScopedUnzClose(unzFile file)
55 if (unzClose(m_file) != UNZ_OK)
56 LogPedantic("Failed to close zip input file");
61 unzFile file = m_file;
68 } // namespace anonymous
71 * Seekable multiplexing device
74 * Minizip library lacks serious support for multithreaded
75 * access to zip files. Thus, they cannot be easily extracted
76 * simulateously. Here is introduced seekable device which does
77 * have a context with seek index for each file. File is mapped to
78 * memory and because of that no real synchronization is needed.
79 * Memory addresses can be indexed.
81 * About generalization:
82 * To achieve the same results on abstract input device, there must be
83 * provided a mechanism to read data from random address without synchronization.
84 * In other words: stateless. As described above, stateless property can be
85 * achieved via memory mapping.
90 DPL::ScopedPtr<FileInputMapping> m_fileMapping;
105 Device(const std::string &fileName)
109 LogPedantic("Creating file mapping");
110 m_fileMapping.Reset(new FileInputMapping(fileName));
112 Catch (FileInputMapping::Exception::Base)
114 LogPedantic("Failed to create file mapping");
116 ReThrowMsg(ZipInput::Exception::OpenFailed,
117 "Failed to open zip file mapping");
120 LogPedantic("File mapping created");
123 // zlib_filefunc64_def interface: files
124 static voidpf ZCALLBACK open64_file(voidpf opaque,
125 const void* filename,
131 Device *device = static_cast<Device *>(opaque);
133 // Open file for master device
134 return new File(device);
137 static uLong ZCALLBACK read_file(voidpf opaque,
142 Device *device = static_cast<Device *>(opaque);
143 File *deviceFile = static_cast<File *>(pstream);
145 // Check if offset is out of bounds
146 if (deviceFile->offset >= device->m_fileMapping->GetSize())
148 LogPedantic("Device: read offset out of bounds");
152 off64_t bytesLeft = device->m_fileMapping->GetSize() -
157 // Calculate bytes to read
158 if (static_cast<off64_t>(size) > bytesLeft)
159 bytesToRead = bytesLeft;
161 bytesToRead = static_cast<off64_t>(size);
165 device->m_fileMapping->GetAddress() + deviceFile->offset,
166 static_cast<size_t>(bytesToRead));
168 // Increment file offset
169 deviceFile->offset += bytesToRead;
171 // Return bytes that were actually read
172 return static_cast<uLong>(bytesToRead);
175 static uLong ZCALLBACK write_file(voidpf opaque,
185 // Not supported by device
186 LogPedantic("Unsupported function called!");
190 static int ZCALLBACK close_file(voidpf opaque,
194 File *deviceFile = static_cast<File *>(stream);
203 static int ZCALLBACK testerror_file(voidpf opaque,
213 static ZPOS64_T ZCALLBACK tell64_file(voidpf opaque,
217 File *deviceFile = static_cast<File *>(stream);
219 return static_cast<ZPOS64_T>(deviceFile->offset);
222 static long ZCALLBACK seek64_file(voidpf opaque,
227 Device *device = static_cast<Device *>(opaque);
228 File *deviceFile = static_cast<File *>(stream);
232 case ZLIB_FILEFUNC_SEEK_SET:
233 deviceFile->offset = static_cast<off64_t>(offset);
237 case ZLIB_FILEFUNC_SEEK_CUR:
238 deviceFile->offset += static_cast<off64_t>(offset);
242 case ZLIB_FILEFUNC_SEEK_END:
244 device->m_fileMapping->GetSize() -
245 static_cast<off64_t>(offset);
257 ZipInput::ZipInput(const std::string &fileName)
263 LogPedantic("Zip input file: " << fileName);
265 // Create master device
266 LogPedantic("Creating master device");
267 ScopedPtr<Device> device(new Device(fileName));
270 zlib_filefunc64_def interface;
271 interface.zopen64_file = &Device::open64_file;
272 interface.zread_file = &Device::read_file;
273 interface.zwrite_file = &Device::write_file;
274 interface.ztell64_file = &Device::tell64_file;
275 interface.zseek64_file = &Device::seek64_file;
276 interface.zclose_file = &Device::close_file;
277 interface.zerror_file = &Device::testerror_file;
278 interface.opaque = device.Get();
280 LogPedantic("Opening zip file");
281 unzFile file = unzOpen2_64(NULL, &interface);
285 LogPedantic("Failed to open zip file");
287 // Some errror occured
288 ThrowMsg(Exception::OpenFailed,
289 "Failed to open zip file: " << fileName);
293 ScopedUnzClose scopedUnzClose(file);
296 ReadGlobalInfo(file);
297 ReadGlobalComment(file);
300 // Release scoped unz close
301 m_masterFile = scopedUnzClose.Release();
302 m_device = device.Release();
304 LogPedantic("Zip file opened");
307 ZipInput::~ZipInput()
310 if (unzClose(static_cast<unzFile>(m_masterFile)) != UNZ_OK)
311 LogPedantic("Failed to close zip input file");
317 void ZipInput::ReadGlobalInfo(void *masterFile)
319 // Read number of entries and global comment
320 unz_global_info globalInfo;
322 if (unzGetGlobalInfo(static_cast<unzFile>(masterFile),
323 &globalInfo) != UNZ_OK)
325 LogPedantic("Failed to read zip global info");
327 ThrowMsg(Exception::ReadGlobalInfoFailed,
328 "Failed to read global info");
331 m_numberOfFiles = static_cast<size_t>(globalInfo.number_entry);
332 m_globalCommentSize = static_cast<size_t>(globalInfo.size_comment);
334 LogPedantic("Number of files: " << m_numberOfFiles);
335 LogPedantic("Global comment size: " << m_globalCommentSize);
338 void ZipInput::ReadGlobalComment(void *masterFile)
340 ScopedArray<char> comment(new char[m_globalCommentSize + 1]);
342 if (unzGetGlobalComment(static_cast<unzFile>(masterFile),
344 m_globalCommentSize + 1) != UNZ_OK)
346 LogPedantic("Failed to read zip global comment");
348 ThrowMsg(Exception::ReadGlobalCommentFailed,
349 "Failed to read global comment");
352 m_globalComment = comment.Get();
353 LogPedantic("Global comment: " << m_globalComment);
356 void ZipInput::ReadInfos(void *masterFile)
359 m_fileInfos.reserve(m_numberOfFiles);
361 if (unzGoToFirstFile(static_cast<unzFile>(masterFile)) != UNZ_OK)
363 LogPedantic("Failed to go to first file");
364 ThrowMsg(Exception::SeekFileFailed, "Failed to seek first file");
367 for (size_t i = 0; i < m_numberOfFiles; ++i)
369 unz_file_pos_s filePos;
371 if (unzGetFilePos(static_cast<unzFile>(masterFile),
374 LogPedantic("Failed to get file pos");
375 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
378 unz_file_info64 fileInfo;
380 if (unzGetCurrentFileInfo64(static_cast<unzFile>(masterFile),
389 LogPedantic("Failed to get file pos");
390 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
393 ScopedArray<char> fileName(new char[fileInfo.size_filename + 1]);
394 ScopedArray<char> fileComment(new char[fileInfo.size_file_comment + 1]);
396 if (unzGetCurrentFileInfo64(static_cast<unzFile>(masterFile),
399 fileInfo.size_filename + 1,
403 fileInfo.size_file_comment + 1) != UNZ_OK)
405 LogPedantic("Failed to get file pos");
406 ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info");
409 m_fileInfos.push_back(
412 static_cast<size_t>(filePos.pos_in_zip_directory),
413 static_cast<size_t>(filePos.num_of_file)
415 std::string(fileName.Get()),
416 std::string(fileComment.Get()),
417 static_cast<unsigned long>(fileInfo.version),
418 static_cast<unsigned long>(fileInfo.version_needed),
419 static_cast<unsigned long>(fileInfo.flag),
420 static_cast<unsigned long>(fileInfo.compression_method),
421 static_cast<unsigned long>(fileInfo.dosDate),
422 static_cast<unsigned long>(fileInfo.crc),
423 static_cast<off64_t>(fileInfo.compressed_size),
424 static_cast<off64_t>(fileInfo.uncompressed_size),
425 static_cast<unsigned long>(fileInfo.disk_num_start),
426 static_cast<unsigned long>(fileInfo.internal_fa),
427 static_cast<unsigned long>(fileInfo.external_fa),
429 fileInfo.tmu_date.tm_sec,
430 fileInfo.tmu_date.tm_min,
431 fileInfo.tmu_date.tm_hour,
432 fileInfo.tmu_date.tm_mday,
433 fileInfo.tmu_date.tm_mon,
434 fileInfo.tmu_date.tm_year
439 // If this is not the last file, go to next one
440 if (i != m_numberOfFiles - 1)
443 static_cast<unzFile>(masterFile))!= UNZ_OK)
445 LogPedantic("Failed to go to next file");
447 ThrowMsg(Exception::FileInfoFailed,
448 "Failed to go to next file");
454 ZipInput::const_iterator ZipInput::begin() const
456 return m_fileInfos.begin();
459 ZipInput::const_iterator ZipInput::end() const
461 return m_fileInfos.end();
464 ZipInput::const_reverse_iterator ZipInput::rbegin() const
466 return m_fileInfos.rbegin();
469 ZipInput::const_reverse_iterator ZipInput::rend() const
471 return m_fileInfos.rend();
474 ZipInput::size_type ZipInput::size() const
476 return m_fileInfos.size();
479 ZipInput::File *ZipInput::OpenFile(FileHandle handle)
481 return new File(m_device, handle);
484 ZipInput::File *ZipInput::OpenFile(const std::string &fileName)
486 FOREACH(iterator, m_fileInfos)
488 if (iterator->name == fileName)
490 return new File(m_device, iterator->handle);
494 ThrowMsg(Exception::OpenFileFailed,
495 "Failed to open zip file: " << fileName);
498 ZipInput::File::File(class Device *device, FileHandle handle)
501 zlib_filefunc64_def interface;
502 interface.zopen64_file = &Device::open64_file;
503 interface.zread_file = &Device::read_file;
504 interface.zwrite_file = &Device::write_file;
505 interface.ztell64_file = &Device::tell64_file;
506 interface.zseek64_file = &Device::seek64_file;
507 interface.zclose_file = &Device::close_file;
508 interface.zerror_file = &Device::testerror_file;
509 interface.opaque = device;
511 LogPedantic("Opening zip file");
512 unzFile file = unzOpen2_64(NULL, &interface);
516 LogPedantic("Failed to open zip file");
518 // Some errror occured
519 ThrowMsg(ZipInput::Exception::OpenFileFailed,
520 "Failed to open zip file");
524 ScopedUnzClose scopedUnzClose(file);
526 // Look up file handle
527 unz64_file_pos filePos =
529 static_cast<ZPOS64_T>(handle.first),
530 static_cast<ZPOS64_T>(handle.second)
533 if (unzGoToFilePos64(file, &filePos) != UNZ_OK)
535 LogPedantic("Failed to seek to zip file");
537 // Some errror occured
538 ThrowMsg(ZipInput::Exception::OpenFileFailed,
539 "Failed to open zip file");
542 // Open current file for reading
543 if (unzOpenCurrentFile(file) != UNZ_OK)
545 LogPedantic("Failed to open current zip file");
547 // Some errror occured
548 ThrowMsg(ZipInput::Exception::OpenFileFailed,
549 "Failed to open current zip file");
552 // Release scoped unz close
553 m_file = scopedUnzClose.Release();
555 LogPedantic("Zip file opened");
558 ZipInput::File::~File()
560 // Close current file for reading
561 if (unzCloseCurrentFile(static_cast<unzFile>(m_file)) != UNZ_OK)
562 LogPedantic("Failed to close current zip input file");
565 if (unzClose(static_cast<unzFile>(m_file)) != UNZ_OK)
566 LogPedantic("Failed to close zip input file");
569 DPL::BinaryQueueAutoPtr ZipInput::File::Read(size_t size)
571 // Do not even try to unzip if requested zero bytes
573 return DPL::BinaryQueueAutoPtr(new DPL::BinaryQueue());
576 size_t sizeToRead = size > EXTRACT_BUFFER_SIZE ?
577 EXTRACT_BUFFER_SIZE :
580 // Extract zip file data (one-copy)
581 ScopedFree<void> rawBuffer(malloc(sizeToRead));
584 throw std::bad_alloc();
587 int bytes = unzReadCurrentFile(static_cast<unzFile>(m_file),
591 // Internal unzipper error
594 LogPedantic("Extract failed. Error: " << bytes);
596 ThrowMsg(ZipInput::Exception::ReadFileFailed,
597 "Failed to extract file with error: " << bytes);
600 // Data was read (may be zero bytes)
601 DPL::BinaryQueueAutoPtr buffer(new DPL::BinaryQueue());
603 buffer->AppendUnmanaged(rawBuffer.Get(),
604 static_cast<size_t>(bytes),
605 &DPL::BinaryQueue::BufferDeleterFree,
613 const std::string &ZipInput::GetGlobalComment() const
615 return m_globalComment;
618 bool ZipInput::empty() const
620 return m_fileInfos.empty();