Skip to content

Commit

Permalink
Improve symlink handling
Browse files Browse the repository at this point in the history
First, symlink zipping in JlCompress was broken terribly:
it created symlinks, but their target was the contents
of the file they should point at, not its path. This is
fixed now.

Second, symlink unzipping wasn't implemented at all.
JlCompress now properly creates symlinks when extracting,
at least on Linux. On Windows it will probably end up
creating shortcuts, but that needs to be tested further.

And lastly, the convenience method QuaZipFileInfo64::isSymbolicLink()
was added.
  • Loading branch information
stachenov committed Oct 5, 2020
1 parent aa7902c commit e226541
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 13 deletions.
4 changes: 4 additions & 0 deletions NEWS.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
QuaZip changes

* Current
* Fixed symlink handling in JlCompress compression
* Implemented symlink handling in JlCompress extraction

* 2020-10-05 1.0
* Preliminary Qt 6 support
* Consistent naming of binaries (libquazip1-qt5 style)
Expand Down
36 changes: 25 additions & 11 deletions quazip/JlCompress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,31 @@ bool JlCompress::compressFile(QuaZip* zip, QString fileName, QString fileDest) {
zip->getMode()!=QuaZip::mdAppend &&
zip->getMode()!=QuaZip::mdAdd) return false;

// Apro il file originale
QFile inFile;
inFile.setFileName(fileName);
if(!inFile.open(QIODevice::ReadOnly)) return false;

// Apro il file risulato
QuaZipFile outFile(zip);
if(!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()))) return false;

// Copio i dati
if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK) {
return false;
if(!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, fileName))) return false;

QFileInfo input(fileName);
if (quazip_is_symlink(input)) {
// Not sure if we should use any specialized codecs here.
// After all, a symlink IS just a byte array. And
// this is mostly for Link, where UTF-8 is ubiquitous these days.
QString path = quazip_symlink_target(input);
QString relativePath = input.dir().relativeFilePath(path);
outFile.write(QFile::encodeName(relativePath));
} else {
QFile inFile;
inFile.setFileName(fileName);
if (!inFile.open(QIODevice::ReadOnly))
return false;
if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK)
return false;
inFile.close();
}

// Chiudo i file
outFile.close();
if (outFile.getZipError()!=UNZ_OK) return false;
inFile.close();

return true;
}
Expand Down Expand Up @@ -167,6 +174,13 @@ bool JlCompress::extractFile(QuaZip* zip, QString fileName, QString fileDest) {
return true;
}

if (info.isSymbolicLink()) {
QString target = QFile::decodeName(inFile.readAll());
if (!QFile::link(target, fileDest))
return false;
return true;
}

// Apro il file risultato
QFile outFile;
outFile.setFileName(fileDest);
Expand Down
25 changes: 25 additions & 0 deletions quazip/quazip_qt_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,31 @@ inline QDateTime quazip_ctime(const QFileInfo &fi) {
}
#endif

// this is just a slightly better alternative
#include <QFileInfo>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
inline bool quazip_is_symlink(const QFileInfo &fi) {
return fi.isSymbolicLink();
}
#else
inline bool quazip_is_symlink(const QFileInfo &fi) {
// also detects *.lnk on Windows, but better than nothing
return fi.isSymLink();
}
#endif

// I'm not even sure what this one is, but nevertheless
#include <QFileInfo>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
inline QString quazip_symlink_target(const QFileInfo &fi) {
return fi.symLinkTarget();
}
#else
inline QString quazip_symlink_target(const QFileInfo &fi) {
return fi.readLink(); // What's the difference? I've no idea.
}
#endif

// this is not a deprecation but an improvement, for a change
#include <QDateTime>
#if (QT_VERSION >= 0x040700)
Expand Down
6 changes: 6 additions & 0 deletions quazip/quazipfileinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ QFile::Permissions QuaZipFileInfo64::getPermissions() const
return permissionsFromExternalAttr(externalAttr);
}

