1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/util/zip_reader.h"
18
19 #include <cstdint>
20 #include <cstring>
21 #include <ctime>
22 #include <string>
23 #include <vector>
24
25 #include "perfetto/base/build_config.h"
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/string_view.h"
28 #include "perfetto/trace_processor/trace_blob.h"
29 #include "perfetto/trace_processor/trace_blob_view.h"
30 #include "src/base/test/status_matchers.h"
31 #include "test/gtest_and_gmock.h"
32
33 namespace perfetto::trace_processor::util {
34 namespace {
35 using base::gtest_matchers::IsError;
36
37 // This zip file contains the following:
38 // Zip file size: 386 bytes, number of entries: 2
39 // -rw-r--r-- 3.0 unx 4 tx stor 22-Jul-25 16:43 stored_file
40 // -rw-r--r-- 3.0 unx 89 tx defN 22-Jul-25 18:34 dir/deflated_file
41 // 2 files, 92 bytes uncompressed, 52 bytes compressed: 43.5%
42 //
43 // /stored_file content: "foo"
44 // dir/deflated_file content: 2x "The quick brown fox jumps over the lazy dog\n"
45 const uint8_t kTestZip[] = {
46 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x85,
47 0xf9, 0x54, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
48 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64,
49 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x54, 0x09, 0x00, 0x03, 0x17, 0xba,
50 0xde, 0x62, 0x44, 0xba, 0xde, 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04,
51 0xce, 0x69, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f,
52 0x0a, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x47,
53 0x94, 0xf9, 0x54, 0xf2, 0x03, 0x92, 0x3c, 0x34, 0x00, 0x00, 0x00, 0x59,
54 0x00, 0x00, 0x00, 0x11, 0x00, 0x1c, 0x00, 0x64, 0x69, 0x72, 0x2f, 0x64,
55 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65,
56 0x55, 0x54, 0x09, 0x00, 0x03, 0x15, 0xd4, 0xde, 0x62, 0xf4, 0xba, 0xde,
57 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xce, 0x69, 0x02, 0x00, 0x04,
58 0x00, 0x00, 0x00, 0x00, 0x0b, 0xc9, 0x48, 0x55, 0x28, 0x2c, 0xcd, 0x4c,
59 0xce, 0x56, 0x48, 0x2a, 0xca, 0x2f, 0xcf, 0x53, 0x48, 0xcb, 0xaf, 0x50,
60 0xc8, 0x2a, 0xcd, 0x2d, 0x28, 0x56, 0xc8, 0x2f, 0x4b, 0x2d, 0x52, 0x28,
61 0x01, 0x4a, 0xe7, 0x24, 0x56, 0x55, 0x2a, 0xa4, 0xe4, 0xa7, 0x73, 0x85,
62 0x10, 0xa9, 0x36, 0xad, 0x08, 0xa8, 0x18, 0x00, 0x50, 0x4b, 0x01, 0x02,
63 0x1e, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x85, 0xf9, 0x54,
64 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
65 0x0b, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
66 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64,
67 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x54, 0x05, 0x00, 0x03, 0x17, 0xba,
68 0xde, 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xce, 0x69, 0x02, 0x00,
69 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x14,
70 0x00, 0x00, 0x00, 0x08, 0x00, 0x47, 0x94, 0xf9, 0x54, 0xf2, 0x03, 0x92,
71 0x3c, 0x34, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x11, 0x00, 0x18,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x49,
73 0x00, 0x00, 0x00, 0x64, 0x69, 0x72, 0x2f, 0x64, 0x65, 0x66, 0x6c, 0x61,
74 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x54, 0x05, 0x00,
75 0x03, 0x15, 0xd4, 0xde, 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xce,
76 0x69, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06,
77 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0xa8, 0x00, 0x00, 0x00,
78 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00};
79
vec2str(const std::vector<uint8_t> & vec)80 std::string vec2str(const std::vector<uint8_t>& vec) {
81 return {reinterpret_cast<const char*>(vec.data()), vec.size()};
82 }
83
ValidateTestZip(ZipReader & zr)84 void ValidateTestZip(ZipReader& zr) {
85 ASSERT_EQ(zr.files().size(), 2u);
86
87 std::vector<uint8_t> dec;
88 ASSERT_EQ(zr.files()[0].name(), "stored_file");
89 ASSERT_EQ(zr.files()[0].GetDatetimeStr(), "2022-07-25 16:43:20");
90
91 ASSERT_EQ(zr.files()[1].name(), "dir/deflated_file");
92 ASSERT_EQ(zr.files()[1].GetDatetimeStr(), "2022-07-25 18:34:14");
93
94 // This file is STORE-d and doesn't require any decompression.
95 auto res = zr.files()[0].Decompress(&dec);
96 ASSERT_TRUE(res.ok()) << res.message();
97 ASSERT_EQ(dec.size(), 4u);
98 ASSERT_EQ(vec2str(dec), "foo\n");
99
100 // This file is DEFLATE-d and requires zlib.
101 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
102 res = zr.files()[1].Decompress(&dec);
103 ASSERT_TRUE(res.ok()) << res.message();
104 ASSERT_EQ(dec.size(), 89u);
105 ASSERT_EQ(vec2str(dec),
106 "The quick brown fox jumps over the lazy dog\n"
107 "The quick brown fox jumps over the lazy frog\n");
108 #endif
109 }
110
TEST(ZipReaderTest,ValidZip_OneShotParse)111 TEST(ZipReaderTest, ValidZip_OneShotParse) {
112 ZipReader zr;
113 ASSERT_OK(
114 zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, sizeof(kTestZip)))));
115 ValidateTestZip(zr);
116 }
117
TEST(ZipReaderTest,ValidZip_OneByteChunks)118 TEST(ZipReaderTest, ValidZip_OneByteChunks) {
119 ZipReader zr;
120 for (auto i : kTestZip) {
121 ASSERT_OK(zr.Parse(TraceBlobView(TraceBlob::CopyFrom(&i, 1))));
122 }
123 ValidateTestZip(zr);
124 }
125
TEST(ZipReaderTest,MalformedZip_InvalidSignature)126 TEST(ZipReaderTest, MalformedZip_InvalidSignature) {
127 ZipReader zr;
128 uint8_t content[sizeof(kTestZip)];
129 memcpy(content, kTestZip, sizeof(kTestZip));
130 content[0] = 0xff; // Invalid signature
131 ASSERT_THAT(
132 zr.Parse(TraceBlobView(TraceBlob::CopyFrom(content, sizeof(kTestZip)))),
133 IsError());
134 ASSERT_EQ(zr.files().size(), 0u);
135 }
136
TEST(ZipReaderTest,MalformedZip_VersionTooHigh)137 TEST(ZipReaderTest, MalformedZip_VersionTooHigh) {
138 ZipReader zr;
139 uint8_t content[sizeof(kTestZip)];
140 memcpy(content, kTestZip, sizeof(kTestZip));
141 content[5] = 9; // Version: 9.0
142 ASSERT_THAT(
143 zr.Parse(TraceBlobView(TraceBlob::CopyFrom(content, sizeof(kTestZip)))),
144 IsError());
145 ASSERT_EQ(zr.files().size(), 0u);
146 }
147
TEST(ZipReaderTest,TruncatedZip)148 TEST(ZipReaderTest, TruncatedZip) {
149 ZipReader zr;
150 ASSERT_OK(zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, 40))));
151 ASSERT_EQ(zr.files().size(), 0u);
152 }
153
TEST(ZipReaderTest,Find)154 TEST(ZipReaderTest, Find) {
155 ZipReader zr;
156 ASSERT_OK(
157 zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, sizeof(kTestZip)))));
158 ASSERT_EQ(zr.Find("stored_file")->name(), "stored_file");
159 ASSERT_EQ(zr.Find("dir/deflated_file")->name(), "dir/deflated_file");
160 ASSERT_EQ(nullptr, zr.Find("stored_f"));
161 ASSERT_EQ(nullptr, zr.Find("_file*"));
162 ASSERT_EQ(nullptr, zr.Find("dirz/deflated_file"));
163 }
164
165 // All the tests below require zlib.
166 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
167
TEST(ZipReaderTest,ValidZip_DecompressLines)168 TEST(ZipReaderTest, ValidZip_DecompressLines) {
169 ZipReader zr;
170 ASSERT_OK(
171 zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, sizeof(kTestZip)))));
172 ValidateTestZip(zr);
173 int num_callbacks = 0;
174 zr.files()[1].DecompressLines(
175 [&](const std::vector<base::StringView>& lines) {
176 ASSERT_EQ(num_callbacks++, 0);
177 ASSERT_TRUE(lines.size() == 2);
178 ASSERT_EQ(lines[0].ToStdString(),
179 "The quick brown fox jumps over the lazy dog");
180 ASSERT_EQ(lines[1].ToStdString(),
181 "The quick brown fox jumps over the lazy frog");
182 });
183
184 ASSERT_EQ(num_callbacks, 1);
185 }
186
TEST(ZipReaderTest,MalformedZip_DecomprError)187 TEST(ZipReaderTest, MalformedZip_DecomprError) {
188 ZipReader zr;
189 uint8_t content[sizeof(kTestZip)];
190 memcpy(content, kTestZip, sizeof(kTestZip));
191
192 // The 2nd file header starts at 103, the payload at 30 (header) + 17 (fname)
193 // bytes later. We start clobbering at offset=150, so the header is intanct
194 // but decompression fails.
195 memset(&content[150], 0, 40);
196 ASSERT_OK(
197 zr.Parse(TraceBlobView(TraceBlob::CopyFrom(content, sizeof(kTestZip)))));
198 ASSERT_EQ(zr.files().size(), 2u);
199 std::vector<uint8_t> ignored;
200 ASSERT_OK(zr.files()[0].Decompress(&ignored));
201 ASSERT_THAT(zr.files()[1].Decompress(&ignored), IsError());
202 }
203
204 #endif // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
205
206 } // namespace
207 } // namespace perfetto::trace_processor::util
208