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