]> git.mxchange.org Git - simgear.git/commitdiff
Tests for un-tar code.
authorJames Turner <zakalawe@mac.com>
Wed, 15 Jun 2016 21:27:01 +0000 (22:27 +0100)
committerRoland Haeder <roland@mxchange.org>
Sat, 13 Aug 2016 08:21:16 +0000 (10:21 +0200)
simgear/io/CMakeLists.txt
simgear/io/test.tar.gz [new file with mode: 0644]
simgear/io/test2.tar [new file with mode: 0644]
simgear/io/test_untar.cxx [new file with mode: 0644]
simgear/io/untar.cxx
simgear/io/untar.hxx

index a4cf2c8fd9145d27a29cc09b878ce3ea81b5f3ca..fa0d3fc601ec2838097c03e32e6cf3e153b98e99 100644 (file)
@@ -83,4 +83,12 @@ add_executable(test_repository test_repository.cxx)
 target_link_libraries(test_repository ${TEST_LIBS})
 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)
diff --git a/simgear/io/test.tar.gz b/simgear/io/test.tar.gz
new file mode 100644 (file)
index 0000000..22f2a5c
Binary files /dev/null and b/simgear/io/test.tar.gz differ
diff --git a/simgear/io/test2.tar b/simgear/io/test2.tar
new file mode 100644 (file)
index 0000000..e219daa
Binary files /dev/null and b/simgear/io/test2.tar differ
diff --git a/simgear/io/test_untar.cxx b/simgear/io/test_untar.cxx
new file mode 100644 (file)
index 0000000..dc88806
--- /dev/null
@@ -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;
+}
index c2e55b92f4b6fa14d7cda58d87c3d4f6d9c13e18..b65dd4a631b5687d30aa201efe91f49c18534583 100644 (file)
@@ -63,7 +63,7 @@ typedef struct
     const size_t TAR_HEADER_BLOCK_SIZE = 512;
 
 #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 TVERSLEN 2
 
@@ -106,10 +106,12 @@ public:
     z_stream zlibStream;
     uint8_t* zlibOutput;
     bool haveInitedZLib;
+    bool uncompressedData; // set if reading a plain .tar (not tar.gz)
     uint8_t* headerPtr;
 
     TarExtractorPrivate() :
-        haveInitedZLib(false)
+        haveInitedZLib(false),
+        uncompressedData(false)
     {
     }
 
@@ -277,6 +279,7 @@ TarExtractor::TarExtractor(const SGPath& rootPath) :
 
 TarExtractor::~TarExtractor()
 {
+
 }
 
 void TarExtractor::extractBytes(const char* bytes, size_t count)
@@ -289,46 +292,63 @@ void TarExtractor::extractBytes(const char* bytes, size_t count)
     d->zlibStream.avail_in = count;
 
     if (!d->haveInitedZLib) {
-        if (inflateInit2(&d->zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
-            SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
-            d->state = TarExtractorPrivate::BAD_DATA;
-            return;
-        } else {
-            d->haveInitedZLib = true;
-            d->setState(TarExtractorPrivate::READING_HEADER);
-        }
-    }
-    
-    size_t writtenSize;
-
-    // loop, running zlib() inflate and sending output bytes to
-    // our request body handler. Keep calling inflate until no bytes are
-    // written, and ZLIB has consumed all available input
-    do {
-        d->zlibStream.next_out = d->zlibOutput;
-        d->zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
-        int result = inflate(&d->zlibStream, Z_NO_FLUSH);
-        if (result == Z_OK || result == Z_STREAM_END) {
-            // nothing to do
-
-        } else if (result == Z_BUF_ERROR) {
-            // transient error, fall through
+        // 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) {
+                SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
+                d->state = TarExtractorPrivate::BAD_DATA;
+                return;
+            }
         } else {
-            //  _error = result;
-            SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << d->zlibStream.msg);
-            d->state = TarExtractorPrivate::BAD_DATA;
-            return;
-        }
+            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;
+            }
 
-        writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - d->zlibStream.avail_out;
-        if (writtenSize > 0) {
-            d->processBytes((const char*) d->zlibOutput, writtenSize);
+            d->uncompressedData = true;
         }
 
-        if (result == Z_STREAM_END) {
-            break;
-        }
-    } while ((d->zlibStream.avail_in > 0) || (writtenSize > 0));
+        d->haveInitedZLib = true;
+        d->setState(TarExtractorPrivate::READING_HEADER);
+    } // of init on first-bytes case
+
+    if (d->uncompressedData) {
+        d->processBytes(bytes, count);
+    } else {
+        size_t writtenSize;
+        // loop, running zlib() inflate and sending output bytes to
+        // our request body handler. Keep calling inflate until no bytes are
+        // written, and ZLIB has consumed all available input
+        do {
+            d->zlibStream.next_out = d->zlibOutput;
+            d->zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
+            int result = inflate(&d->zlibStream, Z_NO_FLUSH);
+            if (result == Z_OK || result == Z_STREAM_END) {
+                // nothing to do
+
+            } else if (result == Z_BUF_ERROR) {
+                // transient error, fall through
+            } else {
+                //  _error = result;
+                SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << d->zlibStream.msg);
+                d->state = TarExtractorPrivate::BAD_DATA;
+                return;
+            }
+
+            writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - d->zlibStream.avail_out;
+            if (writtenSize > 0) {
+                d->processBytes((const char*) d->zlibOutput, writtenSize);
+            }
+
+            if (result == Z_STREAM_END) {
+                break;
+            }
+        } while ((d->zlibStream.avail_in > 0) || (writtenSize > 0));
+    } // of Zlib-compressed data
 }
 
 bool TarExtractor::isAtEndOfArchive() const
@@ -341,4 +361,58 @@ bool TarExtractor::hasError() const
     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
index b14da6ff90bda1f0a6df61c87eb13e4567ae536a..254d8548557403d3cc1ed489f7d516b6021f511f 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <memory>
 
+#include <cstdlib>
 #include <simgear/misc/sg_path.hxx>
 
 namespace simgear
@@ -33,6 +34,8 @@ public:
     TarExtractor(const SGPath& rootPath);
        ~TarExtractor();
 
+    static bool isTarData(const uint8_t* bytes, size_t count);
+
     void extractBytes(const char* bytes, size_t count);
 
     bool isAtEndOfArchive() const;