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