Home > Net >  QZipReader extractAll issue
QZipReader extractAll issue

Time:05-09

I am using the old Qt - QZipReader class to unzip some zip file. It decompresses successfully only files. When zip file contains the directories with content, it displays this QIODevice::write issue:

QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\7zr.exe"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\aria2c.exe"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert.sh"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert_config_linux"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert_config_macos"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert_ve_plugin"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\test5\qt-unified-windows-x86-3.1.1-online.exe"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\uup-converter-wimlib.7z"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\test\yM37erm8.jpg"): device not open
QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\test\yNFatJN.jpg"): device not open

Code:

#if defined(Q_OS_WIN)
#  undef S_IFREG
#  define S_IFREG 0100000
#  ifndef S_ISDIR
#    define S_ISDIR(x) ((x) & S_IFDIR) > 0
#  endif
#  ifndef S_ISREG
#    define S_ISREG(x) ((x) & 0170000) == S_IFREG
#  endif
#  define S_IFLNK 020000
#  define S_ISLNK(x) ((x) & S_IFLNK) > 0
#  ifndef S_IRUSR
#    define S_IRUSR 0400
#  endif
#  ifndef S_IWUSR
#    define S_IWUSR 0200
#  endif
#  ifndef S_IXUSR
#    define S_IXUSR 0100
#  endif
#  define S_IRGRP 0040
#  define S_IWGRP 0020
#  define S_IXGRP 0010
#  define S_IROTH 0004
#  define S_IWOTH 0002
#  define S_IXOTH 0001
#endif

bool QZipReader::extractAll(const QString &destinationDir) const
    {
        QDir baseDir(destinationDir);
        // create directories first
        const QVector<FileInfo> allFiles = fileInfoList();
    //    for (FileInfo fi : allFiles) {
    //        if (fi.isDir) {
    //            const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath));
    
    //            if (!baseDir.mkpath(fi.filePath)) {
    //                return false;
    //            }
    //            if (!QFile::setPermissions(absPath, fi.permissions)) {
    //                return false;
    //            }
    //        }
    //    }
    
    //    // set up symlinks
    //    for (FileInfo fi : allFiles) {
    //        const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath));
    //        if (fi.isSymLink) {
    //            QString destination = QFile::decodeName(fileData(fi.filePath));
    //            if (destination.isEmpty())
    //                return false;
    //            QFileInfo linkFi(absPath);
    //            if (!QFile::exists(linkFi.absolutePath()))
    //                QDir::root().mkpath(linkFi.absolutePath());
    //            if (!QFile::link(destination, absPath))
    //                return false;
    //            /* cannot change permission of links
    //            if (!QFile::setPermissions(absPath, fi.permissions))
    //                return false;
    //            */
    //        }
    //    }
    
        for (FileInfo fi : allFiles) {
            if (!baseDir.exists()) {
                baseDir.mkpath(destinationDir);
            }
    
            const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath));
    
            if (fi.isDir) {
                baseDir.mkpath(absPath);
            }
    
            if (fi.isFile) {
                QFile f(absPath);
    
                if (!f.isOpen()) {
                    f.open(QIODevice::WriteOnly);
                }
    
                f.write(fileData(fi.filePath));
                f.setPermissions(fi.permissions);
                f.close();
            }
        }
    
        return true;
    }

QVector<QZipReader::FileInfo> QZipReader::fileInfoList() const
{
    d->scanFiles();
    QVector<QZipReader::FileInfo> files;

    for (int i = 0; i < d->fileHeaders.size();   i) {
        QZipReader::FileInfo fi;
        d->fillFileInfo(i, fi);
        files.append(fi);
    }

    return files;
}

