1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <fcntl.h>
16 #include <unistd.h>
17
18 #include <fstream>
19 #include <string>
20
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "contrib/zstd/sandboxed.h"
24 #include "contrib/zstd/utils/utils_zstd.h"
25 #include "sandboxed_api/util/path.h"
26 #include "sandboxed_api/util/status_matchers.h"
27 #include "sandboxed_api/util/temp_file.h"
28
29 namespace {
30
31 using ::sapi::IsOk;
32
GetTestFilePath(const std::string & filename)33 std::string GetTestFilePath(const std::string& filename) {
34 return sapi::file::JoinPath(getenv("TEST_FILES_DIR"), filename);
35 }
36
CompareFiles(const std::string & name1,const std::string & name2)37 bool CompareFiles(const std::string& name1, const std::string& name2) {
38 std::ifstream f1(name1, std::ios::binary);
39 if (!f1.is_open()) {
40 return false;
41 }
42
43 std::ifstream f2(name2, std::ios::binary);
44 if (!f2.is_open()) {
45 return false;
46 }
47
48 while (!f1.eof() && !f2.eof()) {
49 char buf1[128];
50 char buf2[128];
51
52 f1.read(buf1, sizeof(buf1));
53 f2.read(buf2, sizeof(buf2));
54
55 if (f1.gcount() != f2.gcount()) {
56 return false;
57 }
58 if (memcmp(&buf1, &buf2, f2.gcount()) != 0) {
59 return false;
60 }
61 }
62
63 return f1.eof() == f2.eof();
64 }
65
TEST(SandboxTest,CheckVersion)66 TEST(SandboxTest, CheckVersion) {
67 ZstdSapiSandbox sandbox;
68 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
69 ZstdApi api = ZstdApi(&sandbox);
70
71 absl::StatusOr<unsigned> version = api.ZSTD_versionNumber();
72 ASSERT_THAT(version, IsOk())
73 << "fatal error when invoking ZSTD_versionNumber";
74
75 ASSERT_GE(*version, 10000);
76 }
77
TEST(SandboxTest,CheckMinCLevel)78 TEST(SandboxTest, CheckMinCLevel) {
79 ZstdSapiSandbox sandbox;
80 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
81 ZstdApi api = ZstdApi(&sandbox);
82
83 absl::StatusOr<int> level = api.ZSTD_minCLevel();
84 ASSERT_THAT(level, IsOk()) << "fatal error when invoking ZSTD_minCLevel";
85
86 ASSERT_LT(*level, 0);
87 }
88
TEST(SandboxTest,CheckMaxCLevel)89 TEST(SandboxTest, CheckMaxCLevel) {
90 ZstdSapiSandbox sandbox;
91 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
92 ZstdApi api = ZstdApi(&sandbox);
93
94 absl::StatusOr<int> level = api.ZSTD_maxCLevel();
95 ASSERT_THAT(level, IsOk()) << "fatal error when invoking ZSTD_maxCLevel";
96
97 ASSERT_GT(*level, 0);
98 }
99
TEST(SandboxTest,CheckCompressInMemory)100 TEST(SandboxTest, CheckCompressInMemory) {
101 ZstdSapiSandbox sandbox;
102 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
103 ZstdApi api = ZstdApi(&sandbox);
104
105 std::string infile_s = GetTestFilePath("text");
106
107 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
108 sapi::CreateNamedTempFileAndClose("out.zstd"));
109 std::string outfile_s =
110 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
111
112 std::ifstream infile(infile_s, std::ios::binary);
113 ASSERT_TRUE(infile.is_open());
114
115 std::ofstream outfile(outfile_s, std::ios::binary);
116 ASSERT_TRUE(outfile.is_open());
117
118 absl::Status status = CompressInMemory(api, infile, outfile, 0);
119 ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
120
121 ASSERT_LT(outfile.tellp(), infile.tellg());
122 }
123
TEST(SandboxTest,CheckDecompressInMemory)124 TEST(SandboxTest, CheckDecompressInMemory) {
125 ZstdSapiSandbox sandbox;
126 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
127 ZstdApi api = ZstdApi(&sandbox);
128
129 std::string infile_s = GetTestFilePath("text.blob.zstd");
130
131 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
132 sapi::CreateNamedTempFileAndClose("out"));
133 std::string outfile_s =
134 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
135
136 std::ifstream infile(infile_s, std::ios::binary);
137 ASSERT_TRUE(infile.is_open());
138
139 std::ofstream outfile(outfile_s, std::ios::binary);
140 ASSERT_TRUE(outfile.is_open());
141
142 absl::Status status = DecompressInMemory(api, infile, outfile);
143 ASSERT_THAT(status, IsOk()) << "Unable to decompress file in memory";
144
145 ASSERT_GT(outfile.tellp(), infile.tellg());
146
147 ASSERT_TRUE(CompareFiles(GetTestFilePath("text"), outfile_s));
148 }
149
TEST(SandboxTest,CheckCompressAndDecompressInMemory)150 TEST(SandboxTest, CheckCompressAndDecompressInMemory) {
151 ZstdSapiSandbox sandbox;
152 absl::Status status;
153 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
154 ZstdApi api = ZstdApi(&sandbox);
155
156 std::string infile_s = GetTestFilePath("text");
157
158 SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
159 sapi::CreateNamedTempFileAndClose("middle.zstd"));
160 std::string middle_s =
161 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
162
163 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
164 sapi::CreateNamedTempFileAndClose("out"));
165 std::string outfile_s =
166 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
167
168 std::ifstream infile(infile_s, std::ios::binary);
169 ASSERT_TRUE(infile.is_open());
170
171 std::ofstream outmiddle(middle_s, std::ios::binary);
172 ASSERT_TRUE(outmiddle.is_open());
173
174 status = CompressInMemory(api, infile, outmiddle, 0);
175 ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
176
177 ASSERT_LT(outmiddle.tellp(), infile.tellg());
178
179 std::ifstream inmiddle(middle_s, std::ios::binary);
180 ASSERT_TRUE(inmiddle.is_open());
181
182 std::ofstream outfile(outfile_s, std::ios::binary);
183 ASSERT_TRUE(outfile.is_open());
184
185 status = DecompressInMemory(api, inmiddle, outfile);
186 ASSERT_THAT(status, IsOk()) << "Unable to decompress file in memory";
187
188 ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
189 }
190
TEST(SandboxTest,CheckCompressStream)191 TEST(SandboxTest, CheckCompressStream) {
192 ZstdSapiSandbox sandbox;
193 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
194 ZstdApi api = ZstdApi(&sandbox);
195
196 std::string infile_s = GetTestFilePath("text");
197 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
198 sapi::CreateNamedTempFileAndClose("out.zstd"));
199 std::string outfile_s =
200 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
201
202 std::ifstream infile(infile_s, std::ios::binary);
203 ASSERT_TRUE(infile.is_open());
204
205 std::ofstream outfile(outfile_s, std::ios::binary);
206 ASSERT_TRUE(outfile.is_open());
207
208 absl::Status status = CompressStream(api, infile, outfile, 0);
209 ASSERT_THAT(status, IsOk()) << "Unable to compress stream";
210
211 infile.clear();
212
213 ASSERT_LT(outfile.tellp(), infile.tellg());
214 }
215
TEST(SandboxTest,CheckDecompressStream)216 TEST(SandboxTest, CheckDecompressStream) {
217 ZstdSapiSandbox sandbox;
218 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
219 ZstdApi api = ZstdApi(&sandbox);
220
221 std::string infile_s = GetTestFilePath("text.stream.zstd");
222 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
223 sapi::CreateNamedTempFileAndClose("out"));
224 std::string outfile_s =
225 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
226
227 std::ifstream infile(infile_s, std::ios::binary);
228 ASSERT_TRUE(infile.is_open());
229
230 std::ofstream outfile(outfile_s, std::ios::binary);
231 ASSERT_TRUE(outfile.is_open());
232
233 absl::Status status = DecompressStream(api, infile, outfile);
234 ASSERT_THAT(status, IsOk()) << "Unable to decompress stream";
235
236 ASSERT_GT(outfile.tellp(), infile.tellg());
237
238 ASSERT_TRUE(CompareFiles(GetTestFilePath("text"), outfile_s));
239 }
240
TEST(SandboxTest,CheckCompressAndDecompressStream)241 TEST(SandboxTest, CheckCompressAndDecompressStream) {
242 ZstdSapiSandbox sandbox;
243 absl::Status status;
244 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
245 ZstdApi api = ZstdApi(&sandbox);
246
247 std::string infile_s = GetTestFilePath("text");
248
249 SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
250 sapi::CreateNamedTempFileAndClose("middle.zstd"));
251 std::string middle_s =
252 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
253
254 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
255 sapi::CreateNamedTempFileAndClose("out"));
256 std::string outfile_s =
257 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
258
259 std::ifstream infile(infile_s, std::ios::binary);
260 ASSERT_TRUE(infile.is_open());
261
262 std::ofstream outmiddle(middle_s, std::ios::binary);
263 ASSERT_TRUE(outmiddle.is_open());
264
265 status = CompressStream(api, infile, outmiddle, 0);
266 ASSERT_THAT(status, IsOk()) << "Unable to compress stream";
267
268 infile.clear();
269 ASSERT_LT(outmiddle.tellp(), infile.tellg());
270
271 std::ifstream inmiddle(middle_s, std::ios::binary);
272 ASSERT_TRUE(inmiddle.is_open());
273
274 std::ofstream outfile(outfile_s, std::ios::binary);
275 ASSERT_TRUE(outfile.is_open());
276
277 status = DecompressStream(api, inmiddle, outfile);
278 ASSERT_THAT(status, IsOk()) << "Unable to decompress";
279
280 ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
281 }
282
TEST(SandboxTest,CheckCompressInMemoryFD)283 TEST(SandboxTest, CheckCompressInMemoryFD) {
284 ZstdSapiSandbox sandbox;
285 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
286 ZstdApi api = ZstdApi(&sandbox);
287
288 std::string infile_s = GetTestFilePath("text");
289
290 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
291 sapi::CreateNamedTempFileAndClose("out.zstd"));
292 std::string outfile_s =
293 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
294
295 sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
296 ASSERT_GE(infd.GetValue(), 0);
297 sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
298 ASSERT_GE(outfd.GetValue(), 0);
299
300 absl::Status status = CompressInMemoryFD(api, infd, outfd, 0);
301 ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
302
303 off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
304 EXPECT_GE(inpos, 0);
305 off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
306 EXPECT_GE(outpos, 0);
307
308 EXPECT_LT(outpos, inpos);
309 }
310
TEST(SandboxTest,CheckDecompressInMemoryFD)311 TEST(SandboxTest, CheckDecompressInMemoryFD) {
312 ZstdSapiSandbox sandbox;
313 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
314 ZstdApi api = ZstdApi(&sandbox);
315
316 std::string infile_s = GetTestFilePath("text.blob.zstd");
317 sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
318 ASSERT_GE(infd.GetValue(), 0);
319
320 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
321 sapi::CreateNamedTempFileAndClose("out"));
322 std::string outfile_s =
323 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
324 sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
325 ASSERT_GE(outfd.GetValue(), 0);
326
327 absl::Status status = DecompressInMemoryFD(api, infd, outfd);
328 ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
329
330 off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
331 EXPECT_GE(inpos, 0);
332
333 off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
334 EXPECT_GE(outpos, 0);
335
336 EXPECT_GT(outpos, inpos);
337
338 ASSERT_TRUE(CompareFiles(GetTestFilePath("text"), outfile_s));
339 }
340
TEST(SandboxTest,CheckCompressAndDecompressInMemoryFD)341 TEST(SandboxTest, CheckCompressAndDecompressInMemoryFD) {
342 ZstdSapiSandbox sandbox;
343 absl::Status status;
344 int ret;
345 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
346 ZstdApi api = ZstdApi(&sandbox);
347
348 std::string infile_s = GetTestFilePath("text");
349
350 SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
351 sapi::CreateNamedTempFileAndClose("middle.zstd"));
352 std::string middle_s =
353 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
354
355 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
356 sapi::CreateNamedTempFileAndClose("out"));
357 std::string outfile_s =
358 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
359
360 sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
361 ASSERT_GE(infd.GetValue(), 0);
362
363 sapi::v::Fd outmiddlefd(open(middle_s.c_str(), O_WRONLY));
364 ASSERT_GE(outmiddlefd.GetValue(), 0);
365
366 status = CompressInMemoryFD(api, infd, outmiddlefd, 0);
367 ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
368
369 off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
370 EXPECT_GE(inpos, 0);
371
372 off_t outpos = lseek(outmiddlefd.GetValue(), 0, SEEK_END);
373 EXPECT_GE(outpos, 0);
374
375 EXPECT_LT(outpos, inpos);
376
377 infd.CloseLocalFd();
378 outmiddlefd.CloseLocalFd();
379
380 sapi::v::Fd inmiddlefd(open(middle_s.c_str(), O_RDONLY));
381 ASSERT_GE(inmiddlefd.GetValue(), 0);
382
383 sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
384 ASSERT_GE(outfd.GetValue(), 0);
385
386 status = DecompressInMemoryFD(api, inmiddlefd, outfd);
387 ASSERT_THAT(status, IsOk()) << "Unable to decompress file in memory";
388
389 outfd.CloseLocalFd();
390 inmiddlefd.CloseLocalFd();
391
392 ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
393 }
394
TEST(SandboxTest,CheckCompressStreamFD)395 TEST(SandboxTest, CheckCompressStreamFD) {
396 absl::Status status;
397 ZstdSapiSandbox sandbox;
398 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
399 ZstdApi api = ZstdApi(&sandbox);
400
401 std::string infile_s = GetTestFilePath("text");
402
403 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
404 sapi::CreateNamedTempFileAndClose("out.zstd"));
405 std::string outfile_s =
406 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
407
408 sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
409 ASSERT_GE(infd.GetValue(), 0);
410
411 sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
412 ASSERT_GE(outfd.GetValue(), 0);
413
414 status = CompressStreamFD(api, infd, outfd, 0);
415 ASSERT_THAT(status, IsOk()) << "Unable to compress stream";
416
417 off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
418 EXPECT_GE(inpos, 0);
419
420 off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
421 EXPECT_GE(outpos, 0);
422
423 EXPECT_LT(outpos, inpos);
424 }
425
TEST(SandboxTest,CheckDecompressStreamFD)426 TEST(SandboxTest, CheckDecompressStreamFD) {
427 absl::Status status;
428 ZstdSapiSandbox sandbox;
429 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
430 ZstdApi api = ZstdApi(&sandbox);
431
432 std::string infile_s = GetTestFilePath("text.stream.zstd");
433
434 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
435 sapi::CreateNamedTempFileAndClose("out"));
436 std::string outfile_s =
437 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
438
439 sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
440 ASSERT_GE(infd.GetValue(), 0);
441
442 sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
443 ASSERT_GE(outfd.GetValue(), 0);
444
445 status = DecompressStreamFD(api, infd, outfd);
446 ASSERT_THAT(status, IsOk()) << "Unable to decompress stream";
447
448 off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
449 EXPECT_GE(inpos, 0);
450
451 off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
452 EXPECT_GE(outpos, 0);
453
454 EXPECT_GT(outpos, inpos);
455
456 ASSERT_TRUE(CompareFiles(GetTestFilePath("text"), outfile_s));
457 }
458
TEST(SandboxTest,CheckCompressAndDecompressStreamFD)459 TEST(SandboxTest, CheckCompressAndDecompressStreamFD) {
460 ZstdSapiSandbox sandbox;
461 absl::Status status;
462 int ret;
463 ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
464 ZstdApi api = ZstdApi(&sandbox);
465
466 std::string infile_s = GetTestFilePath("text");
467
468 SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
469 sapi::CreateNamedTempFileAndClose("middle.zstd"));
470 std::string middle_s =
471 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
472
473 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
474 sapi::CreateNamedTempFileAndClose("out"));
475 std::string outfile_s =
476 sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
477
478 sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
479 ASSERT_GE(infd.GetValue(), 0);
480
481 sapi::v::Fd outmiddlefd(open(middle_s.c_str(), O_WRONLY));
482 ASSERT_GE(outmiddlefd.GetValue(), 0);
483
484 status = CompressStreamFD(api, infd, outmiddlefd, 0);
485 ASSERT_THAT(status, IsOk()) << "Unable to compress stream";
486
487 off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
488 EXPECT_GE(inpos, 0);
489
490 off_t outmiddlepos = lseek(outmiddlefd.GetValue(), 0, SEEK_END);
491 EXPECT_GE(outmiddlepos, 0);
492
493 EXPECT_LT(outmiddlepos, inpos);
494
495 infd.CloseLocalFd();
496 outmiddlefd.CloseLocalFd();
497
498 sapi::v::Fd inmiddlefd(open(middle_s.c_str(), O_RDONLY));
499 ASSERT_GE(inmiddlefd.GetValue(), 0);
500
501 sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
502 ASSERT_GE(outfd.GetValue(), 0);
503
504 status = DecompressStreamFD(api, inmiddlefd, outfd);
505 ASSERT_THAT(status, IsOk()) << "Unable to decompress stream";
506
507 ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
508 }
509
510 } // namespace
511