diff options
Diffstat (limited to 'src/libs/installer/libarchivearchive.cpp')
-rw-r--r-- | src/libs/installer/libarchivearchive.cpp | 458 |
1 files changed, 382 insertions, 76 deletions
diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp index d3a79bd40..233a4c28b 100644 --- a/src/libs/installer/libarchivearchive.cpp +++ b/src/libs/installer/libarchivearchive.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -29,16 +29,26 @@ #include "libarchivearchive.h" #include "directoryguard.h" +#include "fileguard.h" #include "errors.h" #include "globals.h" #include <stdio.h> +#include <string.h> #include <QApplication> #include <QFileInfo> #include <QDir> #include <QTimer> +#ifdef Q_OS_WIN +#include <locale.h> +#endif + +#if defined(Q_OS_WIN) && !defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) +#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +#endif + namespace QInstaller { /*! @@ -59,6 +69,184 @@ namespace QInstaller { \internal */ +namespace ArchiveEntryPaths { + +// TODO: it is expected that the filename handling will change in the major +// version jump to libarchive 4.x, and the *_w methods will disappear. + +/*! + \internal + + Returns the path name from the archive \a entry as a \c QString. The path is + stored internally in a multistring that can contain a combination of a wide + character or multibyte string in current locale or unicode string encoded as UTF-8. + + \note The MBS version is expected to be convertable from latin-1 which might + not actually be the case, as the encoding depends on the current locale. +*/ +static QString pathname(archive_entry *const entry) +{ + if (!entry) + return QString(); +#ifdef Q_OS_WIN + if (const wchar_t *path = archive_entry_pathname_w(entry)) + return QString::fromWCharArray(path); +#endif + if (const char *path = archive_entry_pathname_utf8(entry)) + return QString::fromUtf8(path); + + return QString::fromLatin1(archive_entry_pathname(entry)); +} + +/*! + \internal + + Sets the path name for the archive \a entry to \a path. This function + tries to update all variants of the string in the internal multistring + struct. +*/ +static void setPathname(archive_entry *const entry, const QString &path) +{ + if (!entry) + return; + + // Try updating all variants at once, stops on failure + if (archive_entry_update_pathname_utf8(entry, path.toUtf8())) + return; + + // If that does not work, then set them individually + archive_entry_set_pathname(entry, path.toLatin1()); + archive_entry_set_pathname_utf8(entry, path.toUtf8()); +#ifdef Q_OS_WIN + wchar_t *wpath = new wchar_t[path.length() + 1]; + path.toWCharArray(wpath); + wpath[path.length()] = '\0'; + + archive_entry_copy_pathname_w(entry, wpath); + delete[] wpath; +#endif +} + +/*! + \internal + + Returns the source path on disk from the current archive \a entry as a \c QString. + The path is stored internally in a multistring that can contain a combination + of a wide character or multibyte string in current locale. + + \note The MBS version is expected to be convertable from UTF-8. +*/ +static QString sourcepath(archive_entry * const entry) +{ + if (!entry) + return QString(); +#ifdef Q_OS_WIN + if (const wchar_t *path = archive_entry_sourcepath_w(entry)) + return QString::fromWCharArray(path); +#endif + return QString::fromUtf8(archive_entry_sourcepath(entry)); +} + +/*! + \internal + + Returns the hardlink path from the current archive \a entry as a \c QString. + The path is stored internally in a multistring that can contain a combination of a wide + character or multibyte string in current locale or unicode string encoded as UTF-8. + + \note The MBS version is expected to be convertable from latin-1 which might + not actually be the case, as the encoding depends on the current locale. +*/ +static QString hardlink(archive_entry * const entry) +{ + if (!entry) + return QString(); +#ifdef Q_OS_WIN + if (const wchar_t *path = archive_entry_hardlink_w(entry)) + return QString::fromWCharArray(path); +#endif + if (const char *path = archive_entry_hardlink_utf8(entry)) + return QString::fromUtf8(path); + + return QString::fromLatin1(archive_entry_hardlink(entry)); +} + +/*! + \internal + + Sets the hard link path for the archive \a entry to \a path. This function + tries to update all variants of the string in the internal multistring + struct. +*/ +static void setHardlink(archive_entry *const entry, const QString &path) +{ + if (!entry) + return; + + // Try updating all variants at once, stops on failure + if (archive_entry_update_hardlink_utf8(entry, path.toUtf8())) + return; + + // If that does not work, then set them individually + archive_entry_set_hardlink(entry, path.toLatin1()); + archive_entry_set_hardlink_utf8(entry, path.toUtf8()); +#ifdef Q_OS_WIN + wchar_t *wpath = new wchar_t[path.length() + 1]; + path.toWCharArray(wpath); + wpath[path.length()] = '\0'; + + archive_entry_copy_hardlink_w(entry, wpath); + delete[] wpath; +#endif +} + +/*! + \internal + + Calls a function object or pointer \a func with any number of extra + arguments \a args. Returns the return value of the function of type T. + + On Windows, this changes the locale category LC_CTYPE from C to system + locale. The original LC_CTYPE is restored after the function call. + Currently the locale is unchanged on other platforms. +*/ +template <typename T, typename F, typename... Args> +static T callWithSystemLocale(F func, Args... args) +{ +#ifdef Q_OS_WIN + const QByteArray oldLocale = setlocale(LC_CTYPE, ""); +#endif + T returnValue = func(std::forward<Args>(args)...); +#ifdef Q_OS_WIN + setlocale(LC_CTYPE, oldLocale.constData()); +#endif + return returnValue; +} + +/*! + \internal + + Calls a function object or pointer \a func with any number of extra + arguments \a args. + + On Windows, this changes the locale category LC_CTYPE from C to system + locale. The original LC_CTYPE is restored after the function call. + Currently the locale is unchanged on other platforms. +*/ +template <typename F, typename... Args> +static void callWithSystemLocale(F func, Args... args) +{ +#ifdef Q_OS_WIN + const QByteArray oldLocale = setlocale(LC_CTYPE, ""); +#endif + func(std::forward<Args>(args)...); +#ifdef Q_OS_WIN + setlocale(LC_CTYPE, oldLocale.constData()); +#endif +} + +} // namespace ArchiveEntryPaths + /*! \inmodule QtInstallerFramework \class QInstaller::ExtractWorker @@ -88,7 +276,7 @@ void ExtractWorker::extract(const QString &dirPath, const quint64 totalFiles) LibArchiveArchive::configureReader(reader.get()); LibArchiveArchive::configureDiskWriter(writer.get()); - DirectoryGuard targetDir(QFileInfo(dirPath).absolutePath()); + DirectoryGuard targetDir(QFileInfo(dirPath).absoluteFilePath()); try { const QStringList createdDirs = targetDir.tryCreate(); // Make sure that all leading directories created get removed as well @@ -102,7 +290,8 @@ void ExtractWorker::extract(const QString &dirPath, const quint64 totalFiles) int status = archive_read_open1(reader.get()); if (status != ARCHIVE_OK) { m_status = Failure; - emit finished(QLatin1String(archive_error_string(reader.get()))); + emit finished(tr("Cannot open archive for reading: %1") + .arg(LibArchiveArchive::errorStringWithCode(reader.get()))); return; } @@ -111,22 +300,23 @@ void ExtractWorker::extract(const QString &dirPath, const quint64 totalFiles) emit finished(QLatin1String("Extract canceled.")); return; } - status = archive_read_next_header(reader.get(), &entry); + status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry); if (status == ARCHIVE_EOF) break; if (status != ARCHIVE_OK) { m_status = Failure; - emit finished(QLatin1String(archive_error_string(reader.get()))); + emit finished(tr("Cannot read entry header: %1") + .arg(LibArchiveArchive::errorStringWithCode(reader.get()))); return; } - const char *current = archive_entry_pathname(entry); - const QString outputPath = dirPath + QDir::separator() + QString::fromLocal8Bit(current); - archive_entry_set_pathname(entry, outputPath.toLocal8Bit()); - - const char *hardlink = archive_entry_hardlink(entry); - if (hardlink) { - const QString hardLinkPath = dirPath + QDir::separator() + QString::fromLocal8Bit(hardlink); - archive_entry_set_hardlink(entry, hardLinkPath.toLocal8Bit()); + const QString current = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry); + const QString outputPath = dirPath + QDir::separator() + current; + ArchiveEntryPaths::callWithSystemLocale(&ArchiveEntryPaths::setPathname, entry, outputPath); + + const QString hardlink = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::hardlink, entry); + if (!hardlink.isEmpty()) { + const QString hardLinkPath = dirPath + QDir::separator() + hardlink; + ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setHardlink, entry, hardLinkPath); } emit currentEntryChanged(outputPath); @@ -222,25 +412,36 @@ bool ExtractWorker::writeEntry(archive *reader, archive *writer, archive_entry * size_t size; int64_t offset; + const QString entryPath = ArchiveEntryPaths::callWithSystemLocale + <QString>(ArchiveEntryPaths::pathname, entry); + + FileGuardLocker locker(entryPath, FileGuard::globalObject()); + status = archive_write_header(writer, entry); if (status != ARCHIVE_OK) { - emit finished(QLatin1String(archive_error_string(writer))); + emit finished(tr("Cannot write entry \"%1\" to disk: %2") + .arg(entryPath, LibArchiveArchive::errorStringWithCode(writer))); return false; } forever { status = archive_read_data_block(reader, &buff, &size, &offset); - if (status == ARCHIVE_EOF) - return true; + if (status == ARCHIVE_EOF) { + status = archive_write_finish_entry(writer); + if (status == ARCHIVE_OK) + return true; + } if (status != ARCHIVE_OK) { m_status = Failure; - emit finished(QLatin1String(archive_error_string(reader))); + emit finished(tr("Cannot write entry \"%1\" to disk: %2") + .arg(entryPath, LibArchiveArchive::errorStringWithCode(reader))); return false; } status = archive_write_data_block(writer, buff, size, offset); if (status != ARCHIVE_OK) { m_status = Failure; - emit finished(QLatin1String(archive_error_string(writer))); + emit finished(tr("Cannot write entry \"%1\" to disk: %2") + .arg(entryPath, LibArchiveArchive::errorStringWithCode(writer))); return false; } } @@ -421,7 +622,7 @@ bool LibArchiveArchive::extract(const QString &dirPath, const quint64 totalFiles configureReader(reader.get()); configureDiskWriter(writer.get()); - DirectoryGuard targetDir(QFileInfo(dirPath).absolutePath()); + DirectoryGuard targetDir(QFileInfo(dirPath).absoluteFilePath()); try { const QStringList createdDirs = targetDir.tryCreate(); // Make sure that all leading directories created get removed as well @@ -429,32 +630,38 @@ bool LibArchiveArchive::extract(const QString &dirPath, const quint64 totalFiles emit currentEntryChanged(directory); int status = archiveReadOpenWithCallbacks(reader.get()); - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot open archive for reading: %1") + .arg(errorStringWithCode(reader.get()))); + } forever { if (m_cancelScheduled) throw Error(QLatin1String("Extract canceled.")); - status = archive_read_next_header(reader.get(), &entry); + status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry); if (status == ARCHIVE_EOF) break; - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot read entry header: %1") + .arg(errorStringWithCode(reader.get()))); + } - const char *current = archive_entry_pathname(entry); - const QString outputPath = dirPath + QDir::separator() + QString::fromLocal8Bit(current); - archive_entry_set_pathname(entry, outputPath.toLocal8Bit()); + const QString current = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry); + const QString outputPath = dirPath + QDir::separator() + current; + ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setPathname, entry, outputPath); - const char *hardlink = archive_entry_hardlink(entry); - if (hardlink) { - const QString hardLinkPath = dirPath + QDir::separator() + QString::fromLocal8Bit(hardlink); - archive_entry_set_hardlink(entry, hardLinkPath.toLocal8Bit()); + const QString hardlink = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::hardlink, entry); + if (!hardlink.isEmpty()) { + const QString hardLinkPath = dirPath + QDir::separator() + hardlink; + ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setHardlink, entry, hardLinkPath); } emit currentEntryChanged(outputPath); - if (!writeEntry(reader.get(), writer.get(), entry)) - throw Error(errorString()); // appropriate error string set in writeEntry() + if (!writeEntry(reader.get(), writer.get(), entry)) { + throw Error(tr("Cannot write entry \"%1\" to disk: %2") + .arg(outputPath, errorString())); // appropriate error string set in writeEntry() + } ++completed; emit completedChanged(completed, totalFiles); @@ -481,43 +688,98 @@ bool LibArchiveArchive::create(const QStringList &data) QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_new()); configureWriter(writer.get()); + QStringList globbedData; + for (auto &dataEntry : data) { // Expand glob pattern entries with proper filenames + if (!dataEntry.contains(QLatin1Char('*'))) { + globbedData.append(dataEntry); + continue; + } + const QFileInfo entryInfo(dataEntry); + if (entryInfo.path().contains(QLatin1Char('*'))) { + setErrorString(QString::fromLatin1("Invalid argument \"%1\": glob patterns " + "are not supported between directory paths.").arg(dataEntry)); + return false; + } + const QDir parentDir = entryInfo.dir(); + const QList<QFileInfo> infoList = parentDir.entryInfoList(QStringList() + << entryInfo.fileName(), QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot); + + for (auto &info : infoList) + globbedData.append(info.absoluteFilePath()); + } + try { int status; - if ((status = archive_write_open_filename(writer.get(), m_data->file.fileName().toLocal8Bit()))) - throw Error(QLatin1String(archive_error_string(writer.get()))); +#ifdef Q_OS_WIN + QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> fileName_w( + new wchar_t[m_data->file.fileName().length() + 1]); - for (auto &dataEntry : data) { + m_data->file.fileName().toWCharArray(fileName_w.get()); + fileName_w.get()[m_data->file.fileName().length()] = '\0'; + + if ((status = archive_write_open_filename_w(writer.get(), fileName_w.get()))) { + throw Error(tr("Cannot open file \"%1\" for writing: %2") + .arg(m_data->file.fileName(), errorStringWithCode(writer.get()))); + } +#else + if ((status = archive_write_open_filename(writer.get(), m_data->file.fileName().toUtf8()))) { + throw Error(tr("Cannot open file \"%1\" for writing: %2") + .arg(m_data->file.fileName(), errorStringWithCode(writer.get()))); + } +#endif + for (auto &dataEntry : globbedData) { QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_disk_new()); configureDiskReader(reader.get()); - if ((status = archive_read_disk_open(reader.get(), dataEntry.toLocal8Bit()))) - throw Error(QLatin1String(archive_error_string(reader.get()))); +#ifdef Q_OS_WIN + QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> dataEntry_w( + new wchar_t[dataEntry.length() + 1]); + dataEntry.toWCharArray(dataEntry_w.get()); + dataEntry_w.get()[dataEntry.length()] = '\0'; + + if ((status = archive_read_disk_open_w(reader.get(), dataEntry_w.get()))) { + throw Error(tr("Cannot open file \"%1\" for reading: %2") + .arg(dataEntry, errorStringWithCode(reader.get()))); + } +#else + if ((status = archive_read_disk_open(reader.get(), dataEntry.toUtf8()))) { + throw Error(tr("Cannot open file \"%1\" for reading: %2") + .arg(dataEntry, errorStringWithCode(reader.get()))); + } +#endif QDir basePath = QFileInfo(dataEntry).dir(); forever { QScopedPointer<archive_entry, ScopedPointerEntryDeleter> entry(archive_entry_new()); - status = archive_read_next_header2(reader.get(), entry.get()); + status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header2, reader.get(), entry.get()); if (status == ARCHIVE_EOF) break; - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot read entry header: %1") + .arg(errorStringWithCode(reader.get()))); + } - const QFileInfo fileOrDir(pathWithoutNamespace(QLatin1String(archive_entry_sourcepath(entry.get())))); + const QFileInfo fileOrDir(pathWithoutNamespace( + ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::sourcepath, entry.get()))); // Set new path name in archive, otherwise we add all directories from absolute path const QString newPath = basePath.relativeFilePath(fileOrDir.filePath()); - archive_entry_set_pathname(entry.get(), newPath.toLocal8Bit()); + ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setPathname, entry.get(), newPath); archive_read_disk_descend(reader.get()); status = archive_write_header(writer.get(), entry.get()); - if (status < ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(writer.get()))); - + if (status < ARCHIVE_OK) { + throw Error(tr("Cannot write entry header for \"%1\": %2") + .arg(fileOrDir.filePath(), errorStringWithCode(writer.get()))); + } if (fileOrDir.isDir() || archive_entry_size(entry.get()) == 0) continue; // nothing to copy - QFile file(pathWithoutNamespace(QLatin1String(archive_entry_sourcepath(entry.get())))); - if (!file.open(QIODevice::ReadOnly)) - throw Error(file.errorString()); + QFile file(pathWithoutNamespace(ArchiveEntryPaths::callWithSystemLocale<QString>( + ArchiveEntryPaths::sourcepath, entry.get()))); + if (!file.open(QIODevice::ReadOnly)) { + throw Error(tr("Cannot open file \"%1\" for reading: %2") + .arg(file.fileName(), file.errorString())); + } QByteArray buffer; constexpr qint64 blockSize = 4 * 1024; @@ -554,20 +816,25 @@ QVector<ArchiveEntry> LibArchiveArchive::list() QVector<ArchiveEntry> entries; try { int status = archiveReadOpenWithCallbacks(reader.get()); - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot open archive for reading: %1") + .arg(errorStringWithCode(reader.get()))); + } forever { - status = archive_read_next_header(reader.get(), &entry); + status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry); if (status == ARCHIVE_EOF) break; - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot read entry header: %1") + .arg(errorStringWithCode(reader.get()))); + } ArchiveEntry archiveEntry; - archiveEntry.path = QLatin1String(archive_entry_pathname(entry)); - archiveEntry.utcTime = QDateTime::fromTime_t(archive_entry_mtime(entry)); + archiveEntry.path = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry); + archiveEntry.utcTime = QDateTime::fromSecsSinceEpoch(archive_entry_mtime(entry)); archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR); + archiveEntry.isSymbolicLink = (archive_entry_filetype(entry) == AE_IFLNK); archiveEntry.uncompressedSize = archive_entry_size(entry); archiveEntry.permissions_mode = archive_entry_perm(entry); @@ -595,8 +862,10 @@ bool LibArchiveArchive::isSupported() try { const int status = archiveReadOpenWithCallbacks(reader.get()); - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot open archive for reading: %1") + .arg(errorStringWithCode(reader.get()))); + } } catch (const Error &e) { setErrorString(e.message()); m_data->file.seek(0); @@ -685,6 +954,7 @@ void LibArchiveArchive::configureReader(archive *archive) archive_read_support_format_tar(archive); archive_read_support_format_zip(archive); + archive_read_support_format_7zip(archive); } /*! @@ -692,16 +962,25 @@ void LibArchiveArchive::configureReader(archive *archive) */ void LibArchiveArchive::configureWriter(archive *archive) { - if (QFileInfo(m_data->file.fileName()).suffix() == QLatin1String("zip")) { - archive_write_set_format_zip(archive); + const QString fileName = m_data->file.fileName(); + if (fileName.endsWith(QLatin1String(".qbsp"), Qt::CaseInsensitive)) { + // The Qt board support package file extension is really a 7z. + archive_write_set_format_7zip(archive); } else { - archive_write_set_format_pax_restricted(archive); - archive_write_set_format_filter_by_ext(archive, m_data->file.fileName().toLatin1()); + archive_write_set_format_filter_by_ext(archive, fileName.toUtf8()); } - const QByteArray options = "compression-level=" + QString::number(compressionLevel()).toLatin1(); - if (archive_write_set_options(archive, options.constData())) { // not fatal - qCWarning(QInstaller::lcInstallerInstallLog) << "Could not set options" << options - << "for archive" << m_data->file.fileName() << ":" << archive_error_string(archive); + + const QByteArray charset = "hdrcharset=UTF-8"; + // not checked as this is ignored on some archive formats like 7z + archive_write_set_options(archive, charset); + + if (compressionLevel() == CompressionLevel::Normal) + return; + + const QByteArray compression = "compression-level=" + QString::number(compressionLevel()).toLatin1(); + if (archive_write_set_options(archive, compression.constData())) { // not fatal + qCWarning(QInstaller::lcInstallerInstallLog) << "Could not set option" << compression + << "for archive" << m_data->file.fileName() << ":" << errorStringWithCode(archive); } } @@ -775,23 +1054,31 @@ bool LibArchiveArchive::writeEntry(archive *reader, archive *writer, archive_ent size_t size; int64_t offset; + const QString entryPath = ArchiveEntryPaths::callWithSystemLocale + <QString>(ArchiveEntryPaths::pathname, entry); + + FileGuardLocker locker(entryPath, FileGuard::globalObject()); + status = archive_write_header(writer, entry); if (status != ARCHIVE_OK) { - setErrorString(QLatin1String(archive_error_string(writer))); + setErrorString(errorStringWithCode(writer)); return false; } forever { status = archive_read_data_block(reader, &buff, &size, &offset); - if (status == ARCHIVE_EOF) - return true; + if (status == ARCHIVE_EOF) { + status = archive_write_finish_entry(writer); + if (status == ARCHIVE_OK) + return true; + } if (status != ARCHIVE_OK) { - setErrorString(QLatin1String(archive_error_string(reader))); + setErrorString(errorStringWithCode(reader)); return false; } status = archive_write_data_block(writer, buff, size, offset); if (status != ARCHIVE_OK) { - setErrorString(QLatin1String(archive_error_string(writer))); + setErrorString(errorStringWithCode(writer)); return false; } } @@ -901,6 +1188,21 @@ QString LibArchiveArchive::pathWithoutNamespace(const QString &path) } /*! + Returns an error message and a textual representaion of the numeric error code + indicating the reason for the most recent error return for the \a archive object. +*/ +QString LibArchiveArchive::errorStringWithCode(archive *const archive) +{ + if (!archive) + return QString(); + + return QString::fromLatin1("%1: %2.").arg( + QLatin1String(archive_error_string(archive)), + QLatin1String(strerror(archive_errno(archive))) + ); +} + +/*! Returns the number of files in this archive. */ quint64 LibArchiveArchive::totalFiles() @@ -913,15 +1215,19 @@ quint64 LibArchiveArchive::totalFiles() try { int status = archiveReadOpenWithCallbacks(reader.get()); - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot open archive for reading: %1") + .arg(errorStringWithCode(reader.get()))); + } forever { - status = archive_read_next_header(reader.get(), &entry); + status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry); if (status == ARCHIVE_EOF) break; - if (status != ARCHIVE_OK) - throw Error(QLatin1String(archive_error_string(reader.get()))); + if (status != ARCHIVE_OK) { + throw Error(tr("Cannot read entry header: %1") + .arg(errorStringWithCode(reader.get()))); + } ++files; } |