NOISSUE adapt to work on Windows, fix reading symlink targets on POSIX

This commit is contained in:
Petr Mrázek 2020-10-12 01:01:51 +02:00
parent f1284ab96b
commit dded11004f
4 changed files with 94 additions and 22 deletions

View File

@ -5,6 +5,12 @@
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QDebug> #include <QDebug>
#ifndef Q_OS_WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
namespace mojang_files { namespace mojang_files {
const Hash hash_of_empty_string = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; const Hash hash_of_empty_string = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
@ -177,6 +183,49 @@ Package Package::fromManifestFile(const QString & filename) {
} }
} }
#ifndef Q_OS_WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
namespace {
// FIXME: Qt obscures symlink targets by making them absolute. that is useless. this is the workaround - we do it ourselves
bool actually_read_symlink_target(const QString & filepath, Path & out)
{
struct ::stat st;
// FIXME: here, we assume the native filesystem encoding. May the Gods have mercy upon our Souls.
QByteArray nativePath = filepath.toUtf8();
const char * filepath_cstr = nativePath.data();
if (lstat(filepath_cstr, &st) != 0)
{
return false;
}
auto size = st.st_size ? st.st_size + 1 : PATH_MAX;
std::string temp(size, '\0');
// because we don't realiably know how long the damn thing actually is, we loop and expand. POSIX is naff
do
{
auto link_length = ::readlink(filepath_cstr, &temp[0], temp.size());
if(link_length == -1)
{
return false;
}
if(std::string::size_type(link_length) < temp.size())
{
// buffer was long enough and we managed to read the link target. RETURN here.
temp.resize(link_length);
out = Path(QString::fromUtf8(temp.c_str()));
return true;
}
temp.resize(temp.size() * 2);
} while (true);
}
}
#endif
// FIXME: Qt filesystem abstraction is bad, but ... let's hope it doesn't break too much? // FIXME: Qt filesystem abstraction is bad, but ... let's hope it doesn't break too much?
// FIXME: The error handling is just DEFICIENT // FIXME: The error handling is just DEFICIENT
Package Package::fromInspectedFolder(const QString& folderPath) Package Package::fromInspectedFolder(const QString& folderPath)
@ -190,10 +239,22 @@ Package Package::fromInspectedFolder(const QString& folderPath)
auto fileInfo = iterator.fileInfo(); auto fileInfo = iterator.fileInfo();
auto relPath = root.relativeFilePath(fileInfo.filePath()); auto relPath = root.relativeFilePath(fileInfo.filePath());
// FIXME: this is probably completely busted on Windows anyway, so just disable it.
// Qt makes shit up and doesn't understand the platform details
// TODO: Actually use a filesystem library that isn't terrible and has decen license.
// I only know one, and I wrote it. Sadly, currently proprietary. PAIN.
#ifndef Q_OS_WIN32
if(fileInfo.isSymLink()) { if(fileInfo.isSymLink()) {
out.addLink(relPath, fileInfo.symLinkTarget()); Path targetPath;
if(!actually_read_symlink_target(fileInfo.filePath(), targetPath)) {
qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
out.valid = false;
}
out.addLink(relPath, targetPath);
} }
else if(fileInfo.isDir()) { else
#endif
if(fileInfo.isDir()) {
out.addFolder(relPath); out.addFolder(relPath);
} }
else if(fileInfo.isFile()) { else if(fileInfo.isFile()) {

View File

@ -20,8 +20,9 @@ private slots:
void test_parse(); void test_parse();
void test_parse_file(); void test_parse_file();
void test_inspect(); void test_inspect();
void test_diff(); #ifndef Q_OS_WIN32
void test_inspect_symlinks();
#endif
void mkdir_deep(); void mkdir_deep();
void rmdir_deep(); void rmdir_deep();
@ -87,7 +88,31 @@ void PackageManifestTest::test_parse_file() {
QVERIFY(manifest.valid == true); QVERIFY(manifest.valid == true);
} }
void PackageManifestTest::test_inspect() { void PackageManifestTest::test_inspect() {
auto path = QFINDTESTDATA("testdata/inspect_win/");
auto manifest = Package::fromInspectedFolder(path);
QVERIFY(manifest.valid == true);
QVERIFY(manifest.files.size() == 2);
QVERIFY(manifest.files.count(Path("a/b.txt")));
auto &file1 = manifest.files[Path("a/b.txt")];
QVERIFY(file1.executable == false);
QVERIFY(file1.hash == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
QVERIFY(file1.size == 0);
QVERIFY(manifest.files.count(Path("a/b/b.txt")));
auto &file2 = manifest.files[Path("a/b/b.txt")];
QVERIFY(file2.executable == false);
QVERIFY(file2.hash == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
QVERIFY(file2.size == 0);
QVERIFY(manifest.folders.size() == 3);
QVERIFY(manifest.folders.count(Path(".")));
QVERIFY(manifest.folders.count(Path("a")));
QVERIFY(manifest.folders.count(Path("a/b")));
QVERIFY(manifest.symlinks.size() == 0);
}
#ifndef Q_OS_WIN32
void PackageManifestTest::test_inspect_symlinks() {
auto path = QFINDTESTDATA("testdata/inspect/"); auto path = QFINDTESTDATA("testdata/inspect/");
auto manifest = Package::fromInspectedFolder(path); auto manifest = Package::fromInspectedFolder(path);
QVERIFY(manifest.valid == true); QVERIFY(manifest.valid == true);
@ -102,25 +127,11 @@ void PackageManifestTest::test_inspect() {
QVERIFY(manifest.folders.count(Path("a"))); QVERIFY(manifest.folders.count(Path("a")));
QVERIFY(manifest.folders.count(Path("a/b"))); QVERIFY(manifest.folders.count(Path("a/b")));
QVERIFY(manifest.symlinks.size() == 1); QVERIFY(manifest.symlinks.size() == 1);
QVERIFY(manifest.symlinks.count(Path("a/b/b.txt")));
qDebug() << manifest.symlinks[Path("a/b/b.txt")];
QVERIFY(manifest.symlinks[Path("a/b/b.txt")] == Path("../b.txt"));
} }
#endif
void PackageManifestTest::test_diff() {
auto path = QFINDTESTDATA("testdata/inspect/");
auto from = Package::fromInspectedFolder(path);
auto to = Package::fromManifestContents(basic_manifest);
auto operations = UpdateOperations::resolve(from, to);
QVERIFY(operations.valid == true);
QVERIFY(operations.mkdirs.size() == 1);
QVERIFY(operations.mkdirs[0] == Path("a/b/c"));
QVERIFY(operations.rmdirs.size() == 0);
QVERIFY(operations.deletes.size() == 1);
QVERIFY(operations.deletes[0] == Path("a/b/b.txt"));
QVERIFY(operations.downloads.size() == 0);
QVERIFY(operations.mklinks.size() == 1);
QVERIFY(operations.mklinks.count(Path("a/b/c.txt")));
QVERIFY(operations.mklinks[Path("a/b/c.txt")] == Path("../b.txt"));
}
void PackageManifestTest::mkdir_deep() { void PackageManifestTest::mkdir_deep() {

View File

View File