• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "sandboxed_api/util/fileops.h"
16 
17 #include <sys/stat.h>
18 #include <unistd.h>
19 
20 #include <algorithm>
21 #include <cerrno>
22 #include <string>
23 #include <vector>
24 
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "absl/strings/str_cat.h"
28 #include "sandboxed_api/testing.h"
29 #include "sandboxed_api/util/file_helpers.h"
30 #include "sandboxed_api/util/status_matchers.h"
31 
32 namespace sapi::file_util {
33 
34 // Forward declare functions that are only used in fileops.cc.
35 namespace fileops {
36 bool GetCWD(std::string* result);
37 bool RemoveLastPathComponent(const std::string& file, std::string* output);
38 }  // namespace fileops
39 
40 namespace {
41 
42 using ::sapi::IsOk;
43 using ::testing::Eq;
44 using ::testing::IsEmpty;
45 using ::testing::IsFalse;
46 using ::testing::IsTrue;
47 using ::testing::Ne;
48 using ::testing::SizeIs;
49 using ::testing::StrEq;
50 
51 class FileOpsTest : public testing::Test {
52  protected:
SetUpTestSuite()53   static void SetUpTestSuite() {
54     ASSERT_THAT(chdir(GetTestTempPath().c_str()), Eq(0));
55   }
56 };
57 
TEST_F(FileOpsTest,GetCWDTest)58 TEST_F(FileOpsTest, GetCWDTest) {
59   std::string result;
60   ASSERT_THAT(fileops::GetCWD(&result), IsTrue());
61   EXPECT_THAT(result, StrEq(GetTestTempPath()));
62 }
63 
TEST_F(FileOpsTest,MakeAbsoluteTest)64 TEST_F(FileOpsTest, MakeAbsoluteTest) {
65   const auto tmp_dir = GetTestTempPath();
66   ASSERT_THAT(chdir(tmp_dir.c_str()), Eq(0));
67   EXPECT_THAT(fileops::MakeAbsolute("", ""), StrEq(""));
68   EXPECT_THAT(fileops::MakeAbsolute(".", ""), StrEq(tmp_dir));
69   EXPECT_THAT(fileops::MakeAbsolute(".", tmp_dir), StrEq(tmp_dir));
70   EXPECT_THAT(fileops::MakeAbsolute(".", "/"), StrEq("/"));
71   EXPECT_THAT(fileops::MakeAbsolute("/", tmp_dir), StrEq("/"));
72   EXPECT_THAT(fileops::MakeAbsolute("/", "/"), StrEq("/"));
73   EXPECT_THAT(fileops::MakeAbsolute("/", ""), StrEq("/"));
74   EXPECT_THAT(fileops::MakeAbsolute("/foo/bar", ""), StrEq("/foo/bar"));
75   EXPECT_THAT(fileops::MakeAbsolute("foo/bar", ""),
76               StrEq(absl::StrCat(tmp_dir, "/foo/bar")));
77   EXPECT_THAT(fileops::MakeAbsolute("foo/bar", tmp_dir),
78               StrEq(absl::StrCat(tmp_dir, "/foo/bar")));
79   EXPECT_THAT(fileops::MakeAbsolute("foo/bar", tmp_dir + "/"),
80               StrEq(absl::StrCat(tmp_dir, "/foo/bar")));
81 }
82 
TEST_F(FileOpsTest,ExistsTest)83 TEST_F(FileOpsTest, ExistsTest) {
84   ASSERT_THAT(file::SetContents("exists_test", "", file::Defaults()), IsOk());
85   EXPECT_THAT(fileops::Exists("exists_test", false), IsTrue());
86   EXPECT_THAT(fileops::Exists("exists_test", true), IsTrue());
87 
88   ASSERT_THAT(symlink("exists_test", "exists_test_link"), Eq(0));
89   EXPECT_THAT(fileops::Exists("exists_test_link", false), IsTrue());
90   EXPECT_THAT(fileops::Exists("exists_test_link", true), IsTrue());
91 
92   ASSERT_THAT(unlink("exists_test"), Eq(0));
93   EXPECT_THAT(fileops::Exists("exists_test_link", false), IsTrue());
94   EXPECT_THAT(fileops::Exists("exists_test_link", true), IsFalse());
95 
96   ASSERT_THAT(unlink("exists_test_link"), Eq(0));
97   EXPECT_THAT(fileops::Exists("exists_test_link", false), IsFalse());
98   EXPECT_THAT(fileops::Exists("exists_test_link", true), IsFalse());
99 }
100 
TEST_F(FileOpsTest,ReadLinkTest)101 TEST_F(FileOpsTest, ReadLinkTest) {
102   EXPECT_THAT(fileops::ReadLink("readlink_not_there"), StrEq(""));
103   EXPECT_THAT(errno, Eq(ENOENT));
104 
105   ASSERT_THAT(file::SetContents("readlink_file", "", file::Defaults()), IsOk());
106   EXPECT_THAT(fileops::ReadLink("readlink_file"), StrEq(""));
107   unlink("readlink_file");
108 
109   ASSERT_THAT(symlink("..", "readlink_dotdot"), Eq(0));
110   EXPECT_THAT(fileops::ReadLink("readlink_dotdot"), StrEq(".."));
111   unlink("readlink_dotdot");
112 
113   ASSERT_THAT(symlink("../", "readlink_dotdotslash"), 0);
114   EXPECT_THAT(fileops::ReadLink("readlink_dotdotslash"), "../");
115   unlink("readlink_dotdotslash");
116 
117   ASSERT_THAT(symlink("/", "readlink_slash"), 0);
118   EXPECT_THAT(fileops::ReadLink("readlink_slash"), "/");
119   unlink("readlink_slash");
120 
121   const std::string very_long_name(PATH_MAX - 1, 'f');
122   ASSERT_THAT(symlink(very_long_name.c_str(), "readlink_long"), Eq(0));
123   EXPECT_THAT(fileops::ReadLink("readlink_long"), StrEq(very_long_name));
124   unlink("readlink_long");
125 }
126 
TEST_F(FileOpsTest,ListDirectoryEntriesFailTest)127 TEST_F(FileOpsTest, ListDirectoryEntriesFailTest) {
128   std::vector<std::string> files;
129   std::string error;
130 
131   EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
132               IsFalse());
133   EXPECT_THAT(files, IsEmpty());
134   EXPECT_THAT(error, StrEq("opendir(new_dir): No such file or directory"));
135 }
136 
TEST_F(FileOpsTest,ListDirectoryEntriesEmptyTest)137 TEST_F(FileOpsTest, ListDirectoryEntriesEmptyTest) {
138   std::vector<std::string> files;
139   std::string error;
140 
141   ASSERT_THAT(mkdir("new_dir", 0700), Eq(0));
142 
143   EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
144               IsTrue());
145   EXPECT_THAT(files, IsEmpty());
146 
147   rmdir("new_dir");
148 }
149 
TEST_F(FileOpsTest,ListDirectoryEntriesOneFileTest)150 TEST_F(FileOpsTest, ListDirectoryEntriesOneFileTest) {
151   ASSERT_THAT(mkdir("new_dir", 0700), Eq(0));
152   ASSERT_THAT(file::SetContents("new_dir/first", "", file::Defaults()), IsOk());
153 
154   std::vector<std::string> files;
155   std::string error;
156   EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
157               IsTrue());
158 
159   unlink("new_dir/first");
160   rmdir("new_dir");
161 
162   ASSERT_THAT(files, SizeIs(1));
163   EXPECT_THAT(files[0], "first");
164 }
165 
TEST_F(FileOpsTest,ListDirectoryEntriesTest)166 TEST_F(FileOpsTest, ListDirectoryEntriesTest) {
167   ASSERT_THAT(mkdir("new_dir", 0700), Eq(0));
168   constexpr int kNumFiles = 10;
169   for (int i = 0; i < kNumFiles; ++i) {
170     ASSERT_THAT(file::SetContents(absl::StrCat("new_dir/file", i), "",
171                                   file::Defaults()),
172                 IsOk());
173   }
174 
175   std::vector<std::string> files;
176   std::string error;
177   EXPECT_THAT(fileops::ListDirectoryEntries("new_dir", &files, &error),
178               IsTrue());
179 
180   fileops::DeleteRecursively("new_dir");
181 
182   ASSERT_THAT(files, SizeIs(kNumFiles));
183   std::sort(files.begin(), files.end());
184   for (int i = 0; i < kNumFiles; ++i) {
185     EXPECT_THAT(files[i], StrEq(absl::StrCat("file", i)));
186   }
187 }
188 
TEST_F(FileOpsTest,RemoveLastPathComponentTest)189 TEST_F(FileOpsTest, RemoveLastPathComponentTest) {
190   std::string result;
191 
192   EXPECT_THAT(fileops::RemoveLastPathComponent("/", &result), IsFalse());
193   EXPECT_THAT(result, StrEq("/"));
194 
195   EXPECT_THAT(fileops::RemoveLastPathComponent("///", &result), IsFalse());
196   EXPECT_THAT(result, StrEq("/"));
197 
198   EXPECT_THAT(fileops::RemoveLastPathComponent("/home", &result), IsTrue());
199   EXPECT_THAT(result, StrEq("/"));
200 
201   EXPECT_THAT(fileops::RemoveLastPathComponent("/home/", &result), IsTrue());
202   EXPECT_THAT(result, StrEq("/"));
203 
204   EXPECT_THAT(fileops::RemoveLastPathComponent("/home///", &result), IsTrue());
205   EXPECT_THAT(result, StrEq("/"));
206 
207   EXPECT_THAT(fileops::RemoveLastPathComponent("/home///", &result), IsTrue());
208   EXPECT_THAT(result, StrEq("/"));
209 
210   EXPECT_THAT(fileops::RemoveLastPathComponent("///home///", &result),
211               IsTrue());
212   EXPECT_THAT(result, StrEq("/"));
213 
214   EXPECT_THAT(fileops::RemoveLastPathComponent("/home/someone", &result),
215               IsTrue());
216   EXPECT_THAT(result, StrEq("/home"));
217 
218   EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone", &result),
219               IsTrue());
220   EXPECT_THAT(result, StrEq("/home"));
221 
222   EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone/", &result),
223               IsTrue());
224   EXPECT_THAT(result, StrEq("/home"));
225 
226   EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone//", &result),
227               IsTrue());
228   EXPECT_THAT(result, StrEq("/home"));
229 
230   EXPECT_THAT(fileops::RemoveLastPathComponent("/home/someone/file", &result),
231               IsTrue());
232   EXPECT_THAT(result, StrEq("/home/someone"));
233 
234   EXPECT_THAT(
235       fileops::RemoveLastPathComponent("/home/someone////file", &result),
236       IsTrue());
237   EXPECT_THAT(result, StrEq("/home/someone"));
238 
239   EXPECT_THAT(fileops::RemoveLastPathComponent("/home///someone/file", &result),
240               IsTrue());
241   EXPECT_THAT(result, StrEq("/home///someone"));
242 
243   EXPECT_THAT(fileops::RemoveLastPathComponent("/home/someone/file", &result),
244               IsTrue());
245   EXPECT_THAT(result, StrEq("/home/someone"));
246 
247   EXPECT_THAT(fileops::RemoveLastPathComponent("no_root", &result), IsTrue());
248   EXPECT_THAT(result, StrEq(""));
249 
250   EXPECT_THAT(fileops::RemoveLastPathComponent("no_root/", &result), IsTrue());
251   EXPECT_THAT(result, StrEq(""));
252 
253   EXPECT_THAT(fileops::RemoveLastPathComponent("no_root///", &result),
254               IsTrue());
255   EXPECT_THAT(result, StrEq(""));
256 
257   EXPECT_THAT(fileops::RemoveLastPathComponent("/file", &result), IsTrue());
258   EXPECT_THAT(result, "/");
259 
260   EXPECT_THAT(fileops::RemoveLastPathComponent("no_root/file", &result),
261               IsTrue());
262   EXPECT_THAT(result, StrEq("no_root"));
263 
264   result = "no_root";
265   EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
266   EXPECT_THAT(result, StrEq(""));
267 
268   result = "no_root/";
269   EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
270   EXPECT_THAT(result, StrEq(""));
271 
272   result = "no_root///";
273   EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
274   EXPECT_THAT(result, StrEq(""));
275 
276   result = "/file";
277   EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
278   EXPECT_THAT(result, StrEq("/"));
279 
280   result = "no_root/file";
281   EXPECT_THAT(fileops::RemoveLastPathComponent(result, &result), IsTrue());
282   EXPECT_THAT(result, StrEq("no_root"));
283 
284   EXPECT_THAT(fileops::RemoveLastPathComponent("", &result), IsFalse());
285   EXPECT_THAT(result, StrEq(""));
286 }
287 
TEST_F(FileOpsTest,TestBasename)288 TEST_F(FileOpsTest, TestBasename) {
289   EXPECT_THAT(fileops::Basename(""), StrEq(""));
290   EXPECT_THAT(fileops::Basename("/"), StrEq(""));
291   EXPECT_THAT(fileops::Basename("//"), StrEq(""));
292   EXPECT_THAT(fileops::Basename("/hello/"), StrEq(""));
293   EXPECT_THAT(fileops::Basename("//hello"), StrEq("hello"));
294   EXPECT_THAT(fileops::Basename("/hello/world"), StrEq("world"));
295   EXPECT_THAT(fileops::Basename("/hello, world"), StrEq("hello, world"));
296 }
297 
TEST_F(FileOpsTest,TestStripBasename)298 TEST_F(FileOpsTest, TestStripBasename) {
299   EXPECT_THAT(fileops::StripBasename(""), StrEq(""));
300   EXPECT_THAT(fileops::StripBasename("/"), StrEq("/"));
301   EXPECT_THAT(fileops::StripBasename("//"), StrEq("/"));
302   EXPECT_THAT(fileops::StripBasename("/hello"), StrEq("/"));
303   EXPECT_THAT(fileops::StripBasename("//hello"), StrEq("/"));
304   EXPECT_THAT(fileops::StripBasename("/hello/"), StrEq("/hello"));
305   EXPECT_THAT(fileops::StripBasename("/hello//"), StrEq("/hello/"));
306   EXPECT_THAT(fileops::StripBasename("/hello/world"), StrEq("/hello"));
307   EXPECT_THAT(fileops::StripBasename("/hello, world"), StrEq("/"));
308 }
309 
SetupDirectory()310 void SetupDirectory() {
311   ASSERT_THAT(mkdir("foo", 0755), Eq(0));
312   ASSERT_THAT(mkdir("foo/bar", 0755), Eq(0));
313   ASSERT_THAT(mkdir("foo/baz", 0755), Eq(0));
314   ASSERT_THAT(file::SetContents("foo/quux", "", file::Defaults()), IsOk());
315   ASSERT_THAT(chmod("foo/quux", 0644), Eq(0));
316 
317   ASSERT_THAT(file::SetContents("foo/bar/foo", "", file::Defaults()), IsOk());
318   ASSERT_THAT(chmod("foo/bar/foo", 0644), Eq(0));
319   ASSERT_THAT(file::SetContents("foo/bar/bar", "", file::Defaults()), IsOk());
320   ASSERT_THAT(chmod("foo/bar/bar", 0644), Eq(0));
321 
322   ASSERT_THAT(mkdir("foo/bar/baz", 0755), Eq(0));
323   ASSERT_THAT(file::SetContents("foo/bar/baz/foo", "", file::Defaults()),
324               IsOk());
325   ASSERT_THAT(chmod("foo/bar/baz/foo", 0644), Eq(0));
326 }
327 
TEST_F(FileOpsTest,CreateDirectoryRecursivelyTest)328 TEST_F(FileOpsTest, CreateDirectoryRecursivelyTest) {
329   constexpr char kTestDir[] = "a/b/c";
330   EXPECT_THAT(fileops::CreateDirectoryRecursively(kTestDir, 0700), IsTrue());
331   EXPECT_THAT(fileops::CreateDirectoryRecursively(kTestDir, 0700), IsTrue());
332 }
333 
TEST_F(FileOpsTest,DeleteRecursivelyTest)334 TEST_F(FileOpsTest, DeleteRecursivelyTest) {
335   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
336   EXPECT_THAT(fileops::DeleteRecursively("/not_there"), IsTrue());
337 
338   // Can't stat file
339   SetupDirectory();
340   ASSERT_THAT(chmod("foo/bar/baz", 0000), Eq(0));
341   EXPECT_THAT(fileops::DeleteRecursively("foo/bar/baz/quux"), IsFalse());
342   EXPECT_THAT(errno, Eq(EACCES));
343   ASSERT_THAT(chmod("foo/bar/baz", 0755), Eq(0));
344 
345   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
346   struct stat64 st;
347   EXPECT_THAT(lstat64("foo", &st), Ne(0));
348 
349   // Can't list subdirectory
350   SetupDirectory();
351   ASSERT_THAT(chmod("foo/bar/baz", 0000), Eq(0));
352   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsFalse());
353   EXPECT_THAT(errno, Eq(EACCES));
354   ASSERT_THAT(chmod("foo/bar/baz", 0755), Eq(0));
355 
356   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
357 
358   // Can't delete file
359   SetupDirectory();
360   ASSERT_THAT(chmod("foo/bar/baz", 0500), Eq(0));
361   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsFalse());
362   EXPECT_THAT(errno, Eq(EACCES));
363   ASSERT_THAT(chmod("foo/bar/baz", 0755), Eq(0));
364 
365   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
366 
367   // Can't delete directory
368   SetupDirectory();
369   ASSERT_THAT(fileops::DeleteRecursively("foo/bar/baz/foo"), IsTrue());
370   ASSERT_THAT(chmod("foo/bar", 0500), Eq(0));
371   EXPECT_THAT(fileops::DeleteRecursively("foo/bar/baz"), IsFalse());
372   EXPECT_THAT(errno, Eq(EACCES));
373   ASSERT_THAT(chmod("foo/bar", 0755), Eq(0));
374 
375   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
376 }
377 
TEST_F(FileOpsTest,ReadLinkAbsoluteTest)378 TEST_F(FileOpsTest, ReadLinkAbsoluteTest) {
379   const auto tmp_dir = GetTestTempPath();
380   ASSERT_THAT(chdir(tmp_dir.c_str()), Eq(0));
381 
382   EXPECT_THAT(fileops::DeleteRecursively("foo"), IsTrue());
383   ASSERT_THAT(symlink("rel/path", "foo"), Eq(0));
384 
385   const std::string expected_path = absl::StrCat(tmp_dir, "/rel/path");
386   const std::string expected_path2 = absl::StrCat(tmp_dir, "/./rel/path");
387   std::string result;
388   EXPECT_THAT(fileops::ReadLinkAbsolute("foo", &result), IsTrue());
389   EXPECT_THAT(result, StrEq(expected_path));
390   EXPECT_THAT(fileops::ReadLinkAbsolute("./foo", &result), IsTrue());
391   EXPECT_THAT(result, StrEq(expected_path2));
392   EXPECT_THAT(fileops::ReadLinkAbsolute(absl::StrCat(tmp_dir, "/foo"), &result),
393               IsTrue());
394   EXPECT_THAT(result, StrEq(expected_path));
395 
396   result.clear();
397   EXPECT_THAT(fileops::ReadLinkAbsolute("/not_there", &result), IsFalse());
398   EXPECT_THAT(result, IsEmpty());
399 }
400 
TEST_F(FileOpsTest,CopyFileTest)401 TEST_F(FileOpsTest, CopyFileTest) {
402   const auto tmp_dir = GetTestTempPath();
403   // Non-existent source
404   EXPECT_THAT(
405       fileops::CopyFile("/not/there", absl::StrCat(tmp_dir, "/out"), 0777),
406       IsFalse());
407 
408   // Unwritable target
409   EXPECT_THAT(fileops::CopyFile("/proc/self/exe", tmp_dir, 0777), IsFalse());
410 
411   EXPECT_THAT(file::SetContents(absl::StrCat(tmp_dir, "/test"), "test\n",
412                                 file::Defaults()),
413               IsOk());
414   EXPECT_THAT(fileops::CopyFile(absl::StrCat(tmp_dir, "/test"),
415                                 absl::StrCat(tmp_dir, "/test2"), 0666),
416               IsTrue());
417 
418   std::string text;
419   EXPECT_THAT(file::GetContents(absl::StrCat(tmp_dir, "/test2"), &text,
420                                 file::Defaults()),
421               IsOk());
422 
423   EXPECT_THAT(text, StrEq("test\n"));
424 
425   unlink((absl::StrCat(tmp_dir, "/test")).c_str());
426   unlink((absl::StrCat(tmp_dir, "/test2")).c_str());
427 }
428 
429 }  // namespace
430 }  // namespace sapi::file_util
431