diff options
Diffstat (limited to 'installerbuilder/common/binaryformat.cpp')
-rw-r--r-- | installerbuilder/common/binaryformat.cpp | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/installerbuilder/common/binaryformat.cpp b/installerbuilder/common/binaryformat.cpp new file mode 100644 index 000000000..d418d0f98 --- /dev/null +++ b/installerbuilder/common/binaryformat.cpp @@ -0,0 +1,952 @@ +/************************************************************************** +** +** This file is part of Qt SDK** +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* +** +** Contact: Nokia Corporation qt-info@nokia.com** +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception version +** 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you are unsure which license is appropriate for your use, please contact +** (qt-info@nokia.com). +** +**************************************************************************/ +#include "binaryformat.h" +#include "errors.h" +#include "fileutils.h" +#include "utils.h" +#include "zipjob.h" + +#include "lib7z_facade.h" + +#include <QDir> +#include <QDirIterator> +#include <QFileInfo> +#include <QDebug> +#include <QResource> +#include <QTemporaryFile> +#include <QThreadPool> +#include <QVector> + +#include "common/kd7zenginehandler.h" + +#include <KDUpdater/UpdateOperation> +#include <KDUpdater/UpdateOperationFactory> + +#include <cassert> +#include <cerrno> + +using namespace QInstaller; +using namespace QInstallerCreator; + +/* +TRANSLATOR QInstallerCreator::Archive +*/ + +/* +TRANSLATOR QInstallerCreator::Component +*/ + +static inline QByteArray &theBuffer(int size) +{ + static QByteArray b; + if (size > b.size()) + b.resize( size ); + return b; +} + +void QInstaller::appendFileData(QIODevice *out, QIODevice *in) +{ + assert( !in->isSequential() ); + const qint64 size = in->size(); + blockingCopy( in, out, size ); +} + + +void QInstaller::retrieveFileData(QIODevice *out, QIODevice *in) +{ + qint64 size = QInstaller::retrieveInt64(in); + appendData( in, out, size ); +/* QByteArray &b = theBuffer(size); + blockingRead(in, b.data(), size); + blockingWrite(out, b.constData(), size);*/ +} + +void QInstaller::appendInt64(QIODevice *out, qint64 n) +{ + blockingWrite(out, reinterpret_cast<const char*>( &n ), sizeof(n)); +} + +void QInstaller::appendInt64Range( QIODevice* out, const Range<qint64>& r ) { + appendInt64( out, r.start() ); + appendInt64( out, r.length() ); +} + +qint64 QInstaller::retrieveInt64(QIODevice *in) +{ + qint64 n = 0; + blockingRead( in, reinterpret_cast<char*>( &n ), sizeof(n) ); + return n; +} + +Range< qint64 > QInstaller::retrieveInt64Range( QIODevice* in ) +{ + const quint64 start = retrieveInt64( in ); + const quint64 length = retrieveInt64( in ); + return Range< qint64 >::fromStartAndLength( start, length ); +} + + + +#if 0 +// Faster or not? +static void appendFileData(QIODevice *out, const QString &fileName) +{ + QFile file(fileName); + openForRead(file); + qint64 size = file.size(); + QInstaller::appendInt64(out, size); + if (size == 0) + return; + uchar *data = file.map(0, size); + if (!data) + throw Error(QInstaller::tr("Cannot map file %1").arg(file.fileName())); + blockingWrite(out, (const char *)data, size); + if (!file.unmap(data)) + throw Error(QInstaller::tr("Cannot unmap file %1").arg(file.fileName())); +} +#endif + +void QInstaller::appendData(QIODevice *out, QIODevice *in, qint64 size) +{ + while( size > 0 ) + { + const qint64 nextSize = qMin( size, 16384LL ); + QByteArray &b = theBuffer( nextSize ); + blockingRead( in, b.data(), nextSize ); + blockingWrite( out, b.constData(), nextSize ); + size -= nextSize; + } +} + +void QInstaller::appendString(QIODevice *out, const QString &str) +{ + appendByteArray( out, str.toUtf8() ); +} + +void QInstaller::appendByteArray(QIODevice *out, const QByteArray &ba) +{ + appendInt64(out, ba.size()); + blockingWrite(out, ba.constData(), ba.size()); +} + +void QInstaller::appendStringList(QIODevice *out, const QStringList &list) +{ + appendInt64( out, list.size() ); + Q_FOREACH ( const QString &s, list ) + appendString(out, s); +} + +void QInstaller::appendDictionary(QIODevice *out, const QHash<QString,QString>& dict) +{ + appendInt64(out, dict.size()); + Q_FOREACH (const QString &key, dict.keys()) { + appendString(out, key); + appendString(out, dict.value(key)); + } +} + +QString QInstaller::retrieveString(QIODevice *in) +{ + const QByteArray b = retrieveByteArray( in ); + return QString::fromUtf8( b ); +} + +QByteArray QInstaller::retrieveByteArray(QIODevice *in) +{ + QByteArray ba; + const qint64 n = retrieveInt64( in ); + ba.resize( n ); + blockingRead( in, ba.data(), n ); + return ba; +} + +QStringList QInstaller::retrieveStringList(QIODevice *in) +{ + QStringList list; + for(qint64 i = retrieveInt64(in); --i >= 0; ) + list << retrieveString(in); + return list; +} + +QHash<QString,QString> QInstaller::retrieveDictionary(QIODevice *in) +{ + QHash<QString,QString> dict; + for (qint64 i = retrieveInt64(in); --i >= 0; ) { + QString key = retrieveString(in); + dict.insert(key, retrieveString(in)); + } + return dict; +} + +qint64 QInstaller::findMagicCookie( QFile* in ) { + assert( in ); + assert( in->isOpen() ); + assert( in->isReadable() ); + const qint64 oldPos = in->pos(); + const qint64 MAX_SEARCH = 1024 * 1024; // stop searching after one MB + qint64 searched = 0; + try { + while ( searched < MAX_SEARCH ) { + const qint64 pos = in->size() - searched - sizeof( qint64 ); + if ( pos < 0 ) + throw Error( QObject::tr("Searched whole file, no marker found") ); + if ( !in->seek( pos ) ) + throw Error( QObject::tr("Could not seek to %1 in file %2: %3.").arg( QString::number(pos), in->fileName(), in->errorString() ) ); + const quint64 num = static_cast<quint64>( retrieveInt64( in ) ); + if ( num == MagicCookie ) { + in->seek( oldPos ); + return pos; + } + searched += 1; + } + throw Error( QObject::tr("No marker found, stopped after %1 bytes").arg( QString::number( MAX_SEARCH ) ) ); + } catch ( const Error& err ) { + in->seek( oldPos ); + throw err; + } catch ( ... ) { + in->seek( oldPos ); + throw Error( QObject::tr("No marker found, unknown exception caught.") ); + } + return -1; // never reached +} + +/** + * Creates an archive providing the data in \a path. + * \a path can be a path to a file or to a directory. If it's a file, it's considered to be + * pre-zipped and gets delivered as it is. If it's a directory, it gets zipped by Archive. + */ +Archive::Archive( const QString& path ) + : m_device( 0 ), + m_isTempFile( false ), + m_path( path ), + m_name( QFileInfo( path ).fileName().toUtf8() ) +{ +} + +Archive::Archive( const QByteArray& identifier, const QByteArray& data ) + : m_device( 0 ), + m_isTempFile( true ), + m_path( generateTemporaryFileName() ), + m_name( identifier ) +{ + QFile file( m_path ); + file.open( QIODevice::WriteOnly ); + file.write( data ); +} + +/** + * Creates an archive identified by \a identifier providing a data \a segment within a \a device. + */ +Archive::Archive( const QByteArray& identifier, QIODevice* device, const Range< qint64 >& segment ) + : m_device( device ), + m_segment( segment ), + m_isTempFile( false ), + m_name( identifier ) +{ +} + +Archive::~Archive() +{ + if( isOpen() ) + close(); + if( m_isTempFile ) + QFile::remove( m_path ); +} + +/** + * Copies the archives contents to the path \a name. + * + * If the archive is a zipped directory, \a name is threated as a directory. The archive + * gets extracted there. + * + * If the archive is a plain file and \a name an existing directory, it gets created + * with it's name. Otherwise it gets saved as \a name. + * Note that if a file with the \a name already exists, copy() return false (i.e. Archive will not overwrite it). + */ +bool Archive::copy( const QString& name ) +{ + if( isZippedDirectory() ) + { + const QFileInfo fi( name ); + if( fi.exists() && !fi.isDir() ) + return false; + errno = 0; + if( !fi.exists() && !QDir().mkpath( fi.absoluteFilePath() ) ) { + setErrorString( tr("Could not create %1: %2").arg( name, QString::fromLocal8Bit( strerror( errno ) ) ) ); + return false; + } + + UnzipJob job; + if( isOpen() ) + close(); + open( QIODevice::ReadOnly ); + job.setInputDevice( this ); + job.setOutputPath( fi.absoluteFilePath() ); + + job.run(); + + close(); + + return true; + } + else + { + if( isOpen() ) + close(); + + open( QIODevice::ReadOnly ); + + QFile target( QFileInfo( name ).isDir() ? QString::fromLatin1( "%1/%2" ).arg( name ).arg( QString::fromUtf8( m_name.data(), m_name.count() ) ) : name ); + if( target.exists() ) + return false; + target.open( QIODevice::WriteOnly ); + blockingCopy( this, &target, size() ); + + close(); + return true; + } +} + +/** + * \reimp + */ +bool Archive::seek( qint64 pos ) +{ + if( m_inputFile.isOpen() ) + return m_inputFile.seek( pos ) && QIODevice::seek( pos ); + return QIODevice::seek( pos ); +} + +/** + * Returns true, if this archive was created by zipping a directory. + */ +bool Archive::isZippedDirectory() const +{ + if( m_device == 0 ) + { + // easy, just check wheter it's a dir + return QFileInfo( m_path ).isDir(); + } + else + { + // more complex, check the zip header magic + Archive* const arch = const_cast< Archive* >( this ); + + const bool opened = !isOpen(); + if( opened ) + arch->open( QIODevice::ReadOnly ); + const qint64 p = pos(); + arch->seek( 0 ); + + const QByteArray ba = arch->read( 4 ); + const bool result = ba == QByteArray( "\x50\x4b\x03\04" ); + + arch->seek( p ); + if( opened ) + arch->close(); + return result; + } +} + +QByteArray Archive::name() const +{ + return m_name; +} + +void Archive::setName( const QByteArray& name ) +{ + m_name = name; +} + +/** + * \reimpl + */ +void Archive::close() +{ + m_inputFile.close(); + if( QFileInfo( m_path ).isDir() ) + m_inputFile.remove(); + QIODevice::close(); +} + +/** + * \reimp + */ +bool Archive::open( OpenMode mode ) +{ + if( isOpen() ) + return false; + + const bool writeOnly = ( mode & QIODevice::WriteOnly ) != QIODevice::NotOpen; + const bool append = ( mode & QIODevice::Append ) != QIODevice::NotOpen; + + // no write support + if( writeOnly || append ) + return false; + + if( m_device != 0 ) + return QIODevice::open( mode ); + + // we + + const QFileInfo fi( m_path ); + if( fi.isFile() ) + { + m_inputFile.setFileName( m_path ); + if( !m_inputFile.open( mode ) ) { + setErrorString( tr("Could not open archive file %1 for reading.").arg( m_path ) ); + return false; + } + +/* if ( !Lib7z::isSupportedArchive( &m_inputFile ) ) { + setErrorString( tr("Not in a supported archive format: %1").arg( m_path ) ); + m_inputFile.close(); + return false; + }*/ + + setOpenMode( mode ); + return true; + } + + if( fi.isDir() ) + { + if( m_inputFile.fileName().isEmpty() || !m_inputFile.exists() ) + { + if( !createZippedFile() ) + return false; + } + Q_ASSERT( !m_inputFile.fileName().isEmpty() ); + if( !m_inputFile.open( mode ) ) + return false; + setOpenMode( mode ); + return true; + } + + setErrorString(tr("Could not create archive from %1: Not a file.").arg( m_path ) ); + return false; +} + +bool Archive::createZippedFile() +{ + QTemporaryFile file; + file.setAutoRemove( false ); + if ( !file.open() ) + return false; + m_inputFile.setFileName( file.fileName() ); + file.close(); + m_inputFile.open( QIODevice::ReadWrite ); + try { + Lib7z::createArchive( &m_inputFile, m_path ); + } catch( Lib7z::SevenZipException &e ) { + m_inputFile.close(); + setErrorString( e.message() ); + return false; + } + if( !Lib7z::isSupportedArchive( &m_inputFile ) ) + { + m_inputFile.close(); + setErrorString( tr( "Error while packing directory at %1" ).arg( m_path ) ); + return false; + } + m_inputFile.close(); + return true; +} + +/** + * \reimp + */ +qint64 Archive::size() const +{ + // if we got a device, we just pass the length of the segment + if( m_device != 0 ) + return m_segment.length(); + + const QFileInfo fi( m_path ); + // if we got a regular file, we pass the size of the file + if( fi.isFile() ) + return fi.size(); + else if( fi.isDir() ) + { + if( m_inputFile.fileName().isEmpty() || !m_inputFile.exists() ) + { + if( !const_cast< Archive* >( this )->createZippedFile() ) + throw Error(QObject::tr("Cannot create zipped file for path %1: %2").arg( m_path, errorString() ) ); + } + Q_ASSERT( !m_inputFile.fileName().isEmpty() ); + return m_inputFile.size(); + } + return 0; +} + +/** + * \reimp + */ +qint64 Archive::readData( char* data, qint64 maxSize ) +{ + if( m_device == 0 ) + return m_inputFile.read( data, maxSize ); + + const qint64 p = m_device->pos(); + m_device->seek( m_segment.start() + pos() ); + const qint64 amountRead = m_device->read( data, qMin< quint64 >( maxSize, m_segment.length() - pos() ) ); + m_device->seek( p ); + return amountRead; +} + +/** + * \reimp + */ +qint64 Archive::writeData( const char* data, qint64 maxSize ) +{ + Q_UNUSED( data ); + Q_UNUSED( maxSize ); + // should never be called, as we're read only + return -1; +} + +QByteArray Component::name() const { + return m_name; +} + +void Component::setName( const QByteArray& ba ) { + m_name = ba; +} + +Range<qint64> Component::binarySegment() const { + return m_binarySegment; +} + +void Component::setBinarySegment( const Range<qint64>& r ) { + m_binarySegment = r; +} + +Component Component::readFromIndexEntry( QIODevice* in, qint64 offset ) +{ + Component c; + c.m_name = retrieveByteArray( in ); + c.m_binarySegment = retrieveInt64Range( in ).moved( offset ); + + c.readData( in, offset ); + + return c; +} + +void Component::writeIndexEntry( QIODevice* out, qint64 positionOffset ) const +{ + appendByteArray( out, m_name ); + const Range<qint64> relative = m_binarySegment.moved( positionOffset ); + appendInt64( out, binarySegment().start() ); + appendInt64( out, binarySegment().length() ); +} + +void Component::writeData( QIODevice* out, qint64 offset ) const { + const qint64 dataBegin = out->pos() + offset; + + appendInt64( out, m_archives.count() ); + + qint64 start = out->pos() + offset; + + // add 16 + 16 + number of name characters for each archive (the size of the table) + for( QVector< QSharedPointer< Archive > >::const_iterator it = m_archives.begin(); it != m_archives.end(); ++it ) + start += 3 * sizeof( qint64 ) + (*it)->name().count(); + + QList< qint64 > starts; + + for( QVector< QSharedPointer< Archive > >::const_iterator it = m_archives.begin(); it != m_archives.end(); ++it ) + { + const Archive* const arch = (*it).data(); + appendByteArray( out, arch->name() ); + starts.push_back( start ); + appendInt64Range( out, Range< qint64 >::fromStartAndLength( start, arch->size() ) ); + start += arch->size(); + } + + for( QVector< QSharedPointer< Archive > >::const_iterator it = m_archives.begin(); it != m_archives.end(); ++it ) + { + if( !(*it)->open( QIODevice::ReadOnly ) ) + throw Error( tr("Could not open archive %1: %2").arg( QLatin1String( (*it)->name() ), (*it)->errorString() ) ); + const qint64 expectedStart = starts.takeFirst(); + const qint64 actualStart = out->pos() + offset; + Q_UNUSED( expectedStart ); + Q_UNUSED( actualStart ); + Q_ASSERT( expectedStart == actualStart ); + blockingCopy( it->data(), out, (*it)->size() ); + } + + m_binarySegment = Range<qint64>::fromStartAndEnd( dataBegin, out->pos() + offset ); +} + +void Component::readData( QIODevice* in, qint64 offset ) +{ + const qint64 pos = in->pos(); + + in->seek( m_binarySegment.start() ); + const qint64 count = retrieveInt64( in ); + + QVector< QByteArray > names; + QVector< Range< qint64 > > ranges; + for( int i = 0; i < count; ++i ) + { + names.push_back( retrieveByteArray( in ) ); + ranges.push_back( retrieveInt64Range( in ).moved( offset ) ); + } + + for( int i = 0; i < ranges.count(); ++i ) + m_archives.push_back( QSharedPointer< Archive >( new Archive( names.at( i ), in, ranges.at( i ) ) ) ); + + in->seek( pos ); +} + +QString Component::dataDirectory() const { + return m_dataDirectory; +} + +void Component::setDataDirectory( const QString& path ) { + m_dataDirectory = path; +} + +bool Component::operator<( const Component& other ) const { + if ( m_name != other.name() ) + return m_name < other.m_name; + return m_binarySegment < other.m_binarySegment; +} + +bool Component::operator==( const Component& other ) const { + return m_name == other.m_name && m_binarySegment == other.m_binarySegment; +} + +Component ComponentIndex::componentByName( const QByteArray& id ) const { + return m_components.value( id ); +} + +/** + * Destroys this component. + */ +Component::~Component() +{ +} + +/** + * Appends \a archive to this component. + * The componet takes ownership of \a archive. + */ +void Component::appendArchive( const QSharedPointer<Archive>& archive ) +{ + assert( archive ); + archive->setParent( 0 ); + m_archives.push_back( archive ); +} + +/** + * Returns the archives associated with this component. + */ +QVector< QSharedPointer<Archive> > Component::archives() const +{ + return m_archives; +} + +QSharedPointer<Archive> Component::archiveByName( const QByteArray& name ) const +{ + Q_FOREACH( const QSharedPointer<Archive>& i, m_archives ) + if( i->name() == name ) + return i; + return QSharedPointer<Archive>(); +} + +void ComponentIndex::insertComponent( const Component& c ) +{ + m_components.insert( c.name(), c ); +} + +int ComponentIndex::componentCount() const +{ + return m_components.size(); +} + +void ComponentIndex::removeComponent( const QByteArray& name ) { + m_components.remove( name ); +} + +QVector<Component> ComponentIndex::components() const { + return m_components.values().toVector(); +} + +void ComponentIndex::writeComponentData( QIODevice* out, qint64 offset ) const { + appendInt64( out, componentCount() ); + + for( QHash< QByteArray, Component >::const_iterator it = m_components.begin(); it != m_components.end(); ++it ) + it->writeData( out, offset ); +} + +KD7zEngineHandler* ComponentIndex::zipHandler = 0; + +ComponentIndex::ComponentIndex() +{ + if( zipHandler == 0 ) { + // TODO: this one get leaked + zipHandler = new KD7zEngineHandler; + } +} + +ComponentIndex ComponentIndex::read( QIODevice* dev, qint64 offset ) +{ + ComponentIndex result; + const qint64 size = retrieveInt64( dev ); + for( int i = 0; i < size; ++i ) + result.insertComponent( Component::readFromIndexEntry( dev, offset ) ); + retrieveInt64( dev ); + return result; +} + +void ComponentIndex::writeIndex( QIODevice* out, qint64 offset ) const +{ + // Q: why do we write the size twice? + // A: for us to be able to read it beginning from the end of the file as well + appendInt64( out, componentCount() ); + Q_FOREACH( const Component& i, components() ) + i.writeIndexEntry( out, offset ); + appendInt64( out, componentCount() ); +} + +/*! + \internal + Registers the resource found at \a segment within \a file into the Qt resource system. + */ +static const uchar* addResourceFromBinary( QFile* file, const Range< qint64 >& segment ) +{ + if ( segment.length() <= 0 ) + return 0; + + const uchar* const mapped = file->map( segment.start(), segment.length() ); + if ( !mapped ) + throw Error( QObject::tr("Could not mmap in-binary resource. (offset=%1, length=%2").arg( QString::number( segment.start() ), QString::number( segment.length() ) ) ); + + if ( !QResource::registerResource( mapped, QLatin1String(":/metadata") ) ) + throw Error( QObject::tr("Could not register in-binary resource.") ); + + return mapped; +} + +BinaryContent::BinaryContent( const QString& path ) + : file( new QFile( path ) ), + handler( components ), + magicmaker( 0 ), + dataBlockStart( 0 ) +{ +} + +BinaryContent::~BinaryContent() +{ + for( QVector< const uchar* >::const_iterator it = mappings.begin(); it != mappings.end(); ++it ) + QResource::unregisterResource( *it ); +} + +/*! + \internal + Registers the Qt resources embedded in this binary. + */ +int BinaryContent::registerEmbeddedQResources() +{ + if( !file->isOpen() ) + if( !file->open( QIODevice::ReadOnly )) + throw Error( QObject::tr("Could not open binary %1: %2").arg( file->fileName(), file->errorString() ) ); + Q_FOREACH( const Range<qint64>& i, metadataResourceSegments ) + mappings.push_back( addResourceFromBinary( file.data(), i ) ); + //file->close(); + return mappings.count(); +} + +/*! + \internal + \fn static BinaryContent BinaryContent::readFromApplicationFile() + Reads BinaryContent stored in the current application binary. + */ +BinaryContent BinaryContent::readFromApplicationFile() +{ + return BinaryContent::readFromBinary( QCoreApplication::applicationFilePath() ); +} + +/*! + * \class QInstaller::BinaryContent + * + * BinaryContent handles binary information embedded into executables. + * Qt resources as well as component information can be stored. + * + * Explanation of the binary blob at the end of the file: + * + * \verbatim + * Meta data segment 0 + * Meta data segment ... + * Meta data segment n + * ------------------------------------------------------ + * Component data segment 0 + * Component data segment .. + * Component data segment n + * ------------------------------------------------------ + * Component index segment + * ------------------------------------------------------ + * quint64 offset of component index segment + * quint64 length of component index segment + * ------------------------------------------------------ + * quint64 offset of meta data segment 0 + * quint64 length of meta data segment 0 + * quint64 offset of meta data segment .. + * quint64 length of meta data segment .. + * quint64 offset of meta data segment n + * quint64 length of meta data segment n + * ------------------------------------------------------ + * quint64 number of meta data segments + * quint64 Magic cookie (0xc2 0x63 0x0a 0x1c 0x99 0xd6 0x68 0xf8) + * <eof> + * + * All offsets are addresses relative to the end of the file. + * + * Meta data segments are stored as Qt resources, which must be "mounted" + * via QResource::registerResource() + * + * Component index segment: + * quint64 number of index entries + * QString identifier of component 0 + * quint64 offset of component data segment 0 + * quint64 length of component data segment 0 + * QString identifier of component .. + * quint64 offset of component data segment .. + * quint64 length of component data segment .. + * QString identifier of component n + * quint64 offset of component data segment n + * quint64 length of component data segment n + * quint64 number of index entries + * + * Component data segment: + * quint64 number of archives in this component + * QString name of archive 0 + * quint64 offset of archive 0 + * quint64 length of archive 0 + * QString name of archive .. + * quint64 offset of archive .. + * quint64 length of archive .. + * QString name of archive n + * quint64 offset of archive n + * quint64 length of archive n + * Archive 0 + * Archive .. + * Archive n + * \endverbatim + */ + +BinaryContent BinaryContent::readFromBinary( const QString& path ) { + BinaryContent c( path ); + + QFile* const file = c.file.data(); + + if ( !file->open( QIODevice::ReadOnly ) ) + throw Error( QObject::tr("Could not open binary %1: %2").arg( path, file->errorString()) ); + + const qint64 cookiepos = findMagicCookie( file ); + Q_ASSERT( cookiepos >= 0 ); + const qint64 endOfData = cookiepos + sizeof( qint64 ); + const qint64 indexSize = 6 * sizeof( qint64 ); + if ( !file->seek( endOfData - indexSize ) ) + throw Error( QObject::tr("Could not seek to binary layout section") ); + + qint64 operationsStart = retrieveInt64( file ); + /*qint64 operationsEnd = */ retrieveInt64( file ); // don't care + const qint64 count = retrieveInt64( file ); + const qint64 dataBlockSize = retrieveInt64( file ); + const qint64 dataBlockStart = endOfData - dataBlockSize; + + operationsStart += dataBlockStart; + //operationsEnd += dataBlockStart; + c.magicmaker = retrieveInt64( file ); + + const quint64 magicCookie = retrieveInt64( file ); + Q_UNUSED( magicCookie ); + Q_ASSERT( magicCookie == MagicCookie ); + + const qint64 resourceSectionSize = 2 * sizeof( qint64 ) * count; + for( int i = 0; i < count; ++i ) + { + if ( !file->seek(endOfData - indexSize - 2 * sizeof( qint64 ) * ( i + 1 ) ) ) + throw Error( QObject::tr("Could not seek to metadata index") ); + const qint64 metadataResourceOffset = retrieveInt64( file ) + dataBlockStart; + const qint64 metadataResourceLength = retrieveInt64( file ); + c.metadataResourceSegments.push_back( Range< qint64 >::fromStartAndLength( metadataResourceOffset, metadataResourceLength ) ); + } + + if ( !file->seek( operationsStart ) ) + throw Error( QObject::tr("Could not seek to operation list") ); + QStack<KDUpdater::UpdateOperation*> performedOperations; + const qint64 operationsCount = retrieveInt64(file); + verbose() << "operationsCount=" << operationsCount << std::endl; + + for(int i = operationsCount; --i >= 0; ) + { + const QString name = retrieveString( file ); + const QString xml = retrieveString( file ); + KDUpdater::UpdateOperation* const op = KDUpdater::UpdateOperationFactory::instance().create( name ); + QString debugXmlString(xml); + debugXmlString.truncate(1000); + verbose() << "Operation name=" << name << " xml=\n" << debugXmlString << std::endl; + Q_ASSERT_X(op, "BinaryContent::readFromBinary", QString::fromLatin1("Invalid operation name='%1'").arg(name).toLatin1()); + if( ! op->fromXml( xml )) + qWarning() << "Failed to load XML for operation=" << name; + performedOperations.push(op); + } + c.performedOperations = performedOperations; + + // seek to the position of the component index + if ( !file->seek( endOfData - indexSize - resourceSectionSize - 2 * sizeof( qint64 ) ) ) + throw Error( QObject::tr("Could not seek to component index information") ); + + const qint64 compIndexStart = retrieveInt64( file ) + dataBlockStart; + if ( !file->seek( compIndexStart ) ) + throw Error( QObject::tr("Could not seek to component index") ); + + + c.components = QInstallerCreator::ComponentIndex::read( file, dataBlockStart ); + c.handler.setComponentIndex( c.components ); + + const QVector< QInstallerCreator::Component > comps = c.components.components(); + verbose() << "components loaded:" << comps.count() << std::endl; + for( QVector< QInstallerCreator::Component >::const_iterator it = comps.begin(); it != comps.end(); ++it ) + { + verbose() << "loaded " << it->name(); + const QVector<QSharedPointer<Archive> > archives = it->archives(); + verbose() << " having " << archives.count() << " archives:" << std::endl; + Q_FOREACH( const QSharedPointer<Archive>& arch, archives ) + { + verbose() << " " << arch->name() << " (" << arch->size() << " bytes)" << std::endl; + } + } + return c; +} |