/* * Cantata * * Copyright (c) 2011-2017 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "tar.h" #include "qtiocompressor/qtiocompressor.h" Tar::Tar(const QString &fileName) : file(fileName) , compressor(0) , dev(0) { } Tar::~Tar() { delete compressor; } bool Tar::open() { if (!file.open(QIODevice::ReadOnly)) { return false; } // Check for gzip header... QByteArray header=file.read(2); bool isCompressed=((unsigned char)header[0])==0x1f && ((unsigned char)header[1])==0x8b; file.seek(0); if (isCompressed) { compressor=new QtIOCompressor(&file); compressor->setStreamFormat(QtIOCompressor::GzipFormat); if (!compressor->open(QIODevice::ReadOnly)) { return false; } dev=compressor; } else { dev=&file; } return true; } static const qint64 constHeaderLen=512; static qint64 roundUp(qint64 sz) { return ((sz/constHeaderLen)*constHeaderLen)+((sz%constHeaderLen) ? constHeaderLen : 0); } struct TarHeader { TarHeader() : fileSize(0) { } bool ok() const { return fileSize>0 && !fileName.isEmpty(); } QString fileName; qint64 fileSize; }; static unsigned int octStrToInt(char *ch, unsigned int size) { unsigned int val = 0; while (size > 0){ val = (val * 8) + (*ch - '0'); ch++; size--; } return val; } static TarHeader readHeader(QIODevice *dev) { TarHeader header; char buffer[constHeaderLen]; qint64 bytesRead=dev->read(buffer, constHeaderLen); if (constHeaderLen==bytesRead && ('0'==buffer[156] || '\0'==buffer[156])) { buffer[100]='\0'; header.fileName=QFile::decodeName(buffer); header.fileSize=octStrToInt(&buffer[124], 11); } return header; } static QString getExt(const QString &fileName) { int pos=fileName.lastIndexOf("."); return -1!=pos ? fileName.mid(pos) : fileName; } QMap Tar::extract(const QStringList &files) { QMap data; if (!dev) { return data; } qint64 offset=0; qint64 pos=0; for (;;) { TarHeader header=readHeader(dev); if (header.ok()) { pos+=constHeaderLen; if (!data.contains(header.fileName) && (files.contains(header.fileName) || files.contains(getExt(header.fileName)))) { data[header.fileName]=dev->read(header.fileSize); pos+=header.fileSize; } offset+=constHeaderLen+header.fileSize; offset=roundUp(offset); if (dev->isSequential()) { static const qint64 constSkipBlock=1024; // Can't seek with QtIOCompressor - so fake this by reading and discarding while (posread(toRead); pos+=toRead; } } else { dev->seek(offset); } } else { break; } } return data; }