void QZipReaderPrivate::scanFiles()
{
    if (!dirtyFileTree) {
        return;
    }

    if (!(device->isOpen() || device->open(QIODevice::ReadOnly))) {
        status = QZipReader::FileOpenError;
        return;
    }

    if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
        status = QZipReader::FileReadError;
        return;
    }

    dirtyFileTree = false;
    uchar tmp[4];
    device->read((char *)tmp, 4);

    if (readUInt(tmp) != 0x04034b50) {
        qWarning() << "QZip: not a zip file!";
        return;
    }

    // find EndOfDirectory header    
    int i = 0;
    int start_of_directory = -1;
    int num_dir_entries = 0;
    EndOfDirectory eod;
    while (start_of_directory == -1) {
      const int pos = device->size() - static_cast<int>(sizeof(EndOfDirectory)) - i;

      if (pos < 0 || i > 65535) {
          qWarning() << "QZip: EndOfDirectory not found";
          return;
      }

      device->seek(pos);
      device->read((char *)&eod, sizeof(EndOfDirectory));

      if (readUInt(eod.signature) == 0x06054b50) {
          //start_of_directory = pos;
          break;
      }

        i;
    }

    // have the eod
    start_of_directory = readUInt(eod.dir_start_offset);
    num_dir_entries = readUShort(eod.num_dir_entries);
    //qDebug() << "start_of_directory at: " << start_of_directory << " | num_dir_entries: " << num_dir_entries;
    int comment_length = readUShort(eod.comment_length);

    if (comment_length != i) {
        qWarning() << "QZip: failed to parse zip file.";
    }

    comment = device->read(qMin(comment_length, i));

    device->seek(start_of_directory);

    for (i = 0; i < num_dir_entries;   i) {
        FileHeader header;
        int read = device->read((char *) &header.h, sizeof(CentralFileHeader));

        if (read < (int)sizeof(CentralFileHeader)) {
            qWarning() << "QZip: Failed to read complete header, index may be incomplete";
            break;
        }

        if (readUInt(header.h.signature) != 0x02014b50) {
            qWarning() << "QZip: invalid header signature, index may be incomplete";
            break;
        }

        int l = readUShort(header.h.file_name_length);
        header.file_name = device->read(l);

        if (header.file_name.length() != l) {
            qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete";
            break;
        }

        l = readUShort(header.h.extra_field_length);
        header.extra_field = device->read(l);

        if (header.extra_field.length() != l) {
            qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
            break;
        }

        l = readUShort(header.h.file_comment_length);
        header.file_comment = device->read(l);

        if (header.file_comment.length() != l) {
            qWarning() << "QZip: Failed to read read file comment, index may be incomplete";
            break;
        }

        qDebug() << "Found file: " << header.file_name.data();
        fileHeaders.append(header);
    }
}

void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const
{
    FileHeader header = fileHeaders.at(index);
    fileInfo.filePath = QString::fromLocal8Bit(header.file_name);
    const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
    fileInfo.isDir = S_ISDIR(mode);
    fileInfo.isFile = S_ISREG(mode);

    qDebug() << "Mode: " << mode;
    qDebug() << "fileInfo.filePath: " << fileInfo.filePath << " isFile: " << fileInfo.isFile << " isDir: " << fileInfo.isDir;

    fileInfo.isSymLink = S_ISLNK(mode);
    fileInfo.permissions = modeToPermissions(mode);
    fileInfo.size = readUInt(header.h.uncompressed_size);
    fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
}

Console app:

#include <QCoreApplication>
#include <QDir>
#include <QDebug>
#include "qzipreader.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    qDebug() << "This is a QZip test..." << endl;
    QString zipFileName = "C:\\Users\\cobra\\Downloads\\22610.1_amd64_en-us_professional_00fb7ba0_convert.zip";
    QString outputDirPath = "C:\\Users\\cobra\\Downloads\\Output";
    QZipReader qZip(zipFileName);
    bool isExtracted = qZip.extractAll(outputDirPath);
    qDebug() << "isExtracted: " << isExtracted;
    qZip.close();
    system("Pause");
    return 0;
}

I have checked that everything in zip file is treated as files even directories with content, which leads to this QIODevice::write issue. The fileInfo.isDir returns false (0). Any ideas how to fix it? I can share more Qt code if needed. Thank you.

CodePudding user response:

So, I have fixed it by adding this line: baseDir.mkpath(QFileInfo(absPath).absoluteDir().path());

Code:

bool QZipReader::extractAll(const QString &destinationDir) const
{
    QDir baseDir(destinationDir);

    if (!baseDir.exists()) {
        baseDir.mkpath(destinationDir);
    }

    const QVector<FileInfo> allFiles = fileInfoList();

    for (FileInfo fi : allFiles) {
         const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath));
         baseDir.mkpath(QFileInfo(absPath).absoluteDir().path());

         if (fi.isFile) {
             QFile f(absPath);

             if (!f.isOpen()) {
                 f.open(QIODevice::WriteOnly);
             }

             f.write(fileData(fi.filePath));
             f.setPermissions(fi.permissions);
             f.close();
         }
    }

    return true;
}

Now, it creates all directories and extracts files. The issue is resolved. Thank you.

  • Related