1*ec63e07aSXin Li // Copyright 2020 Google LLC
2*ec63e07aSXin Li //
3*ec63e07aSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*ec63e07aSXin Li // you may not use this file except in compliance with the License.
5*ec63e07aSXin Li // You may obtain a copy of the License at
6*ec63e07aSXin Li //
7*ec63e07aSXin Li // https://www.apache.org/licenses/LICENSE-2.0
8*ec63e07aSXin Li //
9*ec63e07aSXin Li // Unless required by applicable law or agreed to in writing, software
10*ec63e07aSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*ec63e07aSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ec63e07aSXin Li // See the License for the specific language governing permissions and
13*ec63e07aSXin Li // limitations under the License.
14*ec63e07aSXin Li
15*ec63e07aSXin Li #include <fstream>
16*ec63e07aSXin Li
17*ec63e07aSXin Li #include "sapi_minitar.h" // NOLINT(build/include)
18*ec63e07aSXin Li #include "gtest/gtest.h"
19*ec63e07aSXin Li #include "sandboxed_api/sandbox2/util.h"
20*ec63e07aSXin Li #include "sandboxed_api/util/fileops.h"
21*ec63e07aSXin Li #include "sandboxed_api/util/path.h"
22*ec63e07aSXin Li #include "sandboxed_api/util/status_matchers.h"
23*ec63e07aSXin Li
24*ec63e07aSXin Li namespace {
25*ec63e07aSXin Li
26*ec63e07aSXin Li using ::sapi::IsOk;
27*ec63e07aSXin Li using ::sapi::file::JoinPath;
28*ec63e07aSXin Li using ::sapi::file_util::fileops::Exists;
29*ec63e07aSXin Li using ::testing::Eq;
30*ec63e07aSXin Li using ::testing::IsTrue;
31*ec63e07aSXin Li using ::testing::StrEq;
32*ec63e07aSXin Li
33*ec63e07aSXin Li // We will use a fixture class for testing which allows us to override the
34*ec63e07aSXin Li // SetUp and TearDown functions. Also, data that needs to be initialized
35*ec63e07aSXin Li // or destroyed only once (the test files and directories) will be handled
36*ec63e07aSXin Li // in the SetUpTestSuite and TearDownTestSuite functions which are executed
37*ec63e07aSXin Li // only once.
38*ec63e07aSXin Li // All of the testing data will be placed in a temporary directory and each
39*ec63e07aSXin Li // test will have it's own temporary directory. At the end of each test
40*ec63e07aSXin Li // and all of the tests, the temporary data is deleted.
41*ec63e07aSXin Li class MiniTarTest : public ::testing::Test {
42*ec63e07aSXin Li protected:
43*ec63e07aSXin Li // Before running the tests, we create a temporary directory which will
44*ec63e07aSXin Li // store generated files and directories used for testing.
45*ec63e07aSXin Li // The directory will look as follows:
46*ec63e07aSXin Li // -file1
47*ec63e07aSXin Li // -dir1 - file2
48*ec63e07aSXin Li // - dir2 - file3
SetUpTestSuite()49*ec63e07aSXin Li static void SetUpTestSuite() {
50*ec63e07aSXin Li absl::StatusOr<std::string> tmp_status = CreateTempDirAtCWD();
51*ec63e07aSXin Li ASSERT_THAT(tmp_status, IsOk());
52*ec63e07aSXin Li data_dir_ = new std::string(std::move(tmp_status).value());
53*ec63e07aSXin Li
54*ec63e07aSXin Li init_wd_ = new std::string(sandbox2::file_util::fileops::GetCWD());
55*ec63e07aSXin Li ASSERT_THAT(Exists(data_dir_, false), IsTrue())
56*ec63e07aSXin Li << "Test data directory was not created";
57*ec63e07aSXin Li ASSERT_THAT(chdir(data_dir_.data()), Eq(0))
58*ec63e07aSXin Li << "Could not chdir into test data directory";
59*ec63e07aSXin Li
60*ec63e07aSXin Li CreateAndWriteToFile(kFile1);
61*ec63e07aSXin Li ASSERT_THAT(mkdir(kDir1.data(), 0755), Eq(0)) << "Could not create dir1";
62*ec63e07aSXin Li CreateAndWriteToFile(kFile2);
63*ec63e07aSXin Li ASSERT_THAT(mkdir(kDir2.data(), 0755), Eq(0)) << "Could not create dir2";
64*ec63e07aSXin Li CreateAndWriteToFile(kFile3);
65*ec63e07aSXin Li
66*ec63e07aSXin Li test_count_ = 0;
67*ec63e07aSXin Li }
68*ec63e07aSXin Li
TearDownTestSuite()69*ec63e07aSXin Li static void TearDownTestSuite() {
70*ec63e07aSXin Li // The tests have the data directory as their working directory at the end
71*ec63e07aSXin Li // so we move to the initial working directory in order to not delete the
72*ec63e07aSXin Li // directory that we are inside of.
73*ec63e07aSXin Li ASSERT_THAT(chdir(init_wd_->data()), Eq(0))
74*ec63e07aSXin Li << "Could not chdir into initial working directory";
75*ec63e07aSXin Li EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(*data_dir_),
76*ec63e07aSXin Li IsTrue)
77*ec63e07aSXin Li << "Error during test data deletion";
78*ec63e07aSXin Li delete init_wd_;
79*ec63e07aSXin Li delete data_dir_;
80*ec63e07aSXin Li }
81*ec63e07aSXin Li
SetUp()82*ec63e07aSXin Li void SetUp() override {
83*ec63e07aSXin Li // We use a unique id based on test count to make sure that files created
84*ec63e07aSXin Li // during tests do not overlap.
85*ec63e07aSXin Li id_ = "test" + std::to_string(test_count_);
86*ec63e07aSXin Li
87*ec63e07aSXin Li absl::StatusOr<std::string> tmp_status = CreateTempDirAtCWD();
88*ec63e07aSXin Li ASSERT_THAT(tmp_status, IsOk());
89*ec63e07aSXin Li tmp_dir_ = tmp_status.value();
90*ec63e07aSXin Li
91*ec63e07aSXin Li ASSERT_THAT(Exists(tmp_dir_, false), IsTrue)
92*ec63e07aSXin Li << "Could not create test specific temporary directory";
93*ec63e07aSXin Li ASSERT_THAT(chdir(data_dir_->data()), Eq(0))
94*ec63e07aSXin Li << "Could not chdir into test data directory";
95*ec63e07aSXin Li }
96*ec63e07aSXin Li
TearDown()97*ec63e07aSXin Li void TearDown() override {
98*ec63e07aSXin Li // Move to another directory before deleting the temporary folder.
99*ec63e07aSXin Li ASSERT_THAT(chdir(data_dir_->data()), Eq(0))
100*ec63e07aSXin Li << "Could not chdir into test data directory";
101*ec63e07aSXin Li
102*ec63e07aSXin Li EXPECT_THAT(sandbox2::file_util::fileops::DeleteRecursively(tmp_dir_),
103*ec63e07aSXin Li IsTrue)
104*ec63e07aSXin Li << "Error during test temporary directory deletion";
105*ec63e07aSXin Li ++test_count_;
106*ec63e07aSXin Li }
107*ec63e07aSXin Li
108*ec63e07aSXin Li // Creates the file specified and writes the same filename.
109*ec63e07aSXin Li // This is done in order to not have completely empty files for the
110*ec63e07aSXin Li // archiving step.
CreateAndWriteToFile(absl::string_view file)111*ec63e07aSXin Li static void CreateAndWriteToFile(absl::string_view file) {
112*ec63e07aSXin Li std::ofstream fin(file.data());
113*ec63e07aSXin Li ASSERT_THAT(fin.is_open(), IsTrue()) << "Could not create" << file;
114*ec63e07aSXin Li fin << file;
115*ec63e07aSXin Li fin.close();
116*ec63e07aSXin Li }
117*ec63e07aSXin Li
118*ec63e07aSXin Li // Checks if the files exists and if the contents are correct.
119*ec63e07aSXin Li // In these tests, each file contains the relative path from the test
120*ec63e07aSXin Li // directory.
121*ec63e07aSXin Li // Example: dir1/dir2/file3 will contain dir1/dir2/file3.
122*ec63e07aSXin Li // What the files contain does not matter as much, the only important thing
123*ec63e07aSXin Li // is that they are not empty so we can check if the contents are preserved.
CheckFile(const std::string & file)124*ec63e07aSXin Li static void CheckFile(const std::string& file) {
125*ec63e07aSXin Li ASSERT_THAT(Exists(file, false), IsTrue()) << "Could not find " << file;
126*ec63e07aSXin Li std::ifstream fin(file);
127*ec63e07aSXin Li ASSERT_THAT(fin.is_open(), IsTrue()) << "Error when opening " << file;
128*ec63e07aSXin Li
129*ec63e07aSXin Li std::string file_contents((std::istreambuf_iterator<char>(fin)),
130*ec63e07aSXin Li std::istreambuf_iterator<char>());
131*ec63e07aSXin Li
132*ec63e07aSXin Li EXPECT_THAT(file_contents, StrEq(file))
133*ec63e07aSXin Li << "Contents of " << file << " are different after extraction";
134*ec63e07aSXin Li fin.close();
135*ec63e07aSXin Li }
136*ec63e07aSXin Li
137*ec63e07aSXin Li static int test_count_;
138*ec63e07aSXin Li static std::string* data_dir_;
139*ec63e07aSXin Li static std::string* init_wd_;
140*ec63e07aSXin Li std::string tmp_dir_;
141*ec63e07aSXin Li std::string id_;
142*ec63e07aSXin Li
143*ec63e07aSXin Li static constexpr absl::string_view kFile1 = "file1";
144*ec63e07aSXin Li static constexpr absl::string_view kFile2 = "dir1/file2";
145*ec63e07aSXin Li static constexpr absl::string_view kFile3 = "dir1/dir2/file3";
146*ec63e07aSXin Li static constexpr absl::string_view kDir1 = "dir1";
147*ec63e07aSXin Li static constexpr absl::string_view kDir2 = "dir1/dir2";
148*ec63e07aSXin Li };
149*ec63e07aSXin Li
150*ec63e07aSXin Li int MiniTarTest::test_count_;
151*ec63e07aSXin Li std::string* MiniTarTest::data_dir_;
152*ec63e07aSXin Li std::string* MiniTarTest::init_wd_;
153*ec63e07aSXin Li
154*ec63e07aSXin Li // The tests have the following pattern:
155*ec63e07aSXin Li // 1) From inside the test data directory, call the create function with
156*ec63e07aSXin Li // different arguments.
157*ec63e07aSXin Li // 2) Move to the test specific temporary directory created during the
158*ec63e07aSXin Li // set up phase.
159*ec63e07aSXin Li // 3) Extract the archive created at step 1.
160*ec63e07aSXin Li // 4) Check that the files in the archive have been extracted correctly
161*ec63e07aSXin Li // by first checking if they exist and then checking if the content is the
162*ec63e07aSXin Li // same as in the original file.
TEST_F(MiniTarTest,TestFileSimple)163*ec63e07aSXin Li TEST_F(MiniTarTest, TestFileSimple) {
164*ec63e07aSXin Li std::vector<std::string> v = {kFile1.data()};
165*ec63e07aSXin Li
166*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), 0, v, false), IsOk());
167*ec63e07aSXin Li
168*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
169*ec63e07aSXin Li << "Could not chdir into test data directory";
170*ec63e07aSXin Li
171*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
172*ec63e07aSXin Li IsOk());
173*ec63e07aSXin Li
174*ec63e07aSXin Li CheckFile(std::string(kFile1));
175*ec63e07aSXin Li }
176*ec63e07aSXin Li
TEST_F(MiniTarTest,TestMultipleFiles)177*ec63e07aSXin Li TEST_F(MiniTarTest, TestMultipleFiles) {
178*ec63e07aSXin Li std::vector<std::string> v = {kFile1.data(), kFile2.data(), kFile3.data()};
179*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), 0, v, false), IsOk());
180*ec63e07aSXin Li ASSERT_THAT(Exists(id_.data(), false), IsTrue())
181*ec63e07aSXin Li << "Archive file was not created";
182*ec63e07aSXin Li
183*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
184*ec63e07aSXin Li << "Could not chdir into test data directory";
185*ec63e07aSXin Li
186*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
187*ec63e07aSXin Li IsOk());
188*ec63e07aSXin Li
189*ec63e07aSXin Li CheckFile(std::string(kFile1));
190*ec63e07aSXin Li CheckFile(std::string(kFile2));
191*ec63e07aSXin Li CheckFile(std::string(kFile3));
192*ec63e07aSXin Li }
193*ec63e07aSXin Li
TEST_F(MiniTarTest,TestDirectorySimple)194*ec63e07aSXin Li TEST_F(MiniTarTest, TestDirectorySimple) {
195*ec63e07aSXin Li std::vector<std::string> v = {kDir2.data()};
196*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), 0, v, false), IsOk());
197*ec63e07aSXin Li
198*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
199*ec63e07aSXin Li << "Could not chdir into test data directory";
200*ec63e07aSXin Li
201*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
202*ec63e07aSXin Li IsOk());
203*ec63e07aSXin Li
204*ec63e07aSXin Li CheckFile(std::string(kFile3));
205*ec63e07aSXin Li }
206*ec63e07aSXin Li
TEST_F(MiniTarTest,TestDirectoryNested)207*ec63e07aSXin Li TEST_F(MiniTarTest, TestDirectoryNested) {
208*ec63e07aSXin Li std::vector<std::string> v = {kDir1.data()};
209*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), 0, v, false), IsOk());
210*ec63e07aSXin Li
211*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
212*ec63e07aSXin Li << "Could not chdir into test data directory";
213*ec63e07aSXin Li
214*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
215*ec63e07aSXin Li IsOk());
216*ec63e07aSXin Li
217*ec63e07aSXin Li CheckFile(std::string(kFile2));
218*ec63e07aSXin Li CheckFile(std::string(kFile3));
219*ec63e07aSXin Li }
220*ec63e07aSXin Li
TEST_F(MiniTarTest,TestComplex)221*ec63e07aSXin Li TEST_F(MiniTarTest, TestComplex) {
222*ec63e07aSXin Li std::vector<std::string> v = {kFile1.data(), kDir1.data()};
223*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), 0, v, false), IsOk());
224*ec63e07aSXin Li
225*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
226*ec63e07aSXin Li << "Could not chdir into test data directory";
227*ec63e07aSXin Li
228*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
229*ec63e07aSXin Li IsOk());
230*ec63e07aSXin Li
231*ec63e07aSXin Li CheckFile(std::string(kFile1));
232*ec63e07aSXin Li CheckFile(std::string(kFile2));
233*ec63e07aSXin Li CheckFile(std::string(kFile3));
234*ec63e07aSXin Li }
235*ec63e07aSXin Li
TEST_F(MiniTarTest,TestCompress)236*ec63e07aSXin Li TEST_F(MiniTarTest, TestCompress) {
237*ec63e07aSXin Li std::vector<std::string> v = {kFile1.data(), kDir1.data()};
238*ec63e07aSXin Li int compress = 'Z';
239*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), compress, v, false), IsOk());
240*ec63e07aSXin Li
241*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
242*ec63e07aSXin Li << "Could not chdir into test data directory";
243*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
244*ec63e07aSXin Li IsOk());
245*ec63e07aSXin Li
246*ec63e07aSXin Li CheckFile(std::string(kFile1));
247*ec63e07aSXin Li CheckFile(std::string(kFile2));
248*ec63e07aSXin Li CheckFile(std::string(kFile3));
249*ec63e07aSXin Li }
250*ec63e07aSXin Li
TEST_F(MiniTarTest,TestGZIP)251*ec63e07aSXin Li TEST_F(MiniTarTest, TestGZIP) {
252*ec63e07aSXin Li std::vector<std::string> v = {kFile1.data(), kDir1.data()};
253*ec63e07aSXin Li int compress = 'z';
254*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), compress, v, false), IsOk());
255*ec63e07aSXin Li
256*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
257*ec63e07aSXin Li << "Could not chdir into test data directory";
258*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
259*ec63e07aSXin Li IsOk());
260*ec63e07aSXin Li
261*ec63e07aSXin Li CheckFile(std::string(kFile1));
262*ec63e07aSXin Li CheckFile(std::string(kFile2));
263*ec63e07aSXin Li CheckFile(std::string(kFile3));
264*ec63e07aSXin Li }
265*ec63e07aSXin Li
TEST_F(MiniTarTest,TestBZIP2)266*ec63e07aSXin Li TEST_F(MiniTarTest, TestBZIP2) {
267*ec63e07aSXin Li std::vector<std::string> v = {kFile1.data(), kDir1.data()};
268*ec63e07aSXin Li int compress = 'j';
269*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), compress, v, false), IsOk());
270*ec63e07aSXin Li
271*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
272*ec63e07aSXin Li << "Could not chdir into test data directory";
273*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
274*ec63e07aSXin Li IsOk());
275*ec63e07aSXin Li
276*ec63e07aSXin Li CheckFile(std::string(kFile1));
277*ec63e07aSXin Li CheckFile(std::string(kFile2));
278*ec63e07aSXin Li CheckFile(std::string(kFile3));
279*ec63e07aSXin Li }
280*ec63e07aSXin Li
TEST_F(MiniTarTest,TestPaths)281*ec63e07aSXin Li TEST_F(MiniTarTest, TestPaths) {
282*ec63e07aSXin Li // These should be equivalent to kFile1 and kDir1 after cleaning.
283*ec63e07aSXin Li std::vector<std::string> v = {JoinPath("a/b/../../c/../", kFile1).data(),
284*ec63e07aSXin Li JoinPath("d/../e/././///../", kDir1).data()};
285*ec63e07aSXin Li ASSERT_THAT(CreateArchive(id_.data(), 0, v, false), IsOk());
286*ec63e07aSXin Li
287*ec63e07aSXin Li ASSERT_THAT(chdir(tmp_dir_.data()), Eq(0))
288*ec63e07aSXin Li << "Could not chdir into test data directory";
289*ec63e07aSXin Li ASSERT_THAT(ExtractArchive(JoinPath(*data_dir_, id_).data(), 1, 0, false),
290*ec63e07aSXin Li IsOk());
291*ec63e07aSXin Li
292*ec63e07aSXin Li CheckFile(std::string(kFile1));
293*ec63e07aSXin Li CheckFile(std::string(kFile2));
294*ec63e07aSXin Li CheckFile(std::string(kFile3));
295*ec63e07aSXin Li }
296*ec63e07aSXin Li
297*ec63e07aSXin Li } // namespace
298