Tests for un-tar code.

This commit is contained in:
James Turner 2016-06-15 22:27:01 +01:00
parent f824cf85a4
commit e695505e62
6 changed files with 180 additions and 37 deletions

View File

@ -83,4 +83,12 @@ add_executable(test_repository test_repository.cxx)
target_link_libraries(test_repository ${TEST_LIBS}) target_link_libraries(test_repository ${TEST_LIBS})
add_test(http_repository ${EXECUTABLE_OUTPUT_PATH}/test_repository) add_test(http_repository ${EXECUTABLE_OUTPUT_PATH}/test_repository)
add_executable(test_untar test_untar.cxx)
set_target_properties(test_untar PROPERTIES
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
target_link_libraries(test_untar ${TEST_LIBS})
add_test(untar ${EXECUTABLE_OUTPUT_PATH}/test_untar)
endif(ENABLE_TESTS) endif(ENABLE_TESTS)

BIN
simgear/io/test.tar.gz Normal file

Binary file not shown.

BIN
simgear/io/test2.tar Normal file

Binary file not shown.

58
simgear/io/test_untar.cxx Normal file
View File

@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////////////////
// Test harness.
////////////////////////////////////////////////////////////////////////
#include <simgear/compiler.h>
#include <iostream>
#include "untar.hxx"
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/sg_file.hxx>
using std::cout;
using std::cerr;
using std::endl;
using namespace simgear;
void testTarGz()
{
SGPath p = SGPath(SRC_DIR);
p.append("test.tar.gz");
SGBinaryFile f(p.str());
f.open(SG_IO_IN);
uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
VERIFY(TarExtractor::isTarData(buf, bufSize));
}
void testPlainTar()
{
SGPath p = SGPath(SRC_DIR);
p.append("test2.tar");
SGBinaryFile f(p.str());
f.open(SG_IO_IN);
uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
VERIFY(TarExtractor::isTarData(buf, bufSize));
}
int main (int ac, char ** av)
{
testTarGz();
testPlainTar();
return 0;
}

View File

@ -63,7 +63,7 @@ typedef struct
const size_t TAR_HEADER_BLOCK_SIZE = 512; const size_t TAR_HEADER_BLOCK_SIZE = 512;
#define TMAGIC "ustar" /* ustar and a null */ #define TMAGIC "ustar" /* ustar and a null */
#define TMAGLEN 6 #define TMAGLEN 5 // 5, not 6, becuase some files use 'ustar '
#define TVERSION "00" /* 00 and no null */ #define TVERSION "00" /* 00 and no null */
#define TVERSLEN 2 #define TVERSLEN 2
@ -106,10 +106,12 @@ public:
z_stream zlibStream; z_stream zlibStream;
uint8_t* zlibOutput; uint8_t* zlibOutput;
bool haveInitedZLib; bool haveInitedZLib;
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
uint8_t* headerPtr; uint8_t* headerPtr;
TarExtractorPrivate() : TarExtractorPrivate() :
haveInitedZLib(false) haveInitedZLib(false),
uncompressedData(false)
{ {
} }
@ -277,6 +279,7 @@ TarExtractor::TarExtractor(const SGPath& rootPath) :
TarExtractor::~TarExtractor() TarExtractor::~TarExtractor()
{ {
} }
void TarExtractor::extractBytes(const char* bytes, size_t count) void TarExtractor::extractBytes(const char* bytes, size_t count)
@ -289,18 +292,34 @@ void TarExtractor::extractBytes(const char* bytes, size_t count)
d->zlibStream.avail_in = count; d->zlibStream.avail_in = count;
if (!d->haveInitedZLib) { if (!d->haveInitedZLib) {
// now we have data, see if we're dealing with GZ-compressed data or not
uint8_t* ubytes = (uint8_t*) bytes;
if ((ubytes[0] == 0x1f) && (ubytes[1] == 0x8b)) {
// GZIP identification bytes
if (inflateInit2(&d->zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) { if (inflateInit2(&d->zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed"); SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
d->state = TarExtractorPrivate::BAD_DATA; d->state = TarExtractorPrivate::BAD_DATA;
return; return;
}
} else { } else {
UstarHeaderBlock* header = (UstarHeaderBlock*) bytes;
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "didn't find tar magic in header");
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
d->uncompressedData = true;
}
d->haveInitedZLib = true; d->haveInitedZLib = true;
d->setState(TarExtractorPrivate::READING_HEADER); d->setState(TarExtractorPrivate::READING_HEADER);
} } // of init on first-bytes case
}
if (d->uncompressedData) {
d->processBytes(bytes, count);
} else {
size_t writtenSize; size_t writtenSize;
// loop, running zlib() inflate and sending output bytes to // loop, running zlib() inflate and sending output bytes to
// our request body handler. Keep calling inflate until no bytes are // our request body handler. Keep calling inflate until no bytes are
// written, and ZLIB has consumed all available input // written, and ZLIB has consumed all available input
@ -329,6 +348,7 @@ void TarExtractor::extractBytes(const char* bytes, size_t count)
break; break;
} }
} while ((d->zlibStream.avail_in > 0) || (writtenSize > 0)); } while ((d->zlibStream.avail_in > 0) || (writtenSize > 0));
} // of Zlib-compressed data
} }
bool TarExtractor::isAtEndOfArchive() const bool TarExtractor::isAtEndOfArchive() const
@ -341,4 +361,58 @@ bool TarExtractor::hasError() const
return (d->state >= TarExtractorPrivate::ERROR_STATE); return (d->state >= TarExtractorPrivate::ERROR_STATE);
} }
bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
{
if (count < 2) {
return false;
}
UstarHeaderBlock* header = 0;
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
// GZIP identification bytes
z_stream z;
uint8_t* zlibOutput = static_cast<uint8_t*>(alloca(4096));
memset(&z, 0, sizeof(z_stream));
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.avail_out = 4096;
z.next_out = zlibOutput;
z.next_in = (uint8_t*) bytes;
z.avail_in = count;
if (inflateInit2(&z, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
return false;
}
int result = inflate(&z, Z_SYNC_FLUSH);
if (result != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result);
return false; // not tar data
}
size_t written = 4096 - z.avail_out;
if (written < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
return false;
}
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
} else {
// uncompressed tar
if (count < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
return false;
}
header = (UstarHeaderBlock*) bytes;
}
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "not a tar file");
return false;
}
return true;
}
} // of simgear } // of simgear

View File

@ -20,6 +20,7 @@
#include <memory> #include <memory>
#include <cstdlib>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
namespace simgear namespace simgear
@ -33,6 +34,8 @@ public:
TarExtractor(const SGPath& rootPath); TarExtractor(const SGPath& rootPath);
~TarExtractor(); ~TarExtractor();
static bool isTarData(const uint8_t* bytes, size_t count);
void extractBytes(const char* bytes, size_t count); void extractBytes(const char* bytes, size_t count);
bool isAtEndOfArchive() const; bool isAtEndOfArchive() const;