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