From 2b3a4ea8913861198280cddc1cb78571a8f9b13f Mon Sep 17 00:00:00 2001 From: Niels Weber Date: Mon, 15 Apr 2013 16:48:33 +0200 Subject: Document all repository settings. Change-Id: Ic10639cf187bab6c403cd4ba72ba45c5b1d6d2fd Reviewed-by: Tim Jenssen --- doc/installerfw.qdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index a68b7af46..3984429e8 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -792,6 +792,9 @@ \list \o URL, which points to a list of available components. + \o Enabled, with 0 disabling this repository. + \o Username, which is used as user on a protected repository. + \o Password, which sets the password to use on a protected repository. \endlist -- cgit v1.2.3 From f62a9c18206c0d256120b68b9eb3022e0ca79d23 Mon Sep 17 00:00:00 2001 From: Niels Weber Date: Wed, 17 Apr 2013 15:10:23 +0200 Subject: Use proper English. Change-Id: I3f9c351c8bfd7d97b50cdb41a01ddd274ef44d41 Reviewed-by: Kai Koehne --- src/libs/installer/repository.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp index f5b366d05..2f0510760 100644 --- a/src/libs/installer/repository.cpp +++ b/src/libs/installer/repository.cpp @@ -88,12 +88,12 @@ Repository::Repository(const QUrl &url, bool isDefault) */ Repository Repository::fromUserInput(const QString &repositoryUrl) { - QStringList supportedShemes; - supportedShemes << QLatin1String("http") << QLatin1String("https") << QLatin1String("ftp") << + QStringList supportedSchemes; + supportedSchemes << QLatin1String("http") << QLatin1String("https") << QLatin1String("ftp") << QLatin1String("file"); QUrl url = QUrl::fromUserInput(repositoryUrl); - if (!supportedShemes.contains(url.scheme()) && QFileInfo(url.toString()).exists()) + if (!supportedSchemes.contains(url.scheme()) && QFileInfo(url.toString()).exists()) url = QLatin1String("file:///") + url.toString(); QString userName = url.userName(); -- cgit v1.2.3 From bbd2cab7c4097a0776c28a5e1d4cc2f8b66b53c1 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 18 Apr 2013 17:54:09 +0200 Subject: fix error handling in 7z lib - remove an already existing symlink at extraction, because it uses QFile to get the io device and that would be the symlink target which was wrong. - try to get better 7z error output - an error would be now: "There was a problem while performing the operation: Error while extracting 'C:\Qt\qtcreator-2.7.0_test.7z': Cannot open file: D:/test/qtcreator-2.7.0_test/bin/sqldrivers/qsqlite4.dll (Access is denied.)" - move setPermissions to the end so read only files are getting the right timestamp Change-Id: I8881144660296618d443568e6974bf2721328991 Reviewed-by: Niels Weber Reviewed-by: Karsten Heimrich --- src/libs/installer/extractarchiveoperation_p.h | 7 +- src/libs/installer/getrepositorymetainfojob.cpp | 2 +- src/libs/installer/lib7z_facade.cpp | 235 ++++++++++++++++++------ src/libs/installer/lib7z_facade.h | 2 +- src/sdk/installerbase_p.cpp | 4 +- 5 files changed, 182 insertions(+), 68 deletions(-) diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h index 846bda83a..44e75a949 100644 --- a/src/libs/installer/extractarchiveoperation_p.h +++ b/src/libs/installer/extractarchiveoperation_p.h @@ -190,12 +190,7 @@ public: Lib7z::extractArchive(&archive, targetDir, callback); emit finished(true, QString()); } catch (const Lib7z::SevenZipException& e) { -#ifdef Q_OS_WIN - emit finished(false, tr("Error while extracting %1: %2. (Maybe the target dir(%3) is blocked by " - "another process.)").arg(archivePath, e.message(), targetDir)); -#else - emit finished(false, tr("Error while extracting %1: %2.").arg(archivePath, e.message())); -#endif + emit finished(false, tr("Error while extracting '%1': %2").arg(archivePath, e.message())); } catch (...) { emit finished(false, tr("Unknown exception caught while extracting %1.").arg(archivePath)); } diff --git a/src/libs/installer/getrepositorymetainfojob.cpp b/src/libs/installer/getrepositorymetainfojob.cpp index 6fa21e32c..bbf3afe25 100644 --- a/src/libs/installer/getrepositorymetainfojob.cpp +++ b/src/libs/installer/getrepositorymetainfojob.cpp @@ -98,7 +98,7 @@ public: } emit finished(true, QString()); } catch (const Lib7z::SevenZipException& e) { - emit finished(false, tr("Error while extracting %1. Error: %2").arg(m_archive, e.message())); + emit finished(false, tr("Error while extracting '%1': %2").arg(m_archive, e.message())); } catch (...) { emit finished(false, tr("Unknown exception caught while extracting %1.").arg(m_archive)); } diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp index 0c02890e2..3695c0038 100644 --- a/src/libs/installer/lib7z_facade.cpp +++ b/src/libs/installer/lib7z_facade.cpp @@ -73,6 +73,26 @@ bool CreateHardLinkWrapper(const QString &dest, const QString &file) using namespace Lib7z; using namespace NWindows; + +namespace Lib7z { + Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock) + Q_GLOBAL_STATIC(QString, getLastErrorString) + + QString lastError() + { + QReadLocker locker(lastErrorReadWriteLock()); + Q_UNUSED(locker) + return *getLastErrorString(); + } + + void setLastError(const QString &errorString) + { + QWriteLocker locker(lastErrorReadWriteLock()); + Q_UNUSED(locker) + *getLastErrorString() = errorString; + } +} + namespace { /** * RAII class to create a directory (tryCreate()) and delete it on destruction unless released. @@ -273,57 +293,81 @@ namespace Lib7z { class QIODeviceSequentialOutStream : public ISequentialOutStream, public CMyUnknownImp { public: + enum DestructorBehavior{ + CloseAndDeleteIODeviceAtDestructor, + KeepIODeviceAtItIsAtDestructor + }; + MY_UNKNOWN_IMP - explicit QIODeviceSequentialOutStream(QIODevice* device); + explicit QIODeviceSequentialOutStream(QIODevice* device, DestructorBehavior behavior); ~QIODeviceSequentialOutStream(); + QString errorString() const; /* reimp */ STDMETHOD(Write)(const void* data, UInt32 size, UInt32* processedSize); private: QPointer m_device; - const bool closeOnDestroy; + const DestructorBehavior m_destructorBehavior; + QString m_errorString; }; -QIODeviceSequentialOutStream::QIODeviceSequentialOutStream(QIODevice* device) - : ISequentialOutStream(), - CMyUnknownImp(), - m_device(device), - closeOnDestroy(!device->isOpen()) +QIODeviceSequentialOutStream::QIODeviceSequentialOutStream(QIODevice* device, DestructorBehavior behavior) + : ISequentialOutStream() + , CMyUnknownImp() + , m_device(device) + , m_destructorBehavior(behavior) { - assert(m_device); - if (closeOnDestroy) - m_device->open(QIODevice::WriteOnly); + Q_ASSERT(m_device); + + if (!m_device->open(QIODevice::WriteOnly)) { + m_errorString = m_device->errorString(); + } } QIODeviceSequentialOutStream::~QIODeviceSequentialOutStream() { - if (closeOnDestroy) - { + if (m_destructorBehavior == CloseAndDeleteIODeviceAtDestructor) { m_device->close(); delete m_device; + m_device = 0; } } +QString QIODeviceSequentialOutStream::errorString() const +{ + return m_errorString; +} + HRESULT QIODeviceSequentialOutStream::Write(const void* data, UInt32 size, UInt32* processedSize) { if (!m_device) { if (processedSize) *processedSize = 0; + m_errorString = QObject::tr("No device set for output stream"); return E_FAIL; } - if (closeOnDestroy && !m_device->isOpen()) { + if (!m_device->isOpen()) { const bool opened = m_device->open(QIODevice::WriteOnly); if (!opened) { if (processedSize) *processedSize = 0; + m_errorString = m_device->errorString(); return E_FAIL; } } const qint64 written = m_device->write(reinterpret_cast(data), size); + if (written == -1) { + if (processedSize) + *processedSize = 0; + m_errorString = m_device->errorString(); + return E_FAIL; + } + if (processedSize) *processedSize = written; - return written >= 0 ? S_OK : E_FAIL; + m_errorString = QString(); + return S_OK; } class QIODeviceInStream : public IInStream, public CMyUnknownImp @@ -704,9 +748,15 @@ public: /* reimp */ STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode) { Q_UNUSED(askExtractMode) - *outStream = 0; + *outStream = 0; if (device != 0) { - CMyComPtr stream = new QIODeviceSequentialOutStream(device); + QIODeviceSequentialOutStream *qOutStream = new QIODeviceSequentialOutStream(device, + QIODeviceSequentialOutStream::KeepIODeviceAtItIsAtDestructor); + if (!qOutStream->errorString().isEmpty()) { + Lib7z::setLastError(qOutStream->errorString()); + return E_FAIL; + } + CMyComPtr stream = qOutStream; *outStream = stream.Detach(); return S_OK; } else if (!targetDir.isEmpty()) { @@ -715,11 +765,14 @@ public: currentIndex = index; UString s; - if (arc->GetItemPath(index, s) != S_OK) - throw SevenZipException(QObject::tr("Could not retrieve path of archive item %1").arg(index)); + if (arc->GetItemPath(index, s) != S_OK) { + Lib7z::setLastError(QObject::tr("Could not retrieve path of archive item %1").arg(index)); + return E_FAIL; + } const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, path)); + DirectoryGuard guard(fi.absolutePath()); const QStringList directories = guard.tryCreate(); @@ -729,8 +782,8 @@ public: QDir(fi.absolutePath()).mkdir(fi.fileName()); // this makes sure that all directories created get removed as well - for (QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it) - q->setCurrentFile(*it); + foreach (const QString &directory, directories) + q->setCurrentFile(directory); if (!isDir && !q->prepareForFile(fi.absoluteFilePath())) return E_FAIL; @@ -738,8 +791,22 @@ public: q->setCurrentFile(fi.absoluteFilePath()); if (!isDir) { - CMyComPtr< ISequentialOutStream > stream = new QIODeviceSequentialOutStream(new QFile(fi - .absoluteFilePath())); +#ifndef Q_OS_WIN + // do not follow symlinks, so we need to remove an existing one + if (fi.isSymLink() && (!QFile::remove(fi.absoluteFilePath()))) { + Lib7z::setLastError(QObject::tr("Could not remove already existing " + "symlink. %1").arg(fi.absoluteFilePath())); + return E_FAIL; + } +#endif + QIODeviceSequentialOutStream *qOutStream = new QIODeviceSequentialOutStream( + new QFile(fi.absoluteFilePath()), QIODeviceSequentialOutStream::CloseAndDeleteIODeviceAtDestructor); + if (!qOutStream->errorString().isEmpty()) { + Lib7z::setLastError(QObject::tr("Could not open file: %1 (%2)").arg( + fi.absoluteFilePath(), qOutStream->errorString())); + return E_FAIL; + } + CMyComPtr stream = qOutStream; *outStream = stream; stream.Detach(); } @@ -761,50 +828,56 @@ public: Q_UNUSED(resultEOperationResult) if (!targetDir.isEmpty()) { - bool hasPerm; + bool hasPerm = false; const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm); UString s; if (arc->GetItemPath(currentIndex, s) != S_OK) { - throw SevenZipException(QObject::tr("Could not retrieve path of archive item %1") + Lib7z::setLastError(QObject::tr("Could not retrieve path of archive item %1") .arg(currentIndex)); + return E_FAIL; } const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir, path)) .absoluteFilePath(); - if (hasPerm) { - QFile::setPermissions(absFilePath, permissions); - - // do we have a symlink? - const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0); - struct stat stat_info; - stat_info.st_mode = attributes >> 16; - if (S_ISLNK(stat_info.st_mode)) { - QFile f(absFilePath); - f.open(QIODevice::ReadOnly); - const QByteArray path = f.readAll(); - f.close(); - f.remove(); + // do we have a symlink? + const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0); + struct stat stat_info; + stat_info.st_mode = attributes >> 16; + if (S_ISLNK(stat_info.st_mode)) { #ifdef Q_OS_WIN - if (!CreateHardLinkWrapper(absFilePath, QLatin1String(path))) { - throw SevenZipException(QObject::tr("Could not create file system link at %1") - .arg(absFilePath)); - } + qFatal(QString::fromLatin1("Creating a link from archive is not implemented for windows. " + "Link filename: %1").arg(absFilePath).toLatin1()); + // TODO +// if (!CreateHardLinkWrapper(absFilePath, QLatin1String(symlinkTarget))) { +// return S_FALSE; +// } #else - if (!QFile::link(QString::fromLatin1(path), absFilePath)) { - throw SevenZipException(QObject::tr("Could not create softlink at %1") - .arg(absFilePath)); - } + QFileInfo symlinkPlaceHolderFileInfo(absFilePath); + if (symlinkPlaceHolderFileInfo.isSymLink()) { + Lib7z::setLastError(QObject::tr("Could not create symlink at '%1'. " + "Another one is already existing.").arg(absFilePath)); + return E_FAIL; + } + QFile symlinkPlaceHolderFile(absFilePath); + if (!symlinkPlaceHolderFile.open(QIODevice::ReadOnly)) { + Lib7z::setLastError(QObject::tr("Could not read symlink target from file '%1'." + ).arg(absFilePath)); + return E_FAIL; + } - // If we have a symlink, bail out after we have linked it to avoid deleting the link. - // CFileBase::Create(...) seems to broken in regard of symlink handling. If the link - // already exists, it simple does an unlink on it. So we can't set time values on the - // symlink in the following code, as COutFile::Open(...) calls former mentioned Create() - // function and thus the symlink is deleted. - return S_OK; -#endif + const QByteArray symlinkTarget = symlinkPlaceHolderFile.readAll(); + symlinkPlaceHolderFile.close(); + symlinkPlaceHolderFile.remove(); + QFile targetFile(QString::fromLatin1(symlinkTarget)); + if (!targetFile.link(absFilePath)) { + Lib7z::setLastError(QObject::tr("Could not create symlink at %1. %2").arg( + absFilePath, targetFile.errorString())); + return E_FAIL; } + return S_OK; +#endif } try { @@ -830,6 +903,9 @@ public: #endif } } catch (...) {} + + if (hasPerm) + QFile::setPermissions(absFilePath, permissions); } return S_OK; @@ -1157,6 +1233,46 @@ void ExtractItemJob::setTarget(QIODevice* dev) d->target = dev; } +namespace{ + QString errorMessageFrom7zResult(const LONG & extractResult) + { + if (!Lib7z::lastError().isEmpty()) + return Lib7z::lastError(); + + QString errorMessage = QObject::tr("internal code: %1"); + switch (extractResult) { + case S_OK: + qFatal("S_OK value is not a valid error code."); + break; + case E_NOTIMPL: + errorMessage = errorMessage.arg(QLatin1String("E_NOTIMPL")); + break; + case E_NOINTERFACE: + errorMessage = errorMessage.arg(QLatin1String("E_NOINTERFACE")); + break; + case E_ABORT: + errorMessage = errorMessage.arg(QLatin1String("E_ABORT")); + break; + case E_FAIL: + errorMessage = errorMessage.arg(QLatin1String("E_FAIL")); + break; + case STG_E_INVALIDFUNCTION: + errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION")); + break; + case E_OUTOFMEMORY: + errorMessage = QObject::tr("not enough memory"); + break; + case E_INVALIDARG: + errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG")); + break; + default: + errorMessage = QObject::tr("Error: %1").arg(extractResult); + break; + } + return errorMessage; + } +} + void Lib7z::createArchive(QIODevice* archive, const QStringList &sourcePaths, UpdateCallback* callback) { assert(archive); @@ -1219,7 +1335,8 @@ void Lib7z::createArchive(QIODevice* archive, const QStringList &sourcePaths, Up CUpdateErrorInfo errorInfo; const HRESULT res = UpdateArchive(codecs.get(), censor, options, errorInfo, 0, callback->impl()); if (res != S_OK || !QFile::exists(tempFile)) - throw SevenZipException(QObject::tr("Could not create archive %1").arg(tempFile)); + throw SevenZipException(QObject::tr("Could not create archive %1. %2").arg( + tempFile, errorMessageFrom7zResult(res))); { //TODO remove temp file even if one the following throws @@ -1282,9 +1399,10 @@ void Lib7z::extractArchive(QIODevice* archive, const File& item, QIODevice* targ callback->setTarget(target); const LONG extractResult = parchive->Extract(&itemIdx, 1, /*testmode=*/1, callback->impl()); - //TODO: how to interpret result? + if (extractResult != S_OK) - throw SevenZipException(QObject::tr("Extracting %1 failed.").arg(item.path)); + throw SevenZipException(errorMessageFrom7zResult(extractResult)); + } catch (const char *err) { throw SevenZipException(err); } catch (...) { @@ -1339,9 +1457,9 @@ void Lib7z::extractArchive(QIODevice* archive, const QString &targetDirectory, E IInArchive* const arch = arc.Archive; callback->impl()->setArchive(&arc); const LONG extractResult = arch->Extract(0, static_cast< UInt32 >(-1), false, callback->impl()); - //TODO is it possible to get a more detailed error? + if (extractResult != S_OK) - throw SevenZipException(QObject::tr("Extraction failed.")); + throw SevenZipException(errorMessageFrom7zResult(extractResult)); } outDir.release(); @@ -1407,7 +1525,8 @@ void ExtractItemJob::doStart() extractArchive(d->archive, d->targetDirectory, d->callback); } catch (const SevenZipException& e) { setError(Failed); - setErrorString(e.message()); + setErrorString(QObject::tr("Error while extracting '%1': %2").arg( + d->item.path, e.message())); } catch (...) { setError(Failed); setErrorString(QObject::tr("Unknown exception caught (%1)").arg(QObject::tr("Failed"))); diff --git a/src/libs/installer/lib7z_facade.h b/src/libs/installer/lib7z_facade.h index e61cce6ac..515d63bba 100644 --- a/src/libs/installer/lib7z_facade.h +++ b/src/libs/installer/lib7z_facade.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "Common/MyWindows.h" @@ -23,7 +24,6 @@ template class QVector; QT_END_NAMESPACE namespace Lib7z { - class INSTALLER_EXPORT SevenZipException : public std::runtime_error { public: explicit SevenZipException( const QString& msg ) : std::runtime_error( msg.toStdString() ), m_message( msg ) {} diff --git a/src/sdk/installerbase_p.cpp b/src/sdk/installerbase_p.cpp index dc4d82d10..0d59e541e 100644 --- a/src/sdk/installerbase_p.cpp +++ b/src/sdk/installerbase_p.cpp @@ -260,11 +260,11 @@ int InstallerBase::replaceMaintenanceToolBinary(QStringList arguments) try { Lib7z::extractArchive(&archive, QDir::tempPath()); if (!archive.remove()) { - qDebug() << QString::fromLatin1("Could not delete file %1: %2.").arg( + qDebug() << QString::fromLatin1("Could not delete file %1: %2").arg( target, archive.errorString()); } } catch (const Lib7z::SevenZipException& e) { - qDebug() << QString::fromLatin1("Error while extracting %1: %2.").arg(target, e.message()); + qDebug() << QString::fromLatin1("Error while extracting '%1': %2").arg(target, e.message()); return EXIT_FAILURE; } catch (...) { qDebug() << QString::fromLatin1("Unknown exception caught while extracting %1.").arg(target); -- cgit v1.2.3