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