Home | History | Annotate | Download | only in src
      1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <unistd.h>
      6 
      7 #include <vector>
      8 
      9 #include "gtest/gtest.h"
     10 
     11 #include "puffin/src/file_stream.h"
     12 #include "puffin/src/include/puffin/common.h"
     13 #include "puffin/src/include/puffin/utils.h"
     14 #include "puffin/src/memory_stream.h"
     15 #include "puffin/src/unittest_common.h"
     16 
     17 namespace puffin {
     18 
     19 using std::string;
     20 using std::vector;
     21 
     22 namespace {
     23 const uint8_t kZipEntries[] = {
     24     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xfc, 0x88,
     25     0x28, 0x4c, 0xcb, 0x86, 0xe1, 0x80, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00,
     26     0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x31, 0x55, 0x54, 0x09, 0x00, 0x03,
     27     0xec, 0x15, 0x54, 0x5a, 0x49, 0x10, 0x54, 0x5a, 0x75, 0x78, 0x0b, 0x00,
     28     0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33,
     29     0x34, 0x84, 0x00, 0x2e, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02,
     30     0x00, 0x08, 0x00, 0x01, 0x89, 0x28, 0x4c, 0xe0, 0xe8, 0x6f, 0x6d, 0x06,
     31     0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32,
     32     0x55, 0x54, 0x09, 0x00, 0x03, 0xf1, 0x15, 0x54, 0x5a, 0x38, 0x10, 0x54,
     33     0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04,
     34     0x88, 0x13, 0x00, 0x00, 0x33, 0x32, 0x82, 0x01, 0x2e, 0x00};
     35 
     36 // (echo "666666" > 2 && zip -fd test.zip 2 &&
     37 //  cat test.zip | hexdump -v -e '10/1 "0x%02x, " "\n"')
     38 const uint8_t kZipEntryWithDataDescriptor[] = {
     39     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74,
     40     0x2b, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
     41     0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03,
     42     0xf5, 0xe5, 0x57, 0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00,
     43     0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33,
     44     0x33, 0x03, 0x01, 0x2e, 0x00, 0x50, 0x4b, 0x07, 0x08, 0xb4, 0xa0, 0xf2,
     45     0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03,
     46     0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74, 0x2b, 0x4c, 0x00,
     47     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01,
     48     0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03, 0xf5, 0xe5, 0x57,
     49     0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f,
     50     0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33, 0x33, 0x03, 0x01,
     51     0x2e, 0x00, 0xb4, 0xa0, 0xf2, 0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00,
     52     0x00, 0x00};
     53 
     54 // echo "0123456789" > test1.txt && echo "9876543210" > test2.txt &&
     55 // gzip -kf test1.txt test2.txt && cat test1.txt.gz test2.txt.gz |
     56 // hexdump -v -e '12/1 "0x%02x, " "\n"'
     57 const uint8_t kGzipEntryWithMultipleMembers[] = {
     58     0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84, 0x5a, 0x00, 0x03, 0x74, 0x65,
     59     0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30, 0x34, 0x32,
     60     0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1, 0xe5, 0x76,
     61     0x40, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84,
     62     0x5a, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x32, 0x2e, 0x74, 0x78, 0x74,
     63     0x00, 0xb3, 0xb4, 0x30, 0x37, 0x33, 0x35, 0x31, 0x36, 0x32, 0x34, 0xe0,
     64     0x02, 0x00, 0x20, 0x9c, 0x5f, 0x89, 0x0b, 0x00, 0x00, 0x00};
     65 
     66 // echo "0123456789" > test1.txt && gzip -kf test1.txt && cat test1.txt.gz |
     67 // hexdump -v -e '12/1 "0x%02x, " "\n"'
     68 // And manually insert extra field with two byte length (10) followed by:
     69 // echo "extrafield" | hexdump -v -e '12/1 "0x%02x, " "\n"'
     70 // Then change the forth byte of array to -x0c to enable the extra field.
     71 const uint8_t kGzipEntryWithExtraField[] = {
     72     0x1f, 0x8b, 0x08, 0x0c, 0xcf, 0x0e, 0x86, 0x5a, 0x00, 0x03,
     73     // Extra field begin
     74     0x0A, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x66, 0x69, 0x65, 0x6c, 0x64,
     75     // Extra field end
     76     0x74, 0x65, 0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30,
     77     0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1,
     78     0xe5, 0x76, 0x40, 0x0b, 0x00, 0x00, 0x00};
     79 
     80 // echo "0123456789" | zlib-flate -compress |
     81 // hexdump -v -e '12/1 "0x%02x, " "\n"'
     82 const uint8_t kZlibEntry[] = {
     83     0x78, 0x9c, 0x33, 0x30, 0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0,
     84     0xe4, 0x02, 0x00, 0x0d, 0x17, 0x02, 0x18};
     85 
     86 void FindDeflatesInZlibBlocks(const Buffer& src,
     87                               const vector<ByteExtent>& zlibs,
     88                               const vector<BitExtent>& deflates) {
     89   string tmp_file;
     90   ASSERT_TRUE(MakeTempFile(&tmp_file, nullptr));
     91   ScopedPathUnlinker unlinker(tmp_file);
     92   auto src_stream = FileStream::Open(tmp_file, false, true);
     93   ASSERT_TRUE(src_stream);
     94   ASSERT_TRUE(src_stream->Write(src.data(), src.size()));
     95   ASSERT_TRUE(src_stream->Close());
     96 
     97   vector<BitExtent> deflates_out;
     98   ASSERT_TRUE(LocateDeflatesInZlibBlocks(tmp_file, zlibs, &deflates_out));
     99   ASSERT_EQ(deflates, deflates_out);
    100 }
    101 
    102 void CheckFindPuffLocation(const Buffer& compressed,
    103                            const vector<BitExtent>& deflates,
    104                            const vector<ByteExtent>& expected_puffs,
    105                            uint64_t expected_puff_size) {
    106   auto src = MemoryStream::CreateForRead(compressed);
    107   vector<ByteExtent> puffs;
    108   uint64_t puff_size;
    109   ASSERT_TRUE(FindPuffLocations(src, deflates, &puffs, &puff_size));
    110   EXPECT_EQ(puffs, expected_puffs);
    111   EXPECT_EQ(puff_size, expected_puff_size);
    112 }
    113 }  // namespace
    114 
    115 // Test Simple Puffing of the source.
    116 TEST(UtilsTest, FindPuffLocations1Test) {
    117   CheckFindPuffLocation(kDeflates8, kSubblockDeflateExtents8, kPuffExtents8,
    118                         kPuffs8.size());
    119 }
    120 
    121 TEST(UtilsTest, FindPuffLocations2Test) {
    122   CheckFindPuffLocation(kDeflates9, kSubblockDeflateExtents9, kPuffExtents9,
    123                         kPuffs9.size());
    124 }
    125 
    126 TEST(UtilsTest, LocateDeflatesInZlib) {
    127   Buffer zlib_data(kZlibEntry, std::end(kZlibEntry));
    128   vector<ByteExtent> deflates;
    129   EXPECT_TRUE(LocateDeflatesInZlib(zlib_data, &deflates));
    130   EXPECT_EQ(static_cast<size_t>(1), deflates.size());
    131   EXPECT_EQ(ByteExtent(2, 13), deflates[0]);
    132 }
    133 
    134 TEST(UtilsTest, LocateDeflatesInEmptyZlib) {
    135   Buffer empty;
    136   vector<ByteExtent> empty_zlibs;
    137   vector<BitExtent> empty_deflates;
    138   FindDeflatesInZlibBlocks(empty, empty_zlibs, empty_deflates);
    139 }
    140 
    141 TEST(UtilsTest, LocateDeflatesInZlibWithInvalidFields) {
    142   Buffer zlib_data(kZlibEntry, std::end(kZlibEntry));
    143   auto cmf = zlib_data[0];
    144   auto flag = zlib_data[1];
    145 
    146   vector<ByteExtent> deflates;
    147   zlib_data[0] = cmf & 0xF0;
    148   EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
    149   zlib_data[0] = cmf | (8 << 4);
    150   EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
    151   zlib_data[0] = cmf;  // Correct it.
    152 
    153   zlib_data[1] = flag & 0xF0;
    154   EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
    155 }
    156 
    157 TEST(UtilsTest, LocateDeflatesInZipArchiveSmoke) {
    158   Buffer zip_entries(kZipEntries, std::end(kZipEntries));
    159   vector<ByteExtent> deflates;
    160   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates));
    161   EXPECT_EQ(static_cast<size_t>(2), deflates.size());
    162   EXPECT_EQ(ByteExtent(59, 6), deflates[0]);
    163   EXPECT_EQ(ByteExtent(124, 6), deflates[1]);
    164 }
    165 
    166 TEST(UtilsTest, LocateDeflatesInZipArchiveWithDataDescriptor) {
    167   Buffer zip_entries(kZipEntryWithDataDescriptor,
    168                      std::end(kZipEntryWithDataDescriptor));
    169   vector<ByteExtent> deflates;
    170   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates));
    171   EXPECT_EQ(static_cast<size_t>(2), deflates.size());
    172   EXPECT_EQ(ByteExtent(59, 6), deflates[0]);
    173   EXPECT_EQ(ByteExtent(140, 6), deflates[1]);
    174 }
    175 
    176 TEST(UtilsTest, LocateDeflatesInZipArchiveErrorChecks) {
    177   Buffer zip_entries(kZipEntries, std::end(kZipEntries));
    178   // Construct a invalid zip entry whose size overflows.
    179   zip_entries[29] = 0xff;
    180   vector<ByteExtent> deflates_overflow;
    181   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_overflow));
    182   EXPECT_EQ(static_cast<size_t>(1), deflates_overflow.size());
    183   EXPECT_EQ(ByteExtent(124, 6), deflates_overflow[0]);
    184 
    185   zip_entries.resize(128);
    186   vector<ByteExtent> deflates_incomplete;
    187   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_incomplete));
    188   EXPECT_EQ(static_cast<size_t>(0), deflates_incomplete.size());
    189 }
    190 
    191 TEST(UtilsTest, LocateDeflatesInGzip) {
    192   Buffer gzip_data(kGzipEntryWithMultipleMembers,
    193                    std::end(kGzipEntryWithMultipleMembers));
    194   vector<ByteExtent> deflates;
    195   EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
    196   EXPECT_EQ(static_cast<size_t>(2), deflates.size());
    197   EXPECT_EQ(ByteExtent(20, 13), deflates[0]);
    198   EXPECT_EQ(ByteExtent(61, 13), deflates[1]);
    199 }
    200 
    201 TEST(UtilsTest, LocateDeflatesInGzipWithExtraField) {
    202   Buffer gzip_data(kGzipEntryWithExtraField,
    203                    std::end(kGzipEntryWithExtraField));
    204   vector<ByteExtent> deflates;
    205   EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
    206   EXPECT_EQ(static_cast<size_t>(1), deflates.size());
    207   EXPECT_EQ(ByteExtent(32, 13), deflates[0]);
    208 }
    209 
    210 }  // namespace puffin
    211