#include #include #include #include "tar.h" #define BLOCKSIZE 512 #define BLOCKCOUNT(size) \ ((size)/BLOCKSIZE + ((size) % BLOCKSIZE > 0 ? 1 : 0)) struct TARHeader { char name[100]; /* 0 */ char mode[8]; /* 100 */ char uid[8]; /* 108 */ char gid[8]; /* 116 */ char size[12]; /* 124 */ char mtime[12]; /* 136 */ char chksum[8]; /* 148 */ char typeflag; /* 156 */ char linkname[100]; /* 157 */ char magic[6]; /* 257 */ char version[2]; /* 263 */ char uname[32]; /* 265 */ char gname[32]; /* 297 */ char devmajor[8]; /* 329 */ char devminor[8]; /* 337 */ char prefix[155]; /* 345 */ /* 500 */ }; static quint64 number(const char* data, size_t size, int base = 8) { const char *sp; quint64 val = 0; for (sp = data; sp < data + size; sp++) if (isdigit(*sp)) break; for (; sp < data + size && isdigit(*sp); sp++) val = val * base + *sp - '0'; return val; } bool Tar::open() { if (!_file.open(QIODevice::ReadOnly)) { _errorString = _file.errorString(); return false; } if (!_index.isEmpty()) return true; QFileInfo fi(_file.fileName()); QString tmiPath = fi.path() + "/" + fi.completeBaseName() + ".tmi"; if (loadTmi(tmiPath)) return true; else return loadTar(); } bool Tar::loadTar() { char buffer[BLOCKSIZE]; TARHeader *hdr = (TARHeader*)&buffer; quint64 size; qint64 ret; while ((ret = _file.read(buffer, BLOCKSIZE))) { if (ret < BLOCKSIZE) { _file.close(); _index.clear(); _errorString = "Error reading TAR header block"; return false; } size = number(hdr->size, sizeof(hdr->size)); _index.insert(hdr->name, _file.pos() / BLOCKSIZE - 1); if (!_file.seek(_file.pos() + BLOCKCOUNT(size) * BLOCKSIZE)) { _file.close(); _index.clear(); _errorString = "Error skipping data blocks"; return false; } } return true; } bool Tar::loadTmi(const QString &path) { quint64 block; int ln = 1; QFile file(path); if (!file.open(QIODevice::ReadOnly)) return false; while (!file.atEnd()) { QByteArray line(file.readLine(4096)); int pos = line.indexOf(':'); if (line.size() < 10 || pos < 7 || !line.startsWith("block")) { qWarning("%s:%d: syntax error", qPrintable(path), ln); _index.clear(); return false; } block = number(line.constData() + 6, line.size() - 6, 10); QString file(line.mid(pos + 1).trimmed()); _index.insert(file, block); ln++; } return true; } QByteArray Tar::file(const QString &name) { char buffer[BLOCKSIZE]; TARHeader *hdr = (TARHeader*)&buffer; quint64 size; QMap::const_iterator it(_index.find(name)); if (it == _index.constEnd()) return QByteArray(); Q_ASSERT(_file.isOpen()); if (_file.seek(it.value() * BLOCKSIZE)) { if (_file.read(buffer, BLOCKSIZE) < BLOCKSIZE) return QByteArray(); size = number(hdr->size, sizeof(hdr->size)); return _file.read(size); } else return QByteArray(); }