bool QuaZipFileInfo64::isSymbolicLink() const
{
quint32 uPerm = (externalAttr & 0xFFFF0000u) >> 16;
return (uPerm & 0170000) == 0120000;
}

bool QuaZipFileInfo64::toQuaZipFileInfo(QuaZipFileInfo &info) const
{
bool noOverflow = true;
Expand Down
6 changes: 6 additions & 0 deletions quazip/quazipfileinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ struct QUAZIP_EXPORT QuaZipFileInfo64 {
QFile::Permissions.
*/
QFile::Permissions getPermissions() const;
/// Checks whether the file is a symbolic link.
/**
Returns true iff the highest 16 bits of the external attributes
indicate that the file is a symbolic link according to Unix file mode.
*/
bool isSymbolicLink() const;
/// Converts to QuaZipFileInfo
/**
If any of the fields are greater than 0xFFFFFFFFu, they are set to
Expand Down
4 changes: 2 additions & 2 deletions quazip/quazipnewinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file):
dateTime = QDateTime::currentDateTime();
} else {
dateTime = lm;
QuaZipNewInfo_setPermissions(this, info.permissions(), info.isDir(), info.isSymLink());
QuaZipNewInfo_setPermissions(this, info.permissions(), info.isDir(), quazip_is_symlink(info));
}
}

Expand All @@ -117,7 +117,7 @@ void QuaZipNewInfo::setFilePermissions(const QString &file)
{
QFileInfo info = QFileInfo(file);
QFile::Permissions perm = info.permissions();
QuaZipNewInfo_setPermissions(this, perm, info.isDir(), info.isSymLink());
QuaZipNewInfo_setPermissions(this, perm, info.isDir(), quazip_is_symlink(info));
}

void QuaZipNewInfo::setPermissions(QFile::Permissions permissions)
Expand Down
2 changes: 2 additions & 0 deletions qztest/qztest.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ see quazip/(un)zip.h files for details. Basically it's the zlib license.
#include <QStringList>
#include <QTextCodec>

#include <quazip_qt_compat.h>

extern bool createTestFiles(const QStringList &fileNames,
int size = -1,
const QString &dir = "tmp");
Expand Down
24 changes: 24 additions & 0 deletions qztest/testjlcompress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ see quazip/(un)zip.h files for details. Basically it's the zlib license.
#include "qztest.h"

#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTextCodec>

Expand Down Expand Up @@ -413,3 +414,26 @@ void TestJlCompress::zeroPermissions()
curDir.remove("zero.zip");
curDir.remove("zero.txt");
}

#ifdef QUAZIP_SYMLINK_TEST

void TestJlCompress::symlinkHandling()
{
QStringList fileNames { "file.txt" };
if (!createTestFiles(fileNames)) {
QFAIL("Couldn't create test files");
}
QVERIFY(QFile::link("file.txt", "tmp/link.txt"));
fileNames << "link.txt";
QVERIFY(JlCompress::compressDir("symlink.zip", "tmp"));
QDir curDir;
QVERIFY(curDir.mkpath("extsymlink"));
QVERIFY(!JlCompress::extractDir("symlink.zip", "extsymlink").isEmpty());
QFileInfo linkInfo("extsymlink/link.txt");
QVERIFY(quazip_is_symlink(linkInfo));
removeTestFiles(fileNames, "extsymlink");
removeTestFiles(fileNames, "tmp");
curDir.remove("symlink.zip");
}

#endif
7 changes: 7 additions & 0 deletions qztest/testjlcompress.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ see quazip/(un)zip.h files for details. Basically it's the zlib license.

#include <QObject>

#ifdef Q_OS_UNIX
#define QUAZIP_SYMLINK_TEST
#endif

class TestJlCompress: public QObject {
Q_OBJECT
private slots:
Expand All @@ -43,6 +47,9 @@ private slots:
void extractDir_data();
void extractDir();
void zeroPermissions();
#ifdef QUAZIP_SYMLINK_TEST
void symlinkHandling();
#endif
};

#endif // QUAZIP_TEST_JLCOMPRESS_H

0 comments on commit e226541

Please sign in to comment.