mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 01:54:20 +03:00
Fix weird utf archive (#5186)
This commit is contained in:
commit
a3c5f1f6f2
2 changed files with 75 additions and 47 deletions
|
|
@ -24,6 +24,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
|
@ -36,25 +37,36 @@ QStringList ArchiveReader::getFiles()
|
||||||
bool ArchiveReader::collectFiles(bool onlyFiles)
|
bool ArchiveReader::collectFiles(bool onlyFiles)
|
||||||
{
|
{
|
||||||
return parse([this, onlyFiles](File* f) {
|
return parse([this, onlyFiles](File* f) {
|
||||||
if (!onlyFiles || f->isFile())
|
if (!onlyFiles || f->isFile()) {
|
||||||
m_fileNames << f->filename();
|
m_fileNames << f->filename();
|
||||||
|
}
|
||||||
return f->skip();
|
return f->skip();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using getPathFunc = std::function<const char*(archive_entry*)>;
|
||||||
|
static QString decodeLibArchivePath(archive_entry* entry, const getPathFunc& getUtf8Path, const getPathFunc& getPath)
|
||||||
|
{
|
||||||
|
auto fileName = QString::fromUtf8(getUtf8Path(entry));
|
||||||
|
if (fileName.isEmpty()) {
|
||||||
|
fileName = QString::fromLocal8Bit(getPath(entry));
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
QString ArchiveReader::File::filename()
|
QString ArchiveReader::File::filename()
|
||||||
{
|
{
|
||||||
return QString::fromUtf8(archive_entry_pathname_utf8(m_entry));
|
return decodeLibArchivePath(m_entry, archive_entry_pathname_utf8, archive_entry_pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ArchiveReader::File::readAll(int* outStatus)
|
QByteArray ArchiveReader::File::readAll(int* outStatus)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
const void* buff;
|
const void* buff = nullptr;
|
||||||
size_t size;
|
size_t size = 0;
|
||||||
la_int64_t offset;
|
la_int64_t offset = 0;
|
||||||
|
|
||||||
int status;
|
int status = 0;
|
||||||
while ((status = archive_read_data_block(m_archive.get(), &buff, &size, &offset)) == ARCHIVE_OK) {
|
while ((status = archive_read_data_block(m_archive.get(), &buff, &size, &offset)) == ARCHIVE_OK) {
|
||||||
data.append(static_cast<const char*>(buff), static_cast<qsizetype>(size));
|
data.append(static_cast<const char*>(buff), static_cast<qsizetype>(size));
|
||||||
}
|
}
|
||||||
|
|
@ -80,10 +92,10 @@ int ArchiveReader::File::readNextHeader()
|
||||||
return archive_read_next_header(m_archive.get(), &m_entry);
|
return archive_read_next_header(m_archive.get(), &m_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ArchiveReader::goToFile(QString filename) -> std::unique_ptr<File>
|
auto ArchiveReader::goToFile(const QString& filename) -> std::unique_ptr<File>
|
||||||
{
|
{
|
||||||
auto f = std::make_unique<File>();
|
auto f = std::make_unique<File>();
|
||||||
auto a = f->m_archive.get();
|
auto* a = f->m_archive.get();
|
||||||
archive_read_support_format_all(a);
|
archive_read_support_format_all(a);
|
||||||
archive_read_support_filter_all(a);
|
archive_read_support_filter_all(a);
|
||||||
auto fileName = m_archivePath.toStdWString();
|
auto fileName = m_archivePath.toStdWString();
|
||||||
|
|
@ -105,15 +117,16 @@ auto ArchiveReader::goToFile(QString filename) -> std::unique_ptr<File>
|
||||||
|
|
||||||
static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = false)
|
static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = false)
|
||||||
{
|
{
|
||||||
int r;
|
int r = 0;
|
||||||
const void* buff;
|
const void* buff = nullptr;
|
||||||
size_t size;
|
size_t size = 0;
|
||||||
la_int64_t offset;
|
la_int64_t offset = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
r = archive_read_data_block(ar, &buff, &size, &offset);
|
r = archive_read_data_block(ar, &buff, &size, &offset);
|
||||||
if (r == ARCHIVE_EOF)
|
if (r == ARCHIVE_EOF) {
|
||||||
return (ARCHIVE_OK);
|
return ARCHIVE_OK;
|
||||||
|
}
|
||||||
if (r < ARCHIVE_OK) {
|
if (r < ARCHIVE_OK) {
|
||||||
qCritical() << "Failed reading data block:" << archive_error_string(ar);
|
qCritical() << "Failed reading data block:" << archive_error_string(ar);
|
||||||
return (r);
|
return (r);
|
||||||
|
|
@ -130,39 +143,43 @@ static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = fal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool willEscapeRoot(const QDir& root, archive_entry* entry)
|
static bool willEscapeRoot(const QDir& root, archive_entry* entry)
|
||||||
{
|
{
|
||||||
const char* entryPathC = archive_entry_pathname(entry);
|
auto entryPath = decodeLibArchivePath(entry, archive_entry_pathname_utf8, archive_entry_pathname);
|
||||||
const char* linkTargetC = archive_entry_symlink(entry);
|
auto linkTarget = decodeLibArchivePath(entry, archive_entry_symlink_utf8, archive_entry_symlink);
|
||||||
const char* hardlinkC = archive_entry_hardlink(entry);
|
auto hardLink = decodeLibArchivePath(entry, archive_entry_hardlink_utf8, archive_entry_hardlink);
|
||||||
|
|
||||||
if (!entryPathC || (!linkTargetC && !hardlinkC))
|
if (entryPath.isEmpty() || (linkTarget.isEmpty() && hardLink.isEmpty())) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QString entryPath = QString::fromUtf8(entryPathC);
|
bool isHardLink = false;
|
||||||
QString linkTarget = linkTargetC ? QString::fromUtf8(linkTargetC) : QString::fromUtf8(hardlinkC);
|
if (isHardLink = linkTarget.isEmpty(); isHardLink) {
|
||||||
|
linkTarget = hardLink;
|
||||||
|
}
|
||||||
|
|
||||||
QString linkFullPath = root.filePath(entryPath);
|
QString linkFullPath = root.filePath(entryPath);
|
||||||
auto rootDir = QUrl::fromLocalFile(root.absolutePath());
|
auto rootDir = QUrl::fromLocalFile(root.absolutePath());
|
||||||
|
|
||||||
if (!rootDir.isParentOf(QUrl::fromLocalFile(linkFullPath)))
|
if (!rootDir.isParentOf(QUrl::fromLocalFile(linkFullPath))) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QDir linkDir = QFileInfo(linkFullPath).dir();
|
QDir linkDir = QFileInfo(linkFullPath).dir();
|
||||||
if (!QDir::isAbsolutePath(linkTarget)) {
|
if (!QDir::isAbsolutePath(linkTarget)) {
|
||||||
linkTarget = (linkTargetC ? linkDir : root).filePath(linkTarget);
|
linkTarget = (!isHardLink ? linkDir : root).filePath(linkTarget);
|
||||||
}
|
}
|
||||||
return !rootDir.isParentOf(QUrl::fromLocalFile(QDir::cleanPath(linkTarget)));
|
return !rootDir.isParentOf(QUrl::fromLocalFile(QDir::cleanPath(linkTarget)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, bool notBlock)
|
bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName, bool notBlock)
|
||||||
{
|
{
|
||||||
return writeFile(out, targetFileName, {}, notBlock);
|
return writeFile(out, targetFileName, {}, notBlock);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, std::optional<QDir> root, bool notBlock)
|
bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName, std::optional<QDir> root, bool notBlock)
|
||||||
{
|
{
|
||||||
auto entry = m_entry;
|
auto* entry = m_entry;
|
||||||
std::unique_ptr<archive_entry, decltype(&archive_entry_free)> entryClone(nullptr, &archive_entry_free);
|
std::unique_ptr<archive_entry, decltype(&archive_entry_free)> entryClone(nullptr, &archive_entry_free);
|
||||||
if (!targetFileName.isEmpty()) {
|
if (!targetFileName.isEmpty()) {
|
||||||
entryClone.reset(archive_entry_clone(m_entry));
|
entryClone.reset(archive_entry_clone(m_entry));
|
||||||
|
|
@ -175,25 +192,29 @@ bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, std::o
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (archive_write_header(out, entry) < ARCHIVE_OK) {
|
if (archive_write_header(out, entry) < ARCHIVE_OK) {
|
||||||
qCritical() << "Failed to write header to entry:" << filename() << "-" << archive_error_string(out);
|
qCritical() << "Failed to write header to entry:" << filename() << "-" << archive_error_string(out) << targetFileName;
|
||||||
return false;
|
|
||||||
} else if (archive_entry_size(m_entry) > 0) {
|
|
||||||
auto r = copy_data(m_archive.get(), out, notBlock);
|
|
||||||
if (r < ARCHIVE_OK)
|
|
||||||
qCritical() << "Failed reading data block:" << archive_error_string(out);
|
|
||||||
if (r < ARCHIVE_WARN)
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (archive_entry_size(m_entry) > 0) {
|
||||||
|
auto r = copy_data(m_archive.get(), out, notBlock);
|
||||||
|
if (r < ARCHIVE_OK) {
|
||||||
|
qCritical() << "Failed reading data block:" << archive_error_string(out);
|
||||||
|
}
|
||||||
|
if (r < ARCHIVE_WARN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto r = archive_write_finish_entry(out);
|
auto r = archive_write_finish_entry(out);
|
||||||
if (r < ARCHIVE_OK)
|
if (r < ARCHIVE_OK) {
|
||||||
qCritical() << "Failed to finish writing entry:" << archive_error_string(out);
|
qCritical() << "Failed to finish writing entry:" << archive_error_string(out);
|
||||||
|
}
|
||||||
return (r >= ARCHIVE_WARN);
|
return (r >= ARCHIVE_WARN);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArchiveReader::parse(std::function<bool(File*, bool&)> doStuff)
|
bool ArchiveReader::parse(const std::function<bool(File*, bool&)>& doStuff)
|
||||||
{
|
{
|
||||||
auto f = std::make_unique<File>();
|
auto f = std::make_unique<File>();
|
||||||
auto a = f->m_archive.get();
|
auto* a = f->m_archive.get();
|
||||||
archive_read_support_format_all(a);
|
archive_read_support_format_all(a);
|
||||||
archive_read_support_filter_all(a);
|
archive_read_support_filter_all(a);
|
||||||
auto fileName = m_archivePath.toStdWString();
|
auto fileName = m_archivePath.toStdWString();
|
||||||
|
|
@ -217,7 +238,7 @@ bool ArchiveReader::parse(std::function<bool(File*, bool&)> doStuff)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArchiveReader::parse(std::function<bool(File*)> doStuff)
|
bool ArchiveReader::parse(const std::function<bool(File*)>& doStuff)
|
||||||
{
|
{
|
||||||
return parse([doStuff](File* f, bool&) { return doStuff(f); });
|
return parse([doStuff](File* f, bool&) { return doStuff(f); });
|
||||||
}
|
}
|
||||||
|
|
@ -241,27 +262,33 @@ QString ArchiveReader::getZipName()
|
||||||
|
|
||||||
bool ArchiveReader::exists(const QString& filePath) const
|
bool ArchiveReader::exists(const QString& filePath) const
|
||||||
{
|
{
|
||||||
if (filePath == QLatin1String("/") || filePath.isEmpty())
|
if (filePath == QLatin1String("/") || filePath.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
// Normalize input path (remove trailing slash, if any)
|
// Normalize input path (remove trailing slash, if any)
|
||||||
QString normalizedPath = QDir::cleanPath(filePath);
|
QString normalizedPath = QDir::cleanPath(filePath);
|
||||||
if (normalizedPath.startsWith('/'))
|
if (normalizedPath.startsWith('/')) {
|
||||||
normalizedPath.remove(0, 1);
|
normalizedPath.remove(0, 1);
|
||||||
if (normalizedPath == QLatin1String("."))
|
}
|
||||||
|
if (normalizedPath == QLatin1String(".")) {
|
||||||
return true;
|
return true;
|
||||||
if (normalizedPath == QLatin1String(".."))
|
}
|
||||||
|
if (normalizedPath == QLatin1String("..")) {
|
||||||
return false; // root only
|
return false; // root only
|
||||||
|
}
|
||||||
|
|
||||||
// Check for exact file match
|
// Check for exact file match
|
||||||
if (m_fileNames.contains(normalizedPath, Qt::CaseInsensitive))
|
if (m_fileNames.contains(normalizedPath, Qt::CaseInsensitive)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for directory existence by seeing if any file starts with that path
|
// Check for directory existence by seeing if any file starts with that path
|
||||||
QString dirPath = normalizedPath + QLatin1Char('/');
|
QString dirPath = normalizedPath + QLatin1Char('/');
|
||||||
for (const QString& f : m_fileNames) {
|
for (const QString& f : m_fileNames) {
|
||||||
if (f.startsWith(dirPath, Qt::CaseInsensitive))
|
if (f.startsWith(dirPath, Qt::CaseInsensitive)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
struct archive;
|
struct archive;
|
||||||
struct archive_entry;
|
struct archive_entry;
|
||||||
|
|
@ -30,7 +31,7 @@ namespace MMCZip {
|
||||||
class ArchiveReader {
|
class ArchiveReader {
|
||||||
public:
|
public:
|
||||||
using ArchivePtr = std::unique_ptr<struct archive, int (*)(struct archive*)>;
|
using ArchivePtr = std::unique_ptr<struct archive, int (*)(struct archive*)>;
|
||||||
ArchiveReader(QString fileName) : m_archivePath(fileName) {}
|
explicit ArchiveReader(QString fileName) : m_archivePath(std::move(fileName)) {}
|
||||||
virtual ~ArchiveReader() = default;
|
virtual ~ArchiveReader() = default;
|
||||||
|
|
||||||
QStringList getFiles();
|
QStringList getFiles();
|
||||||
|
|
@ -50,8 +51,8 @@ class ArchiveReader {
|
||||||
|
|
||||||
QByteArray readAll(int* outStatus = nullptr);
|
QByteArray readAll(int* outStatus = nullptr);
|
||||||
bool skip();
|
bool skip();
|
||||||
bool writeFile(archive* out, QString targetFileName = "", bool notBlock = false);
|
bool writeFile(archive* out, const QString& targetFileName = "", bool notBlock = false);
|
||||||
bool writeFile(archive* out, QString targetFileName, std::optional<QDir> root, bool notBlock = false);
|
bool writeFile(archive* out, const QString& targetFileName, std::optional<QDir> root, bool notBlock = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int readNextHeader();
|
int readNextHeader();
|
||||||
|
|
@ -62,14 +63,14 @@ class ArchiveReader {
|
||||||
archive_entry* m_entry;
|
archive_entry* m_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<File> goToFile(QString filename);
|
std::unique_ptr<File> goToFile(const QString& filename);
|
||||||
bool parse(std::function<bool(File*)>);
|
bool parse(const std::function<bool(File*)>&);
|
||||||
bool parse(std::function<bool(File*, bool&)>);
|
bool parse(const std::function<bool(File*, bool&)>&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_archivePath;
|
QString m_archivePath;
|
||||||
size_t m_blockSize = 10240;
|
size_t m_blockSize = 10240;
|
||||||
|
|
||||||
QStringList m_fileNames = {};
|
QStringList m_fileNames;
|
||||||
};
|
};
|
||||||
} // namespace MMCZip
|
} // namespace MMCZip
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue