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