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/sandbox2/mounts.h"
16
17 #include <unistd.h>
18
19 #include <cstddef>
20 #include <string>
21 #include <vector>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/status/status.h"
26 #include "absl/strings/match.h"
27 #include "absl/strings/str_cat.h"
28 #include "sandboxed_api/testing.h"
29 #include "sandboxed_api/util/path.h"
30 #include "sandboxed_api/util/status_matchers.h"
31 #include "sandboxed_api/util/temp_file.h"
32
33 namespace sandbox2 {
34 namespace {
35
36 namespace file = ::sapi::file;
37 using ::sapi::CreateNamedTempFileAndClose;
38 using ::sapi::CreateTempDir;
39 using ::sapi::GetTestSourcePath;
40 using ::sapi::GetTestTempPath;
41 using ::sapi::IsOk;
42 using ::sapi::StatusIs;
43 using ::testing::Eq;
44 using ::testing::StrEq;
45 using ::testing::UnorderedElementsAreArray;
46
47 constexpr size_t kTmpfsSize = 1024;
48
TEST(MountTreeTest,TestInvalidFilenames)49 TEST(MountTreeTest, TestInvalidFilenames) {
50 Mounts mounts;
51
52 EXPECT_THAT(mounts.AddFile(""), StatusIs(absl::StatusCode::kInvalidArgument));
53 EXPECT_THAT(mounts.AddFile("a"),
54 StatusIs(absl::StatusCode::kInvalidArgument));
55 EXPECT_THAT(mounts.AddFileAt("/a", ""),
56 StatusIs(absl::StatusCode::kInvalidArgument));
57 EXPECT_THAT(mounts.AddFileAt("", "/a"),
58 StatusIs(absl::StatusCode::kInvalidArgument));
59 EXPECT_THAT(mounts.AddFileAt("/a", "a"),
60 StatusIs(absl::StatusCode::kInvalidArgument));
61 EXPECT_THAT(mounts.AddFile("/"),
62 StatusIs(absl::StatusCode::kInvalidArgument));
63 EXPECT_THAT(mounts.AddFileAt("/a", "/"),
64 StatusIs(absl::StatusCode::kInvalidArgument));
65 EXPECT_THAT(mounts.Remove("/"), StatusIs(absl::StatusCode::kInvalidArgument));
66 }
67
TEST(MountTreeTest,TestAddFile)68 TEST(MountTreeTest, TestAddFile) {
69 Mounts mounts;
70
71 EXPECT_THAT(mounts.AddFile("/a"), IsOk());
72 EXPECT_THAT(mounts.AddFile("/b"), IsOk());
73 EXPECT_THAT(mounts.AddFile("/c/d"), IsOk());
74 EXPECT_THAT(mounts.AddFile("/c/e"), IsOk());
75 EXPECT_THAT(mounts.AddFile("/c/dd/e"), IsOk());
76
77 EXPECT_THAT(mounts.AddFileAt("/a", "/f"), IsOk());
78 }
79
TEST(MountTreeTest,TestAddDir)80 TEST(MountTreeTest, TestAddDir) {
81 Mounts mounts;
82
83 EXPECT_THAT(mounts.AddDirectoryAt("/a", "/a"), IsOk());
84 EXPECT_THAT(mounts.AddDirectoryAt("/c/d", "/c/d"), IsOk());
85 EXPECT_THAT(mounts.AddDirectoryAt("/c/d/e", "/c/d/e"), IsOk());
86 }
87
TEST(MountTreeTest,TestAddTmpFs)88 TEST(MountTreeTest, TestAddTmpFs) {
89 Mounts mounts;
90
91 EXPECT_THAT(mounts.AddTmpfs("/a", kTmpfsSize), IsOk());
92 EXPECT_THAT(mounts.AddTmpfs("/a/b", kTmpfsSize), IsOk());
93 EXPECT_THAT(mounts.AddFile("/a/b/c"), IsOk());
94 EXPECT_THAT(mounts.AddDirectoryAt("/a/b/d", "/a/b/d"), IsOk());
95 }
96
TEST(MountTreeTest,TestRemove)97 TEST(MountTreeTest, TestRemove) {
98 Mounts mounts;
99 EXPECT_THAT(mounts.AddTmpfs("/a", kTmpfsSize), IsOk());
100 EXPECT_THAT(mounts.AddFile("/b/c/d"), IsOk());
101 EXPECT_THAT(mounts.AddFile("/c/c/d"), IsOk());
102 EXPECT_THAT(mounts.AddDirectoryAt("/d/b/d", "/d/b/d"), IsOk());
103 EXPECT_THAT(mounts.AddDirectoryAt("/e/b/d", "/e/b/d"), IsOk());
104 EXPECT_THAT(mounts.Remove("/a/b"), StatusIs(absl::StatusCode::kNotFound));
105 EXPECT_THAT(mounts.Remove("/a"), IsOk());
106 EXPECT_THAT(mounts.Remove("/b/c/d/e"), StatusIs(absl::StatusCode::kNotFound));
107 EXPECT_THAT(mounts.Remove("/b/c/e"), StatusIs(absl::StatusCode::kNotFound));
108 EXPECT_THAT(mounts.Remove("/b/c/d"), IsOk());
109 EXPECT_THAT(mounts.Remove("/c"), IsOk());
110 EXPECT_THAT(mounts.Remove("/d/b/d/e"), StatusIs(absl::StatusCode::kNotFound));
111 EXPECT_THAT(mounts.Remove("/d/b/d"), IsOk());
112 EXPECT_THAT(mounts.Remove("/e"), IsOk());
113 EXPECT_THAT(mounts.Remove("/f"), StatusIs(absl::StatusCode::kNotFound));
114 }
115
TEST(MountTreeTest,TestMultipleInsertionFileSymlink)116 TEST(MountTreeTest, TestMultipleInsertionFileSymlink) {
117 Mounts mounts;
118
119 SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
120 CreateNamedTempFileAndClose(
121 file::JoinPath(GetTestTempPath(), "testdir_")));
122 SAPI_ASSERT_OK_AND_ASSIGN(std::string symlink_path,
123 CreateNamedTempFileAndClose(
124 file::JoinPath(GetTestTempPath(), "testdir_")));
125
126 ASSERT_THAT(unlink(symlink_path.c_str()), Eq(0));
127 ASSERT_THAT(symlink(path.c_str(), symlink_path.c_str()), Eq(0));
128
129 EXPECT_THAT(mounts.AddFileAt(path, "/a"), IsOk());
130 EXPECT_THAT(mounts.AddFileAt(path, "/a"), IsOk());
131 EXPECT_THAT(mounts.AddFileAt(symlink_path, "/a"), IsOk());
132 }
133
TEST(MountTreeTest,TestMultipleInsertionUpgradeToWritable)134 TEST(MountTreeTest, TestMultipleInsertionUpgradeToWritable) {
135 Mounts mounts;
136 EXPECT_THAT(mounts.AddFile("/a"), IsOk());
137 EXPECT_THAT(mounts.AddFile("/a", /*is_ro=*/false), IsOk());
138 EXPECT_THAT(mounts.AddDirectory("/b"), IsOk());
139 EXPECT_THAT(mounts.AddDirectory("/b", /*is_ro=*/false), IsOk());
140 EXPECT_THAT(mounts.AddFile("/c", /*is_ro=*/false), IsOk());
141 EXPECT_THAT(mounts.AddFile("/c"), IsOk());
142 EXPECT_THAT(mounts.AddDirectory("/d", /*is_ro=*/false), IsOk());
143 EXPECT_THAT(mounts.AddDirectory("/d"), IsOk());
144 }
145
TEST(MountTreeTest,TestMultipleInsertionDirSymlink)146 TEST(MountTreeTest, TestMultipleInsertionDirSymlink) {
147 Mounts mounts;
148
149 SAPI_ASSERT_OK_AND_ASSIGN(
150 std::string path,
151 CreateTempDir(file::JoinPath(GetTestTempPath(), "testdir_")));
152 SAPI_ASSERT_OK_AND_ASSIGN(std::string symlink_path,
153 CreateNamedTempFileAndClose(
154 file::JoinPath(GetTestTempPath(), "testdir_")));
155
156 ASSERT_THAT(unlink(symlink_path.c_str()), Eq(0));
157 ASSERT_THAT(symlink(path.c_str(), symlink_path.c_str()), Eq(0));
158
159 EXPECT_THAT(mounts.AddDirectoryAt(path, "/a"), IsOk());
160 EXPECT_THAT(mounts.AddDirectoryAt(path, "/a"), IsOk());
161 EXPECT_THAT(mounts.AddDirectoryAt(symlink_path, "/a"), IsOk());
162 EXPECT_THAT(mounts.AddDirectoryAt(symlink_path, "/a"), IsOk());
163 }
164
TEST(MountTreeTest,TestMultipleInsertion)165 TEST(MountTreeTest, TestMultipleInsertion) {
166 Mounts mounts;
167
168 EXPECT_THAT(mounts.AddFile("/c/d"), IsOk());
169
170 EXPECT_THAT(mounts.AddFile("/c"),
171 StatusIs(absl::StatusCode::kFailedPrecondition));
172 EXPECT_THAT(mounts.AddFileAt("/f", "/c"),
173 StatusIs(absl::StatusCode::kFailedPrecondition));
174 EXPECT_THAT(mounts.AddDirectoryAt("/f", "/c"), IsOk());
175
176 EXPECT_THAT(mounts.AddFile("/c/d/e"),
177 StatusIs(absl::StatusCode::kFailedPrecondition));
178 EXPECT_THAT(mounts.AddFileAt("/f", "/c/d/e"),
179 StatusIs(absl::StatusCode::kFailedPrecondition));
180 EXPECT_THAT(mounts.AddDirectoryAt("/f", "/c/d/e"),
181 StatusIs(absl::StatusCode::kFailedPrecondition));
182 }
183
TEST(MountTreeTest,TestEvilNullByte)184 TEST(MountTreeTest, TestEvilNullByte) {
185 Mounts mounts;
186 // create the filename with a null byte this way as g4 fix forces newlines
187 // otherwise.
188 std::string filename = "/a/b";
189 filename[2] = '\0';
190
191 EXPECT_THAT(mounts.AddFile(filename),
192 StatusIs(absl::StatusCode::kInvalidArgument));
193 EXPECT_THAT(mounts.AddFileAt(filename, "/a"),
194 StatusIs(absl::StatusCode::kInvalidArgument));
195 EXPECT_THAT(mounts.AddFileAt("/a", filename),
196 StatusIs(absl::StatusCode::kInvalidArgument));
197 EXPECT_THAT(mounts.AddDirectoryAt(filename, "/a"),
198 StatusIs(absl::StatusCode::kInvalidArgument));
199 EXPECT_THAT(mounts.AddDirectoryAt("/a", filename),
200 StatusIs(absl::StatusCode::kInvalidArgument));
201 EXPECT_THAT(mounts.AddTmpfs(filename, kTmpfsSize),
202 StatusIs(absl::StatusCode::kInvalidArgument));
203 EXPECT_THAT(mounts.Remove(filename),
204 StatusIs(absl::StatusCode::kInvalidArgument));
205 }
206
TEST(MountTreeTest,TestMinimalDynamicBinary)207 TEST(MountTreeTest, TestMinimalDynamicBinary) {
208 Mounts mounts;
209 EXPECT_THAT(mounts.AddMappingsForBinary(
210 GetTestSourcePath("sandbox2/testcases/minimal_dynamic")),
211 IsOk());
212 EXPECT_THAT(mounts.AddFile("/lib/x86_64-linux-gnu/libc.so.6"), IsOk());
213 }
214
TEST(MountTreeTest,TestList)215 TEST(MountTreeTest, TestList) {
216 struct TestCase {
217 const char* path;
218 const bool is_ro;
219 };
220 // clang-format off
221 constexpr TestCase kTestCases[] = {
222 // NOTE: Directories have a trailing '/'; files don't.
223 {"/a/b", true},
224 {"/a/c/", true},
225 {"/a/c/d/e/f/g", true},
226 {"/h", true},
227 {"/i/j/k", false},
228 {"/i/l/", false},
229 };
230 // clang-format on
231
232 Mounts mounts;
233
234 // Create actual directories and files on disk and selectively add
235 for (const auto& test_case : kTestCases) {
236 const auto inside_path = test_case.path;
237 const std::string outside_path = absl::StrCat("/some/dir/", inside_path);
238 if (absl::EndsWith(outside_path, "/")) {
239 ASSERT_THAT(
240 mounts.AddDirectoryAt(file::CleanPath(outside_path),
241 file::CleanPath(inside_path), test_case.is_ro),
242 IsOk());
243 } else {
244 ASSERT_THAT(
245 mounts.AddFileAt(file::CleanPath(outside_path),
246 file::CleanPath(inside_path), test_case.is_ro),
247 IsOk());
248 }
249 }
250
251 ASSERT_THAT(mounts.AddTmpfs(file::CleanPath("/d"), 1024 * 1024), IsOk());
252
253 std::vector<std::string> outside_entries;
254 std::vector<std::string> inside_entries;
255 mounts.RecursivelyListMounts(&outside_entries, &inside_entries);
256
257 // clang-format off
258 EXPECT_THAT(
259 inside_entries,
260 UnorderedElementsAreArray({
261 "R /a/b",
262 "R /a/c/",
263 "R /a/c/d/e/f/g",
264 "R /h",
265 "W /i/j/k",
266 "W /i/l/",
267 "/d",
268 }));
269 EXPECT_THAT(
270 outside_entries,
271 UnorderedElementsAreArray({
272 absl::StrCat("/some/dir/", "a/b"),
273 absl::StrCat("/some/dir/", "a/c/"),
274 absl::StrCat("/some/dir/", "a/c/d/e/f/g"),
275 absl::StrCat("/some/dir/", "h"),
276 absl::StrCat("/some/dir/", "i/j/k"),
277 absl::StrCat("/some/dir/", "i/l/"),
278 absl::StrCat("tmpfs: size=", 1024*1024),
279 }));
280 // clang-format on
281 }
282
TEST(MountTreeTest,TestIsWritable)283 TEST(MountTreeTest, TestIsWritable) {
284 MountTree::Node nodes[7];
285 MountTree::FileNode* fn0 = nodes[0].mutable_file_node();
286 fn0->set_writable(false);
287 fn0->set_outside("foo");
288 MountTree::FileNode* fn1 = nodes[1].mutable_file_node();
289 fn1->set_writable(true);
290 fn1->set_outside("bar");
291 MountTree::DirNode* dn0 = nodes[2].mutable_dir_node();
292 dn0->set_writable(false);
293 dn0->set_outside("foo");
294 MountTree::DirNode* dn1 = nodes[3].mutable_dir_node();
295 dn1->set_writable(true);
296 dn1->set_outside("bar");
297 MountTree::RootNode* rn0 = nodes[4].mutable_root_node();
298 rn0->set_writable(false);
299 MountTree::RootNode* rn1 = nodes[5].mutable_root_node();
300 rn1->set_writable(true);
301 MountTree::TmpfsNode* tn0 = nodes[6].mutable_tmpfs_node();
302 tn0->set_tmpfs_options("option1");
303
304 EXPECT_FALSE(internal::IsWritable(nodes[0]));
305 EXPECT_TRUE(internal::IsWritable(nodes[1]));
306 EXPECT_FALSE(internal::IsWritable(nodes[2]));
307 EXPECT_TRUE(internal::IsWritable(nodes[3]));
308 EXPECT_FALSE(internal::IsWritable(nodes[4]));
309 EXPECT_TRUE(internal::IsWritable(nodes[5]));
310 EXPECT_FALSE(internal::IsWritable(nodes[6]));
311 }
312
TEST(MountTreeTest,TestHasSameTarget)313 TEST(MountTreeTest, TestHasSameTarget) {
314 MountTree::Node nodes[10];
315 MountTree::FileNode* fn0 = nodes[0].mutable_file_node();
316 fn0->set_writable(false);
317 fn0->set_outside("foo");
318 MountTree::FileNode* fn1 = nodes[1].mutable_file_node();
319 fn1->set_writable(true);
320 fn1->set_outside("foo");
321 MountTree::FileNode* fn2 = nodes[2].mutable_file_node();
322 fn2->set_writable(false);
323 fn2->set_outside("bar");
324 MountTree::DirNode* dn0 = nodes[3].mutable_dir_node();
325 dn0->set_writable(false);
326 dn0->set_outside("foo");
327 MountTree::DirNode* dn1 = nodes[4].mutable_dir_node();
328 dn1->set_writable(true);
329 dn1->set_outside("foo");
330 MountTree::DirNode* dn2 = nodes[5].mutable_dir_node();
331 dn2->set_writable(false);
332 dn2->set_outside("bar");
333 MountTree::TmpfsNode* tn0 = nodes[6].mutable_tmpfs_node();
334 tn0->set_tmpfs_options("option1");
335 MountTree::TmpfsNode* tn1 = nodes[7].mutable_tmpfs_node();
336 tn1->set_tmpfs_options("option2");
337 MountTree::RootNode* rn0 = nodes[8].mutable_root_node();
338 rn0->set_writable(false);
339 MountTree::RootNode* rn1 = nodes[9].mutable_root_node();
340 rn1->set_writable(true);
341
342 // Compare same file nodes
343 EXPECT_TRUE(internal::HasSameTarget(nodes[0], nodes[0]));
344 // Compare almost same file nodes (ro vs rw)
345 EXPECT_TRUE(internal::HasSameTarget(nodes[0], nodes[1]));
346 // Compare different file nodes
347 EXPECT_FALSE(internal::HasSameTarget(nodes[0], nodes[2]));
348 // Compare file node with dir node
349 EXPECT_FALSE(internal::HasSameTarget(nodes[0], nodes[3]));
350
351 // Compare same dir nodes
352 EXPECT_TRUE(internal::HasSameTarget(nodes[3], nodes[3]));
353 // Compare almost same dir nodes (ro vs rw)
354 EXPECT_TRUE(internal::HasSameTarget(nodes[3], nodes[4]));
355 // Compare different dir nodes
356 EXPECT_FALSE(internal::HasSameTarget(nodes[3], nodes[5]));
357 // Compare dir node with tmpfs node
358 EXPECT_FALSE(internal::HasSameTarget(nodes[3], nodes[6]));
359
360 // Compare same tmpfs nodes
361 EXPECT_TRUE(internal::HasSameTarget(nodes[6], nodes[6]));
362 // Compare different tmpfs nodes
363 EXPECT_FALSE(internal::HasSameTarget(nodes[6], nodes[7]));
364 // Compare dir node with root node
365 EXPECT_FALSE(internal::HasSameTarget(nodes[6], nodes[8]));
366
367 // Compare same root nodes
368 EXPECT_TRUE(internal::HasSameTarget(nodes[8], nodes[8]));
369 // Compare almost same root nodes (ro vs rw)
370 EXPECT_TRUE(internal::HasSameTarget(nodes[8], nodes[9]));
371 }
372
TEST(MountTreeTest,TestNodeEquivalence)373 TEST(MountTreeTest, TestNodeEquivalence) {
374 MountTree::Node nodes[8];
375 MountTree::FileNode* fn0 = nodes[0].mutable_file_node();
376 fn0->set_writable(false);
377 fn0->set_outside("foo");
378 MountTree::FileNode* fn1 = nodes[1].mutable_file_node();
379 fn1->set_writable(false);
380 fn1->set_outside("bar");
381 MountTree::DirNode* dn0 = nodes[2].mutable_dir_node();
382 dn0->set_writable(false);
383 dn0->set_outside("foo");
384 MountTree::DirNode* dn1 = nodes[3].mutable_dir_node();
385 dn1->set_writable(false);
386 dn1->set_outside("bar");
387 MountTree::TmpfsNode* tn0 = nodes[4].mutable_tmpfs_node();
388 tn0->set_tmpfs_options("option1");
389 MountTree::TmpfsNode* tn1 = nodes[5].mutable_tmpfs_node();
390 tn1->set_tmpfs_options("option2");
391 MountTree::RootNode* rn0 = nodes[6].mutable_root_node();
392 rn0->set_writable(false);
393 MountTree::RootNode* rn1 = nodes[7].mutable_root_node();
394 rn1->set_writable(true);
395
396 for (const MountTree::Node& n : nodes) {
397 ASSERT_TRUE(n.IsInitialized());
398 }
399 // Compare same file nodes
400 EXPECT_TRUE(internal::IsEquivalentNode(nodes[0], nodes[0]));
401 // Compare with different file node
402 EXPECT_FALSE(internal::IsEquivalentNode(nodes[0], nodes[1]));
403 // compare file node with dir node
404 EXPECT_FALSE(internal::IsEquivalentNode(nodes[0], nodes[2]));
405
406 // Compare same dir nodes
407 EXPECT_TRUE(internal::IsEquivalentNode(nodes[2], nodes[2]));
408 // Compare with different dir node
409 EXPECT_FALSE(internal::IsEquivalentNode(nodes[2], nodes[3]));
410 // Compare dir node with tmpfs node
411 EXPECT_FALSE(internal::IsEquivalentNode(nodes[2], nodes[4]));
412
413 // Compare same tmpfs nodes
414 EXPECT_TRUE(internal::IsEquivalentNode(nodes[4], nodes[4]));
415 // Compare with different tmpfs nodes
416 EXPECT_FALSE(internal::IsEquivalentNode(nodes[4], nodes[5]));
417 // Compare tmpfs node with root node
418 EXPECT_FALSE(internal::IsEquivalentNode(nodes[4], nodes[6]));
419
420 // Compare same root nodes
421 EXPECT_TRUE(internal::IsEquivalentNode(nodes[6], nodes[6]));
422 // Compare different root node
423 EXPECT_FALSE(internal::IsEquivalentNode(nodes[6], nodes[7]));
424 // Compare root node with file node
425 EXPECT_FALSE(internal::IsEquivalentNode(nodes[6], nodes[0]));
426 }
427
TEST(MountsResolvePathTest,Files)428 TEST(MountsResolvePathTest, Files) {
429 Mounts mounts;
430 ASSERT_THAT(mounts.AddFileAt("/A", "/a"), IsOk());
431 ASSERT_THAT(mounts.AddFileAt("/B", "/d/b"), IsOk());
432 ASSERT_THAT(mounts.AddFileAt("/C/D/E", "/d/c/e/f/h"), IsOk());
433 std::string resolved;
434 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/a"));
435 EXPECT_THAT(resolved, StrEq("/A"));
436 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/b"));
437 EXPECT_THAT(resolved, StrEq("/B"));
438 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/c/e/f/h"));
439 EXPECT_THAT(resolved, StrEq("/C/D/E"));
440 ASSERT_THAT(mounts.ResolvePath("/f"), StatusIs(absl::StatusCode::kNotFound));
441 ASSERT_THAT(mounts.ResolvePath("/d"), StatusIs(absl::StatusCode::kNotFound));
442 ASSERT_THAT(mounts.ResolvePath("/d/c/e/f"),
443 StatusIs(absl::StatusCode::kNotFound));
444 ASSERT_THAT(mounts.ResolvePath("/d/d"),
445 StatusIs(absl::StatusCode::kNotFound));
446 }
447
TEST(MountsResolvePathTest,Dirs)448 TEST(MountsResolvePathTest, Dirs) {
449 Mounts mounts;
450 ASSERT_THAT(mounts.AddDirectoryAt("/A", "/a"), IsOk());
451 ASSERT_THAT(mounts.AddDirectoryAt("/B", "/d/b"), IsOk());
452 ASSERT_THAT(mounts.AddDirectoryAt("/C/D/E", "/d/c/e/f/h"), IsOk());
453 ASSERT_THAT(mounts.AddFileAt("/J/G/H", "/d/c/e/f/h/j"), IsOk());
454 ASSERT_THAT(mounts.AddDirectoryAt("/K/L/M", "/d/c/e/f/h/k"), IsOk());
455 std::string resolved;
456 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/a"));
457 EXPECT_THAT(resolved, StrEq("/A"));
458 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/a/b/c/d/e"));
459 EXPECT_THAT(resolved, StrEq("/A/b/c/d/e"));
460 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/b"));
461 EXPECT_THAT(resolved, StrEq("/B"));
462 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/c/e/f/h"));
463 EXPECT_THAT(resolved, StrEq("/C/D/E"));
464 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/c/e/f/h/i"));
465 EXPECT_THAT(resolved, StrEq("/C/D/E/i"));
466 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/c/e/f/h/j"));
467 EXPECT_THAT(resolved, StrEq("/J/G/H"));
468 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/c/e/f/h/k"));
469 EXPECT_THAT(resolved, StrEq("/K/L/M"));
470 SAPI_ASSERT_OK_AND_ASSIGN(resolved, mounts.ResolvePath("/d/c/e/f/h/k/a"));
471 EXPECT_THAT(resolved, StrEq("/K/L/M/a"));
472 ASSERT_THAT(mounts.ResolvePath("/f"), StatusIs(absl::StatusCode::kNotFound));
473 ASSERT_THAT(mounts.ResolvePath("/d"), StatusIs(absl::StatusCode::kNotFound));
474 ASSERT_THAT(mounts.ResolvePath("/d/c/e/f"),
475 StatusIs(absl::StatusCode::kNotFound));
476 ASSERT_THAT(mounts.ResolvePath("/d/d"),
477 StatusIs(absl::StatusCode::kNotFound));
478 }
479
480 } // namespace
481 } // namespace sandbox2
482