Fixed tar extract

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2024-03-29 00:10:59 +02:00
parent 45028ddc61
commit 81874f22f7
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318

View File

@ -33,13 +33,11 @@
* limitations under the License. * limitations under the License.
*/ */
#include "Untar.h" #include "Untar.h"
#include <qfileinfo.h>
#include <qlogging.h>
#include <quagzipfile.h> #include <quagzipfile.h>
#include <QByteArray> #include <QByteArray>
#include <QFileInfo>
#include <QIODevice> #include <QIODevice>
#include <QString> #include <QString>
#include <cstdlib>
#include "FileSystem.h" #include "FileSystem.h"
// adaptation of the: // adaptation of the:
@ -69,40 +67,30 @@ enum class TypeFlag : char {
GNULongName = 'L', /* long file name */ GNULongName = 'L', /* long file name */
}; };
struct Header { /* byte offset */ // struct Header { /* byte offset */
char name[100]; /* 0 */ // char name[100]; /* 0 */
char mode[8]; /* 100 */ // char mode[8]; /* 100 */
char uid[8]; /* 108 */ // char uid[8]; /* 108 */
char gid[8]; /* 116 */ // char gid[8]; /* 116 */
char size[12]; /* 124 */ // char size[12]; /* 124 */
char mtime[12]; /* 136 */ // char mtime[12]; /* 136 */
char chksum[8]; /* 148 */ // char chksum[8]; /* 148 */
TypeFlag typeflag; /* 156 */ // TypeFlag typeflag; /* 156 */
char linkname[100]; /* 157 */ // char linkname[100]; /* 157 */
char magic[6]; /* 257 */ // char magic[6]; /* 257 */
char version[2]; /* 263 */ // char version[2]; /* 263 */
char uname[32]; /* 265 */ // char uname[32]; /* 265 */
char gname[32]; /* 297 */ // char gname[32]; /* 297 */
char devmajor[8]; /* 329 */ // char devmajor[8]; /* 329 */
char devminor[8]; /* 337 */ // char devminor[8]; /* 337 */
char prefix[155]; /* 345 */ // char prefix[155]; /* 345 */
/* 500 */ // /* 500 */
}; // };
union Buffer { bool readLonglink(QIODevice* in, qint64 size, QByteArray& longlink)
char buffer[BLOCKSIZE];
struct Header header;
};
bool readLonglink(QIODevice* in, Buffer& buffer, QByteArray& longlink)
{ {
qint64 n = 0; qint64 n = 0;
qint64 size = strtoll(buffer.header.size, NULL, 8);
size--; // ignore trailing null size--; // ignore trailing null
if (errno == ERANGE) {
qCritical() << "The filename size can't be read";
return false;
}
if (size < 0) { if (size < 0) {
qCritical() << "The filename size is negative"; qCritical() << "The filename size is negative";
return false; return false;
@ -119,36 +107,51 @@ bool readLonglink(QIODevice* in, Buffer& buffer, QByteArray& longlink)
return true; return true;
} }
int getOctal(char* buffer, int maxlenght, bool* ok)
{
return QByteArray(buffer, qstrnlen(buffer, maxlenght)).toInt(ok, 8);
}
QString decodeName(char* name)
{
return QFile::decodeName(QByteArray(name, qstrnlen(name, 100)));
}
bool Tar::extract(QIODevice* in, QString dst) bool Tar::extract(QIODevice* in, QString dst)
{ {
Buffer buffer; char buffer[BLOCKSIZE];
QString name, symlink, firstFolderName; QString name, symlink, firstFolderName;
bool doNotReset = false; bool doNotReset = false, ok;
while (true) { while (true) {
auto n = in->read(buffer.buffer, BLOCKSIZE); auto n = in->read(buffer, BLOCKSIZE);
if (n != BLOCKSIZE) { // allways expect complete blocks if (n != BLOCKSIZE) { // allways expect complete blocks
qCritical() << "The expected blocksize was not respected"; qCritical() << "The expected blocksize was not respected";
return false; return false;
} }
if (buffer.header.name[0] == 0) { // end of archive if (buffer[0] == 0) { // end of archive
return true; return true;
} }
int mode = strtol(buffer.header.mode, NULL, 8) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions int mode = getOctal(buffer + 100, 8, &ok) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions
if (errno == ERANGE) { if (!ok) {
qCritical() << "The file mode can't be read"; qCritical() << "The file mode can't be read";
return false; return false;
} }
// there are names that are exactly 100 bytes long // there are names that are exactly 100 bytes long
// and neither longlink nor \0 terminated (bug:101472) // and neither longlink nor \0 terminated (bug:101472)
if (name.isEmpty()) { if (name.isEmpty()) {
name = QFile::decodeName(QByteArray(buffer.header.name, qstrnlen(buffer.header.name, 100))); name = decodeName(buffer);
if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) { if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) {
name = name.mid(firstFolderName.size()); name = name.mid(firstFolderName.size());
} }
} }
if (symlink.isEmpty()) if (symlink.isEmpty())
symlink = QFile::decodeName(QByteArray(buffer.header.linkname, qstrnlen(buffer.header.linkname, 100))); symlink = decodeName(buffer);
switch (buffer.header.typeflag) { qint64 size = getOctal(buffer + 124, 12, &ok);
if (!ok) {
qCritical() << "The file size can't be read";
return false;
}
switch (TypeFlag(buffer[156])) {
case TypeFlag::Regular: case TypeFlag::Regular:
/* fallthrough */ /* fallthrough */
case TypeFlag::ARegular: { case TypeFlag::ARegular: {
@ -163,11 +166,6 @@ bool Tar::extract(QIODevice* in, QString dst)
return false; return false;
} }
out.setPermissions(QFile::Permissions(mode)); out.setPermissions(QFile::Permissions(mode));
qint64 size = strtoll(buffer.header.size, NULL, 8);
if (errno == ERANGE) {
qCritical() << "The file size can't be read";
return false;
}
while (size > 0) { while (size > 0) {
QByteArray tmp(BLOCKSIZE, 0); QByteArray tmp(BLOCKSIZE, 0);
n = in->read(tmp.data(), BLOCKSIZE); n = in->read(tmp.data(), BLOCKSIZE);
@ -196,7 +194,7 @@ bool Tar::extract(QIODevice* in, QString dst)
case TypeFlag::GNULongLink: { case TypeFlag::GNULongLink: {
doNotReset = true; doNotReset = true;
QByteArray longlink; QByteArray longlink;
if (readLonglink(in, buffer, longlink)) { if (readLonglink(in, size, longlink)) {
symlink = QFile::decodeName(longlink.constData()); symlink = QFile::decodeName(longlink.constData());
} else { } else {
qCritical() << "Failed to read long link"; qCritical() << "Failed to read long link";
@ -207,7 +205,7 @@ bool Tar::extract(QIODevice* in, QString dst)
case TypeFlag::GNULongName: { case TypeFlag::GNULongName: {
doNotReset = true; doNotReset = true;
QByteArray longlink; QByteArray longlink;
if (readLonglink(in, buffer, longlink)) { if (readLonglink(in, size, longlink)) {
name = QFile::decodeName(longlink.constData()); name = QFile::decodeName(longlink.constData());
} else { } else {
qCritical() << "Failed to read long name"; qCritical() << "Failed to read long name";