1*ec63e07aSXin Li // Copyright 2019 Google LLC
2*ec63e07aSXin Li //
3*ec63e07aSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*ec63e07aSXin Li // you may not use this file except in compliance with the License.
5*ec63e07aSXin Li // You may obtain a copy of the License at
6*ec63e07aSXin Li //
7*ec63e07aSXin Li // https://www.apache.org/licenses/LICENSE-2.0
8*ec63e07aSXin Li //
9*ec63e07aSXin Li // Unless required by applicable law or agreed to in writing, software
10*ec63e07aSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*ec63e07aSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ec63e07aSXin Li // See the License for the specific language governing permissions and
13*ec63e07aSXin Li // limitations under the License.
14*ec63e07aSXin Li
15*ec63e07aSXin Li #include "sandboxed_api/sandbox2/mounts.h"
16*ec63e07aSXin Li
17*ec63e07aSXin Li #include <fcntl.h>
18*ec63e07aSXin Li #include <sys/mount.h>
19*ec63e07aSXin Li #include <sys/stat.h>
20*ec63e07aSXin Li #include <sys/statvfs.h>
21*ec63e07aSXin Li #include <unistd.h>
22*ec63e07aSXin Li
23*ec63e07aSXin Li #include <cerrno>
24*ec63e07aSXin Li #include <cstddef>
25*ec63e07aSXin Li #include <cstdint>
26*ec63e07aSXin Li #include <string>
27*ec63e07aSXin Li #include <tuple>
28*ec63e07aSXin Li #include <utility>
29*ec63e07aSXin Li #include <vector>
30*ec63e07aSXin Li
31*ec63e07aSXin Li #include "absl/container/flat_hash_set.h"
32*ec63e07aSXin Li #include "absl/status/status.h"
33*ec63e07aSXin Li #include "absl/status/statusor.h"
34*ec63e07aSXin Li #include "absl/strings/match.h"
35*ec63e07aSXin Li #include "absl/strings/str_cat.h"
36*ec63e07aSXin Li #include "absl/strings/str_join.h"
37*ec63e07aSXin Li #include "absl/strings/str_split.h"
38*ec63e07aSXin Li #include "absl/strings/string_view.h"
39*ec63e07aSXin Li #include "absl/strings/strip.h"
40*ec63e07aSXin Li #include "sandboxed_api/config.h"
41*ec63e07aSXin Li #include "sandboxed_api/sandbox2/mount_tree.pb.h"
42*ec63e07aSXin Li #include "sandboxed_api/sandbox2/util/minielf.h"
43*ec63e07aSXin Li #include "sandboxed_api/util/fileops.h"
44*ec63e07aSXin Li #include "sandboxed_api/util/path.h"
45*ec63e07aSXin Li #include "sandboxed_api/util/raw_logging.h"
46*ec63e07aSXin Li #include "sandboxed_api/util/status_macros.h"
47*ec63e07aSXin Li
48*ec63e07aSXin Li namespace sandbox2 {
49*ec63e07aSXin Li namespace {
50*ec63e07aSXin Li
51*ec63e07aSXin Li namespace cpu = ::sapi::cpu;
52*ec63e07aSXin Li namespace file_util = ::sapi::file_util;
53*ec63e07aSXin Li namespace host_cpu = ::sapi::host_cpu;
54*ec63e07aSXin Li
PathContainsNullByte(absl::string_view path)55*ec63e07aSXin Li bool PathContainsNullByte(absl::string_view path) {
56*ec63e07aSXin Li return absl::StrContains(path, '\0');
57*ec63e07aSXin Li }
58*ec63e07aSXin Li
GetOutsidePath(const MountTree::Node & node)59*ec63e07aSXin Li absl::string_view GetOutsidePath(const MountTree::Node& node) {
60*ec63e07aSXin Li switch (node.node_case()) {
61*ec63e07aSXin Li case MountTree::Node::kFileNode:
62*ec63e07aSXin Li return node.file_node().outside();
63*ec63e07aSXin Li case MountTree::Node::kDirNode:
64*ec63e07aSXin Li return node.dir_node().outside();
65*ec63e07aSXin Li default:
66*ec63e07aSXin Li SAPI_RAW_LOG(FATAL, "Invalid node type");
67*ec63e07aSXin Li }
68*ec63e07aSXin Li }
69*ec63e07aSXin Li
ExistingPathInsideDir(absl::string_view dir_path,absl::string_view relative_path)70*ec63e07aSXin Li absl::StatusOr<std::string> ExistingPathInsideDir(
71*ec63e07aSXin Li absl::string_view dir_path, absl::string_view relative_path) {
72*ec63e07aSXin Li auto path =
73*ec63e07aSXin Li sapi::file::CleanPath(sapi::file::JoinPath(dir_path, relative_path));
74*ec63e07aSXin Li if (file_util::fileops::StripBasename(path) != dir_path) {
75*ec63e07aSXin Li return absl::InvalidArgumentError("Relative path goes above the base dir");
76*ec63e07aSXin Li }
77*ec63e07aSXin Li if (!file_util::fileops::Exists(path, false)) {
78*ec63e07aSXin Li return absl::NotFoundError(absl::StrCat("Does not exist: ", path));
79*ec63e07aSXin Li }
80*ec63e07aSXin Li return path;
81*ec63e07aSXin Li }
82*ec63e07aSXin Li
ValidateInterpreter(absl::string_view interpreter)83*ec63e07aSXin Li absl::Status ValidateInterpreter(absl::string_view interpreter) {
84*ec63e07aSXin Li const absl::flat_hash_set<std::string> allowed_interpreters = {
85*ec63e07aSXin Li "/lib64/ld-linux-x86-64.so.2",
86*ec63e07aSXin Li "/lib64/ld64.so.2", // PPC64
87*ec63e07aSXin Li "/lib/ld-linux-aarch64.so.1", // AArch64
88*ec63e07aSXin Li "/lib/ld-linux-armhf.so.3", // Arm
89*ec63e07aSXin Li "/system/bin/linker64", // android_arm64
90*ec63e07aSXin Li };
91*ec63e07aSXin Li
92*ec63e07aSXin Li if (!allowed_interpreters.contains(interpreter)) {
93*ec63e07aSXin Li return absl::InvalidArgumentError(
94*ec63e07aSXin Li absl::StrCat("Interpreter not on the whitelist: ", interpreter));
95*ec63e07aSXin Li }
96*ec63e07aSXin Li return absl::OkStatus();
97*ec63e07aSXin Li }
98*ec63e07aSXin Li
ResolveLibraryPath(absl::string_view lib_name,const std::vector<std::string> & search_paths)99*ec63e07aSXin Li std::string ResolveLibraryPath(absl::string_view lib_name,
100*ec63e07aSXin Li const std::vector<std::string>& search_paths) {
101*ec63e07aSXin Li for (const auto& search_path : search_paths) {
102*ec63e07aSXin Li if (auto path_or = ExistingPathInsideDir(search_path, lib_name);
103*ec63e07aSXin Li path_or.ok()) {
104*ec63e07aSXin Li return path_or.value();
105*ec63e07aSXin Li }
106*ec63e07aSXin Li }
107*ec63e07aSXin Li return "";
108*ec63e07aSXin Li }
109*ec63e07aSXin Li
GetPlatformCPUName()110*ec63e07aSXin Li constexpr absl::string_view GetPlatformCPUName() {
111*ec63e07aSXin Li switch (host_cpu::Architecture()) {
112*ec63e07aSXin Li case cpu::kX8664:
113*ec63e07aSXin Li return "x86_64";
114*ec63e07aSXin Li case cpu::kPPC64LE:
115*ec63e07aSXin Li return "ppc64";
116*ec63e07aSXin Li case cpu::kArm64:
117*ec63e07aSXin Li return "aarch64";
118*ec63e07aSXin Li default:
119*ec63e07aSXin Li return "unknown";
120*ec63e07aSXin Li }
121*ec63e07aSXin Li }
122*ec63e07aSXin Li
GetPlatform(absl::string_view interpreter)123*ec63e07aSXin Li std::string GetPlatform(absl::string_view interpreter) {
124*ec63e07aSXin Li return absl::StrCat(GetPlatformCPUName(), "-linux-gnu");
125*ec63e07aSXin Li }
126*ec63e07aSXin Li
127*ec63e07aSXin Li } // namespace
128*ec63e07aSXin Li
129*ec63e07aSXin Li namespace internal {
130*ec63e07aSXin Li
IsSameFile(const std::string & path1,const std::string & path2)131*ec63e07aSXin Li bool IsSameFile(const std::string& path1, const std::string& path2) {
132*ec63e07aSXin Li if (path1 == path2) {
133*ec63e07aSXin Li return true;
134*ec63e07aSXin Li }
135*ec63e07aSXin Li
136*ec63e07aSXin Li struct stat stat1, stat2;
137*ec63e07aSXin Li if (stat(path1.c_str(), &stat1) == -1) {
138*ec63e07aSXin Li return false;
139*ec63e07aSXin Li }
140*ec63e07aSXin Li
141*ec63e07aSXin Li if (stat(path2.c_str(), &stat2) == -1) {
142*ec63e07aSXin Li return false;
143*ec63e07aSXin Li }
144*ec63e07aSXin Li
145*ec63e07aSXin Li return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino;
146*ec63e07aSXin Li }
147*ec63e07aSXin Li
IsWritable(const MountTree::Node & node)148*ec63e07aSXin Li bool IsWritable(const MountTree::Node& node) {
149*ec63e07aSXin Li switch (node.node_case()) {
150*ec63e07aSXin Li case MountTree::Node::kFileNode:
151*ec63e07aSXin Li return node.file_node().writable();
152*ec63e07aSXin Li case MountTree::Node::kDirNode:
153*ec63e07aSXin Li return node.dir_node().writable();
154*ec63e07aSXin Li case MountTree::Node::kRootNode:
155*ec63e07aSXin Li return node.root_node().writable();
156*ec63e07aSXin Li default:
157*ec63e07aSXin Li return false;
158*ec63e07aSXin Li }
159*ec63e07aSXin Li }
160*ec63e07aSXin Li
HasSameTarget(const MountTree::Node & n1,const MountTree::Node & n2)161*ec63e07aSXin Li bool HasSameTarget(const MountTree::Node& n1, const MountTree::Node& n2) {
162*ec63e07aSXin Li // Return early when node types are different
163*ec63e07aSXin Li if (n1.node_case() != n2.node_case()) {
164*ec63e07aSXin Li return false;
165*ec63e07aSXin Li }
166*ec63e07aSXin Li // Compare proto fields
167*ec63e07aSXin Li switch (n1.node_case()) {
168*ec63e07aSXin Li case MountTree::Node::kFileNode:
169*ec63e07aSXin Li // Check whether files are the same (e.g. symlinks / hardlinks)
170*ec63e07aSXin Li return IsSameFile(n1.file_node().outside(), n2.file_node().outside());
171*ec63e07aSXin Li case MountTree::Node::kDirNode:
172*ec63e07aSXin Li // Check whether dirs are the same (e.g. symlinks / hardlinks)
173*ec63e07aSXin Li return IsSameFile(n1.dir_node().outside(), n2.dir_node().outside());
174*ec63e07aSXin Li case MountTree::Node::kTmpfsNode:
175*ec63e07aSXin Li return n1.tmpfs_node().tmpfs_options() == n2.tmpfs_node().tmpfs_options();
176*ec63e07aSXin Li case MountTree::Node::kRootNode:
177*ec63e07aSXin Li return true;
178*ec63e07aSXin Li default:
179*ec63e07aSXin Li return false;
180*ec63e07aSXin Li }
181*ec63e07aSXin Li }
182*ec63e07aSXin Li
IsEquivalentNode(const MountTree::Node & n1,const MountTree::Node & n2)183*ec63e07aSXin Li bool IsEquivalentNode(const MountTree::Node& n1, const MountTree::Node& n2) {
184*ec63e07aSXin Li if (!HasSameTarget(n1, n2)) {
185*ec63e07aSXin Li return false;
186*ec63e07aSXin Li }
187*ec63e07aSXin Li
188*ec63e07aSXin Li // Compare proto fields
189*ec63e07aSXin Li switch (n1.node_case()) {
190*ec63e07aSXin Li case MountTree::Node::kFileNode:
191*ec63e07aSXin Li return n1.file_node().writable() == n2.file_node().writable();
192*ec63e07aSXin Li case MountTree::Node::kDirNode:
193*ec63e07aSXin Li return n1.dir_node().writable() == n2.dir_node().writable();
194*ec63e07aSXin Li case MountTree::Node::kTmpfsNode:
195*ec63e07aSXin Li return true;
196*ec63e07aSXin Li case MountTree::Node::kRootNode:
197*ec63e07aSXin Li return n1.root_node().writable() == n2.root_node().writable();
198*ec63e07aSXin Li default:
199*ec63e07aSXin Li return false;
200*ec63e07aSXin Li }
201*ec63e07aSXin Li }
202*ec63e07aSXin Li
203*ec63e07aSXin Li } // namespace internal
204*ec63e07aSXin Li
Remove(absl::string_view path)205*ec63e07aSXin Li absl::Status Mounts::Remove(absl::string_view path) {
206*ec63e07aSXin Li if (PathContainsNullByte(path)) {
207*ec63e07aSXin Li return absl::InvalidArgumentError(
208*ec63e07aSXin Li absl::StrCat("Path contains a null byte: ", path));
209*ec63e07aSXin Li }
210*ec63e07aSXin Li
211*ec63e07aSXin Li std::string fixed_path = sapi::file::CleanPath(path);
212*ec63e07aSXin Li if (!sapi::file::IsAbsolutePath(fixed_path)) {
213*ec63e07aSXin Li return absl::InvalidArgumentError("Only absolute paths are supported");
214*ec63e07aSXin Li }
215*ec63e07aSXin Li
216*ec63e07aSXin Li if (fixed_path == "/") {
217*ec63e07aSXin Li return absl::InvalidArgumentError("Cannot remove root");
218*ec63e07aSXin Li }
219*ec63e07aSXin Li std::vector<absl::string_view> parts =
220*ec63e07aSXin Li absl::StrSplit(absl::StripPrefix(fixed_path, "/"), '/');
221*ec63e07aSXin Li
222*ec63e07aSXin Li MountTree* curtree = &mount_tree_;
223*ec63e07aSXin Li for (absl::string_view part : parts) {
224*ec63e07aSXin Li if (curtree->has_node() && curtree->node().has_file_node()) {
225*ec63e07aSXin Li return absl::NotFoundError(
226*ec63e07aSXin Li absl::StrCat("File node is mounted at parent of: ", path));
227*ec63e07aSXin Li }
228*ec63e07aSXin Li auto it = curtree->mutable_entries()->find(std::string(part));
229*ec63e07aSXin Li if (it == curtree->mutable_entries()->end()) {
230*ec63e07aSXin Li return absl::NotFoundError(
231*ec63e07aSXin Li absl::StrCat("Path does not exist in mounts: ", path));
232*ec63e07aSXin Li }
233*ec63e07aSXin Li curtree = &it->second;
234*ec63e07aSXin Li }
235*ec63e07aSXin Li curtree->clear_node();
236*ec63e07aSXin Li curtree->clear_entries();
237*ec63e07aSXin Li return absl::OkStatus();
238*ec63e07aSXin Li }
239*ec63e07aSXin Li
Insert(absl::string_view path,const MountTree::Node & new_node)240*ec63e07aSXin Li absl::Status Mounts::Insert(absl::string_view path,
241*ec63e07aSXin Li const MountTree::Node& new_node) {
242*ec63e07aSXin Li // Some sandboxes allow the inside/outside paths to be partially
243*ec63e07aSXin Li // user-controlled with some sanitization.
244*ec63e07aSXin Li // Since we're handling C++ strings and later convert them to C style
245*ec63e07aSXin Li // strings, a null byte in a path component might silently truncate the path
246*ec63e07aSXin Li // and mount something not expected by the caller. Check for null bytes in the
247*ec63e07aSXin Li // strings to protect against this.
248*ec63e07aSXin Li if (PathContainsNullByte(path)) {
249*ec63e07aSXin Li return absl::InvalidArgumentError(
250*ec63e07aSXin Li absl::StrCat("Inside path contains a null byte: ", path));
251*ec63e07aSXin Li }
252*ec63e07aSXin Li switch (new_node.node_case()) {
253*ec63e07aSXin Li case MountTree::Node::kFileNode:
254*ec63e07aSXin Li case MountTree::Node::kDirNode: {
255*ec63e07aSXin Li auto outside_path = GetOutsidePath(new_node);
256*ec63e07aSXin Li if (outside_path.empty()) {
257*ec63e07aSXin Li return absl::InvalidArgumentError("Outside path cannot be empty");
258*ec63e07aSXin Li }
259*ec63e07aSXin Li if (PathContainsNullByte(outside_path)) {
260*ec63e07aSXin Li return absl::InvalidArgumentError(
261*ec63e07aSXin Li absl::StrCat("Outside path contains a null byte: ", outside_path));
262*ec63e07aSXin Li }
263*ec63e07aSXin Li break;
264*ec63e07aSXin Li }
265*ec63e07aSXin Li case MountTree::Node::kRootNode:
266*ec63e07aSXin Li return absl::InvalidArgumentError("Cannot insert a RootNode");
267*ec63e07aSXin Li case MountTree::Node::kTmpfsNode:
268*ec63e07aSXin Li case MountTree::Node::NODE_NOT_SET:
269*ec63e07aSXin Li break;
270*ec63e07aSXin Li }
271*ec63e07aSXin Li
272*ec63e07aSXin Li std::string fixed_path = sapi::file::CleanPath(path);
273*ec63e07aSXin Li if (!sapi::file::IsAbsolutePath(fixed_path)) {
274*ec63e07aSXin Li return absl::InvalidArgumentError("Only absolute paths are supported");
275*ec63e07aSXin Li }
276*ec63e07aSXin Li
277*ec63e07aSXin Li if (fixed_path == "/") {
278*ec63e07aSXin Li return absl::InvalidArgumentError("The root already exists");
279*ec63e07aSXin Li }
280*ec63e07aSXin Li
281*ec63e07aSXin Li std::vector<absl::string_view> parts =
282*ec63e07aSXin Li absl::StrSplit(absl::StripPrefix(fixed_path, "/"), '/');
283*ec63e07aSXin Li std::string final_part(parts.back());
284*ec63e07aSXin Li parts.pop_back();
285*ec63e07aSXin Li
286*ec63e07aSXin Li MountTree* curtree = &mount_tree_;
287*ec63e07aSXin Li for (absl::string_view part : parts) {
288*ec63e07aSXin Li curtree = &(curtree->mutable_entries()
289*ec63e07aSXin Li ->insert({std::string(part), MountTree()})
290*ec63e07aSXin Li .first->second);
291*ec63e07aSXin Li if (curtree->has_node() && curtree->node().has_file_node()) {
292*ec63e07aSXin Li return absl::FailedPreconditionError(
293*ec63e07aSXin Li absl::StrCat("Cannot insert ", path,
294*ec63e07aSXin Li " since a file is mounted as a parent directory"));
295*ec63e07aSXin Li }
296*ec63e07aSXin Li }
297*ec63e07aSXin Li
298*ec63e07aSXin Li curtree = &(curtree->mutable_entries()
299*ec63e07aSXin Li ->insert({final_part, MountTree()})
300*ec63e07aSXin Li .first->second);
301*ec63e07aSXin Li
302*ec63e07aSXin Li if (curtree->has_node()) {
303*ec63e07aSXin Li if (internal::IsEquivalentNode(curtree->node(), new_node)) {
304*ec63e07aSXin Li SAPI_RAW_LOG(INFO, "Inserting %s with the same value twice",
305*ec63e07aSXin Li std::string(path).c_str());
306*ec63e07aSXin Li return absl::OkStatus();
307*ec63e07aSXin Li }
308*ec63e07aSXin Li if (internal::HasSameTarget(curtree->node(), new_node)) {
309*ec63e07aSXin Li if (!internal::IsWritable(curtree->node()) &&
310*ec63e07aSXin Li internal::IsWritable(new_node)) {
311*ec63e07aSXin Li SAPI_RAW_LOG(INFO,
312*ec63e07aSXin Li "Changing %s to writable, was inserted read-only before",
313*ec63e07aSXin Li std::string(path).c_str());
314*ec63e07aSXin Li *curtree->mutable_node() = new_node;
315*ec63e07aSXin Li return absl::OkStatus();
316*ec63e07aSXin Li }
317*ec63e07aSXin Li if (internal::IsWritable(curtree->node()) &&
318*ec63e07aSXin Li !internal::IsWritable(new_node)) {
319*ec63e07aSXin Li SAPI_RAW_LOG(INFO,
320*ec63e07aSXin Li "Inserting %s read-only is a nop, as it was inserted "
321*ec63e07aSXin Li "writable before",
322*ec63e07aSXin Li std::string(path).c_str());
323*ec63e07aSXin Li return absl::OkStatus();
324*ec63e07aSXin Li }
325*ec63e07aSXin Li }
326*ec63e07aSXin Li return absl::FailedPreconditionError(absl::StrCat(
327*ec63e07aSXin Li "Inserting ", path, " twice with conflicting values ",
328*ec63e07aSXin Li curtree->node().DebugString(), " vs. ", new_node.DebugString()));
329*ec63e07aSXin Li }
330*ec63e07aSXin Li
331*ec63e07aSXin Li if (new_node.has_file_node() && !curtree->entries().empty()) {
332*ec63e07aSXin Li return absl::FailedPreconditionError(
333*ec63e07aSXin Li absl::StrCat("Trying to mount file over existing directory at ", path));
334*ec63e07aSXin Li }
335*ec63e07aSXin Li
336*ec63e07aSXin Li *curtree->mutable_node() = new_node;
337*ec63e07aSXin Li return absl::OkStatus();
338*ec63e07aSXin Li }
339*ec63e07aSXin Li
AddFileAt(absl::string_view outside,absl::string_view inside,bool is_ro)340*ec63e07aSXin Li absl::Status Mounts::AddFileAt(absl::string_view outside,
341*ec63e07aSXin Li absl::string_view inside, bool is_ro) {
342*ec63e07aSXin Li MountTree::Node node;
343*ec63e07aSXin Li auto* file_node = node.mutable_file_node();
344*ec63e07aSXin Li file_node->set_outside(std::string(outside));
345*ec63e07aSXin Li file_node->set_writable(!is_ro);
346*ec63e07aSXin Li return Insert(inside, node);
347*ec63e07aSXin Li }
348*ec63e07aSXin Li
AddDirectoryAt(absl::string_view outside,absl::string_view inside,bool is_ro)349*ec63e07aSXin Li absl::Status Mounts::AddDirectoryAt(absl::string_view outside,
350*ec63e07aSXin Li absl::string_view inside, bool is_ro) {
351*ec63e07aSXin Li MountTree::Node node;
352*ec63e07aSXin Li auto* dir_node = node.mutable_dir_node();
353*ec63e07aSXin Li dir_node->set_outside(std::string(outside));
354*ec63e07aSXin Li dir_node->set_writable(!is_ro);
355*ec63e07aSXin Li return Insert(inside, node);
356*ec63e07aSXin Li }
357*ec63e07aSXin Li
ResolvePath(absl::string_view path) const358*ec63e07aSXin Li absl::StatusOr<std::string> Mounts::ResolvePath(absl::string_view path) const {
359*ec63e07aSXin Li if (!sapi::file::IsAbsolutePath(path)) {
360*ec63e07aSXin Li return absl::InvalidArgumentError("Path has to be absolute");
361*ec63e07aSXin Li }
362*ec63e07aSXin Li std::string fixed_path = sapi::file::CleanPath(path);
363*ec63e07aSXin Li absl::string_view tail = absl::StripPrefix(fixed_path, "/");
364*ec63e07aSXin Li
365*ec63e07aSXin Li const MountTree* curtree = &mount_tree_;
366*ec63e07aSXin Li while (!tail.empty()) {
367*ec63e07aSXin Li std::pair<absl::string_view, absl::string_view> parts =
368*ec63e07aSXin Li absl::StrSplit(tail, absl::MaxSplits('/', 1));
369*ec63e07aSXin Li const std::string cur(parts.first);
370*ec63e07aSXin Li const auto it = curtree->entries().find(cur);
371*ec63e07aSXin Li if (it == curtree->entries().end()) {
372*ec63e07aSXin Li if (curtree->node().has_dir_node()) {
373*ec63e07aSXin Li return sapi::file::JoinPath(curtree->node().dir_node().outside(), tail);
374*ec63e07aSXin Li }
375*ec63e07aSXin Li return absl::NotFoundError("Path could not be resolved in the mounts");
376*ec63e07aSXin Li }
377*ec63e07aSXin Li curtree = &it->second;
378*ec63e07aSXin Li tail = parts.second;
379*ec63e07aSXin Li }
380*ec63e07aSXin Li switch (curtree->node().node_case()) {
381*ec63e07aSXin Li case MountTree::Node::kFileNode:
382*ec63e07aSXin Li case MountTree::Node::kDirNode:
383*ec63e07aSXin Li return std::string(GetOutsidePath(curtree->node()));
384*ec63e07aSXin Li case MountTree::Node::kRootNode:
385*ec63e07aSXin Li case MountTree::Node::kTmpfsNode:
386*ec63e07aSXin Li case MountTree::Node::NODE_NOT_SET:
387*ec63e07aSXin Li break;
388*ec63e07aSXin Li }
389*ec63e07aSXin Li return absl::NotFoundError("Path could not be resolved in the mounts");
390*ec63e07aSXin Li }
391*ec63e07aSXin Li
392*ec63e07aSXin Li namespace {
393*ec63e07aSXin Li
LogContainer(const std::vector<std::string> & container)394*ec63e07aSXin Li void LogContainer(const std::vector<std::string>& container) {
395*ec63e07aSXin Li for (size_t i = 0; i < container.size(); ++i) {
396*ec63e07aSXin Li SAPI_RAW_LOG(INFO, "[%4zd]=%s", i, container[i].c_str());
397*ec63e07aSXin Li }
398*ec63e07aSXin Li }
399*ec63e07aSXin Li
400*ec63e07aSXin Li } // namespace
401*ec63e07aSXin Li
AddMappingsForBinary(const std::string & path,absl::string_view ld_library_path)402*ec63e07aSXin Li absl::Status Mounts::AddMappingsForBinary(const std::string& path,
403*ec63e07aSXin Li absl::string_view ld_library_path) {
404*ec63e07aSXin Li SAPI_ASSIGN_OR_RETURN(
405*ec63e07aSXin Li auto elf,
406*ec63e07aSXin Li ElfFile::ParseFromFile(
407*ec63e07aSXin Li path, ElfFile::kGetInterpreter | ElfFile::kLoadImportedLibraries));
408*ec63e07aSXin Li const std::string& interpreter = elf.interpreter();
409*ec63e07aSXin Li
410*ec63e07aSXin Li if (interpreter.empty()) {
411*ec63e07aSXin Li SAPI_RAW_VLOG(1, "The file %s is not a dynamic executable", path.c_str());
412*ec63e07aSXin Li return absl::OkStatus();
413*ec63e07aSXin Li }
414*ec63e07aSXin Li
415*ec63e07aSXin Li SAPI_RAW_VLOG(1, "The file %s is using interpreter %s", path.c_str(),
416*ec63e07aSXin Li interpreter.c_str());
417*ec63e07aSXin Li SAPI_RETURN_IF_ERROR(ValidateInterpreter(interpreter));
418*ec63e07aSXin Li
419*ec63e07aSXin Li std::vector<std::string> search_paths;
420*ec63e07aSXin Li // 1. LD_LIBRARY_PATH
421*ec63e07aSXin Li if (!ld_library_path.empty()) {
422*ec63e07aSXin Li std::vector<std::string> ld_library_paths =
423*ec63e07aSXin Li absl::StrSplit(ld_library_path, absl::ByAnyChar(":;"));
424*ec63e07aSXin Li search_paths.insert(search_paths.end(), ld_library_paths.begin(),
425*ec63e07aSXin Li ld_library_paths.end());
426*ec63e07aSXin Li }
427*ec63e07aSXin Li // 2. Standard paths
428*ec63e07aSXin Li search_paths.insert(search_paths.end(), {
429*ec63e07aSXin Li "/lib",
430*ec63e07aSXin Li "/lib64",
431*ec63e07aSXin Li "/usr/lib",
432*ec63e07aSXin Li "/usr/lib64",
433*ec63e07aSXin Li });
434*ec63e07aSXin Li std::vector<std::string> hw_cap_paths = {
435*ec63e07aSXin Li GetPlatform(interpreter),
436*ec63e07aSXin Li "tls",
437*ec63e07aSXin Li };
438*ec63e07aSXin Li std::vector<std::string> full_search_paths;
439*ec63e07aSXin Li for (const auto& search_path : search_paths) {
440*ec63e07aSXin Li for (int hw_caps_set = (1 << hw_cap_paths.size()) - 1; hw_caps_set >= 0;
441*ec63e07aSXin Li --hw_caps_set) {
442*ec63e07aSXin Li std::string path = search_path;
443*ec63e07aSXin Li for (int hw_cap = 0; hw_cap < hw_cap_paths.size(); ++hw_cap) {
444*ec63e07aSXin Li if ((hw_caps_set & (1 << hw_cap)) != 0) {
445*ec63e07aSXin Li path = sapi::file::JoinPath(path, hw_cap_paths[hw_cap]);
446*ec63e07aSXin Li }
447*ec63e07aSXin Li }
448*ec63e07aSXin Li if (file_util::fileops::Exists(path, /*fully_resolve=*/false)) {
449*ec63e07aSXin Li full_search_paths.push_back(path);
450*ec63e07aSXin Li }
451*ec63e07aSXin Li }
452*ec63e07aSXin Li }
453*ec63e07aSXin Li
454*ec63e07aSXin Li // Arbitrary cut-off values, so we can safely resolve the libs.
455*ec63e07aSXin Li constexpr int kMaxWorkQueueSize = 1000;
456*ec63e07aSXin Li constexpr int kMaxResolvingDepth = 10;
457*ec63e07aSXin Li constexpr int kMaxResolvedEntries = 1000;
458*ec63e07aSXin Li constexpr int kMaxLoadedEntries = 100;
459*ec63e07aSXin Li constexpr int kMaxImportedLibraries = 100;
460*ec63e07aSXin Li
461*ec63e07aSXin Li absl::flat_hash_set<std::string> imported_libraries;
462*ec63e07aSXin Li std::vector<std::pair<std::string, int>> to_resolve;
463*ec63e07aSXin Li {
464*ec63e07aSXin Li auto imported_libs = elf.imported_libraries();
465*ec63e07aSXin Li if (imported_libs.size() > kMaxWorkQueueSize) {
466*ec63e07aSXin Li return absl::FailedPreconditionError(
467*ec63e07aSXin Li "Exceeded max entries pending resolving limit");
468*ec63e07aSXin Li }
469*ec63e07aSXin Li for (const auto& imported_lib : imported_libs) {
470*ec63e07aSXin Li to_resolve.emplace_back(imported_lib, 1);
471*ec63e07aSXin Li }
472*ec63e07aSXin Li
473*ec63e07aSXin Li if (SAPI_RAW_VLOG_IS_ON(1)) {
474*ec63e07aSXin Li SAPI_RAW_VLOG(
475*ec63e07aSXin Li 1, "Resolving dynamic library dependencies of %s using these dirs:",
476*ec63e07aSXin Li path.c_str());
477*ec63e07aSXin Li LogContainer(full_search_paths);
478*ec63e07aSXin Li }
479*ec63e07aSXin Li if (SAPI_RAW_VLOG_IS_ON(2)) {
480*ec63e07aSXin Li SAPI_RAW_VLOG(2, "Direct dependencies of %s to resolve:", path.c_str());
481*ec63e07aSXin Li LogContainer(imported_libs);
482*ec63e07aSXin Li }
483*ec63e07aSXin Li }
484*ec63e07aSXin Li
485*ec63e07aSXin Li // This is DFS with an auxiliary stack
486*ec63e07aSXin Li int resolved = 0;
487*ec63e07aSXin Li int loaded = 0;
488*ec63e07aSXin Li while (!to_resolve.empty()) {
489*ec63e07aSXin Li int depth;
490*ec63e07aSXin Li std::string lib;
491*ec63e07aSXin Li std::tie(lib, depth) = to_resolve.back();
492*ec63e07aSXin Li to_resolve.pop_back();
493*ec63e07aSXin Li ++resolved;
494*ec63e07aSXin Li if (resolved > kMaxResolvedEntries) {
495*ec63e07aSXin Li return absl::FailedPreconditionError(
496*ec63e07aSXin Li "Exceeded max resolved entries limit");
497*ec63e07aSXin Li }
498*ec63e07aSXin Li if (depth > kMaxResolvingDepth) {
499*ec63e07aSXin Li return absl::FailedPreconditionError(
500*ec63e07aSXin Li "Exceeded max resolving depth limit");
501*ec63e07aSXin Li }
502*ec63e07aSXin Li std::string resolved_lib = ResolveLibraryPath(lib, full_search_paths);
503*ec63e07aSXin Li if (resolved_lib.empty()) {
504*ec63e07aSXin Li SAPI_RAW_LOG(ERROR, "Failed to resolve library: %s", lib.c_str());
505*ec63e07aSXin Li continue;
506*ec63e07aSXin Li }
507*ec63e07aSXin Li if (imported_libraries.contains(resolved_lib)) {
508*ec63e07aSXin Li continue;
509*ec63e07aSXin Li }
510*ec63e07aSXin Li
511*ec63e07aSXin Li SAPI_RAW_VLOG(1, "Resolved library: %s => %s", lib.c_str(),
512*ec63e07aSXin Li resolved_lib.c_str());
513*ec63e07aSXin Li
514*ec63e07aSXin Li imported_libraries.insert(resolved_lib);
515*ec63e07aSXin Li if (imported_libraries.size() > kMaxImportedLibraries) {
516*ec63e07aSXin Li return absl::FailedPreconditionError(
517*ec63e07aSXin Li "Exceeded max imported libraries limit");
518*ec63e07aSXin Li }
519*ec63e07aSXin Li ++loaded;
520*ec63e07aSXin Li if (loaded > kMaxLoadedEntries) {
521*ec63e07aSXin Li return absl::FailedPreconditionError("Exceeded max loaded entries limit");
522*ec63e07aSXin Li }
523*ec63e07aSXin Li SAPI_ASSIGN_OR_RETURN(
524*ec63e07aSXin Li auto lib_elf,
525*ec63e07aSXin Li ElfFile::ParseFromFile(resolved_lib, ElfFile::kLoadImportedLibraries));
526*ec63e07aSXin Li auto imported_libs = lib_elf.imported_libraries();
527*ec63e07aSXin Li if (imported_libs.size() > kMaxWorkQueueSize - to_resolve.size()) {
528*ec63e07aSXin Li return absl::FailedPreconditionError(
529*ec63e07aSXin Li "Exceeded max entries pending resolving limit");
530*ec63e07aSXin Li }
531*ec63e07aSXin Li
532*ec63e07aSXin Li if (SAPI_RAW_VLOG_IS_ON(2)) {
533*ec63e07aSXin Li SAPI_RAW_VLOG(2,
534*ec63e07aSXin Li "Transitive dependencies of %s to resolve (depth = %d): ",
535*ec63e07aSXin Li resolved_lib.c_str(), depth + 1);
536*ec63e07aSXin Li LogContainer(imported_libs);
537*ec63e07aSXin Li }
538*ec63e07aSXin Li
539*ec63e07aSXin Li for (const auto& imported_lib : imported_libs) {
540*ec63e07aSXin Li to_resolve.emplace_back(imported_lib, depth + 1);
541*ec63e07aSXin Li }
542*ec63e07aSXin Li }
543*ec63e07aSXin Li
544*ec63e07aSXin Li imported_libraries.insert(interpreter);
545*ec63e07aSXin Li for (const auto& lib : imported_libraries) {
546*ec63e07aSXin Li SAPI_RETURN_IF_ERROR(AddFile(lib));
547*ec63e07aSXin Li }
548*ec63e07aSXin Li
549*ec63e07aSXin Li return absl::OkStatus();
550*ec63e07aSXin Li }
551*ec63e07aSXin Li
AddTmpfs(absl::string_view inside,size_t sz)552*ec63e07aSXin Li absl::Status Mounts::AddTmpfs(absl::string_view inside, size_t sz) {
553*ec63e07aSXin Li MountTree::Node node;
554*ec63e07aSXin Li auto tmpfs_node = node.mutable_tmpfs_node();
555*ec63e07aSXin Li tmpfs_node->set_tmpfs_options(absl::StrCat("size=", sz));
556*ec63e07aSXin Li return Insert(inside, node);
557*ec63e07aSXin Li }
558*ec63e07aSXin Li
559*ec63e07aSXin Li namespace {
560*ec63e07aSXin Li
GetMountFlagsFor(const std::string & path)561*ec63e07aSXin Li uint64_t GetMountFlagsFor(const std::string& path) {
562*ec63e07aSXin Li struct statvfs vfs;
563*ec63e07aSXin Li if (TEMP_FAILURE_RETRY(statvfs(path.c_str(), &vfs)) == -1) {
564*ec63e07aSXin Li SAPI_RAW_PLOG(ERROR, "statvfs");
565*ec63e07aSXin Li return 0;
566*ec63e07aSXin Li }
567*ec63e07aSXin Li
568*ec63e07aSXin Li uint64_t flags = 0;
569*ec63e07aSXin Li using MountPair = std::pair<uint64_t, uint64_t>;
570*ec63e07aSXin Li for (const auto& [mount_flag, vfs_flag] : {
571*ec63e07aSXin Li MountPair(MS_RDONLY, ST_RDONLY),
572*ec63e07aSXin Li MountPair(MS_NOSUID, ST_NOSUID),
573*ec63e07aSXin Li MountPair(MS_NODEV, ST_NODEV),
574*ec63e07aSXin Li MountPair(MS_NOEXEC, ST_NOEXEC),
575*ec63e07aSXin Li MountPair(MS_SYNCHRONOUS, ST_SYNCHRONOUS),
576*ec63e07aSXin Li MountPair(MS_MANDLOCK, ST_MANDLOCK),
577*ec63e07aSXin Li MountPair(MS_NOATIME, ST_NOATIME),
578*ec63e07aSXin Li MountPair(MS_NODIRATIME, ST_NODIRATIME),
579*ec63e07aSXin Li MountPair(MS_RELATIME, ST_RELATIME),
580*ec63e07aSXin Li }) {
581*ec63e07aSXin Li if (vfs.f_flag & vfs_flag) {
582*ec63e07aSXin Li flags |= mount_flag;
583*ec63e07aSXin Li }
584*ec63e07aSXin Li }
585*ec63e07aSXin Li return flags;
586*ec63e07aSXin Li }
587*ec63e07aSXin Li
MountFlagsToString(uint64_t flags)588*ec63e07aSXin Li std::string MountFlagsToString(uint64_t flags) {
589*ec63e07aSXin Li #define SAPI_MAP(x) \
590*ec63e07aSXin Li { x, #x }
591*ec63e07aSXin Li static constexpr std::pair<uint64_t, absl::string_view> kMap[] = {
592*ec63e07aSXin Li SAPI_MAP(MS_RDONLY), SAPI_MAP(MS_NOSUID),
593*ec63e07aSXin Li SAPI_MAP(MS_NODEV), SAPI_MAP(MS_NOEXEC),
594*ec63e07aSXin Li SAPI_MAP(MS_SYNCHRONOUS), SAPI_MAP(MS_REMOUNT),
595*ec63e07aSXin Li SAPI_MAP(MS_MANDLOCK), SAPI_MAP(MS_DIRSYNC),
596*ec63e07aSXin Li SAPI_MAP(MS_NOATIME), SAPI_MAP(MS_NODIRATIME),
597*ec63e07aSXin Li SAPI_MAP(MS_BIND), SAPI_MAP(MS_MOVE),
598*ec63e07aSXin Li SAPI_MAP(MS_REC),
599*ec63e07aSXin Li #ifdef MS_VERBOSE
600*ec63e07aSXin Li SAPI_MAP(MS_VERBOSE), // Deprecated
601*ec63e07aSXin Li #endif
602*ec63e07aSXin Li SAPI_MAP(MS_SILENT), SAPI_MAP(MS_POSIXACL),
603*ec63e07aSXin Li SAPI_MAP(MS_UNBINDABLE), SAPI_MAP(MS_PRIVATE),
604*ec63e07aSXin Li SAPI_MAP(MS_SLAVE), // Inclusive language: system constant
605*ec63e07aSXin Li SAPI_MAP(MS_SHARED), SAPI_MAP(MS_RELATIME),
606*ec63e07aSXin Li SAPI_MAP(MS_KERNMOUNT), SAPI_MAP(MS_I_VERSION),
607*ec63e07aSXin Li SAPI_MAP(MS_STRICTATIME),
608*ec63e07aSXin Li #ifdef MS_LAZYTIME
609*ec63e07aSXin Li SAPI_MAP(MS_LAZYTIME), // Added in Linux 4.0
610*ec63e07aSXin Li #endif
611*ec63e07aSXin Li };
612*ec63e07aSXin Li #undef SAPI_MAP
613*ec63e07aSXin Li std::vector<absl::string_view> flags_list;
614*ec63e07aSXin Li for (const auto& [val, str] : kMap) {
615*ec63e07aSXin Li if ((flags & val) == val) {
616*ec63e07aSXin Li flags &= ~val;
617*ec63e07aSXin Li flags_list.push_back(str);
618*ec63e07aSXin Li }
619*ec63e07aSXin Li }
620*ec63e07aSXin Li std::string flags_str = absl::StrCat(flags);
621*ec63e07aSXin Li if (flags_list.empty() || flags != 0) {
622*ec63e07aSXin Li flags_list.push_back(flags_str);
623*ec63e07aSXin Li }
624*ec63e07aSXin Li return absl::StrJoin(flags_list, "|");
625*ec63e07aSXin Li }
626*ec63e07aSXin Li
MountWithDefaults(const std::string & source,const std::string & target,const char * fs_type,uint64_t extra_flags,const char * option_str,bool is_ro)627*ec63e07aSXin Li void MountWithDefaults(const std::string& source, const std::string& target,
628*ec63e07aSXin Li const char* fs_type, uint64_t extra_flags,
629*ec63e07aSXin Li const char* option_str, bool is_ro) {
630*ec63e07aSXin Li uint64_t flags = MS_REC | MS_NOSUID | extra_flags;
631*ec63e07aSXin Li if (is_ro) {
632*ec63e07aSXin Li flags |= MS_RDONLY;
633*ec63e07aSXin Li }
634*ec63e07aSXin Li SAPI_RAW_VLOG(1, R"(mount("%s", "%s", "%s", %s, "%s"))", source.c_str(),
635*ec63e07aSXin Li target.c_str(), fs_type, MountFlagsToString(flags).c_str(),
636*ec63e07aSXin Li option_str);
637*ec63e07aSXin Li
638*ec63e07aSXin Li int res = mount(source.c_str(), target.c_str(), fs_type, flags, option_str);
639*ec63e07aSXin Li if (res == -1) {
640*ec63e07aSXin Li if (errno == ENOENT) {
641*ec63e07aSXin Li // File does not exist (anymore). This is e.g. the case when we're trying
642*ec63e07aSXin Li // to gather stack-traces on SAPI crashes. The sandboxee application is a
643*ec63e07aSXin Li // memfd file that is not existing anymore.
644*ec63e07aSXin Li SAPI_RAW_LOG(WARNING, "Could not mount %s: file does not exist",
645*ec63e07aSXin Li source.c_str());
646*ec63e07aSXin Li return;
647*ec63e07aSXin Li }
648*ec63e07aSXin Li SAPI_RAW_PLOG(FATAL, "mounting %s to %s failed (flags=%s)", source, target,
649*ec63e07aSXin Li MountFlagsToString(flags));
650*ec63e07aSXin Li }
651*ec63e07aSXin Li
652*ec63e07aSXin Li // Flags are ignored for a bind mount, a remount is needed to set the flags.
653*ec63e07aSXin Li if (extra_flags & MS_BIND) {
654*ec63e07aSXin Li // Get actual mount flags.
655*ec63e07aSXin Li uint64_t target_flags = GetMountFlagsFor(target);
656*ec63e07aSXin Li if ((target_flags & MS_RDONLY) != 0 && (flags & MS_RDONLY) == 0) {
657*ec63e07aSXin Li SAPI_RAW_LOG(FATAL,
658*ec63e07aSXin Li "cannot remount %s as read-write as it's on read-only dev",
659*ec63e07aSXin Li target.c_str());
660*ec63e07aSXin Li }
661*ec63e07aSXin Li res = mount("", target.c_str(), "", flags | target_flags | MS_REMOUNT,
662*ec63e07aSXin Li nullptr);
663*ec63e07aSXin Li SAPI_RAW_PCHECK(res != -1, "remounting %s with flags=%s failed", target,
664*ec63e07aSXin Li MountFlagsToString(flags));
665*ec63e07aSXin Li }
666*ec63e07aSXin Li
667*ec63e07aSXin Li // Mount propagation has to be set separately
668*ec63e07aSXin Li const uint64_t propagation =
669*ec63e07aSXin Li extra_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE);
670*ec63e07aSXin Li if (propagation != 0) {
671*ec63e07aSXin Li res = mount("", target.c_str(), "", propagation, nullptr);
672*ec63e07aSXin Li SAPI_RAW_PCHECK(res != -1, "changing %s mount propagation to %s failed",
673*ec63e07aSXin Li target, MountFlagsToString(propagation).c_str());
674*ec63e07aSXin Li }
675*ec63e07aSXin Li }
676*ec63e07aSXin Li
677*ec63e07aSXin Li // Traverses the MountTree to create all required files and perform the mounts.
CreateMounts(const MountTree & tree,const std::string & path,bool create_backing_files)678*ec63e07aSXin Li void CreateMounts(const MountTree& tree, const std::string& path,
679*ec63e07aSXin Li bool create_backing_files) {
680*ec63e07aSXin Li // First, create the backing files if needed.
681*ec63e07aSXin Li if (create_backing_files) {
682*ec63e07aSXin Li switch (tree.node().node_case()) {
683*ec63e07aSXin Li case MountTree::Node::kFileNode: {
684*ec63e07aSXin Li SAPI_RAW_VLOG(2, "Creating backing file at %s", path.c_str());
685*ec63e07aSXin Li int fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0600);
686*ec63e07aSXin Li SAPI_RAW_PCHECK(fd != -1, "");
687*ec63e07aSXin Li SAPI_RAW_PCHECK(close(fd) == 0, "");
688*ec63e07aSXin Li break;
689*ec63e07aSXin Li }
690*ec63e07aSXin Li case MountTree::Node::kDirNode:
691*ec63e07aSXin Li case MountTree::Node::kTmpfsNode:
692*ec63e07aSXin Li case MountTree::Node::kRootNode:
693*ec63e07aSXin Li case MountTree::Node::NODE_NOT_SET:
694*ec63e07aSXin Li SAPI_RAW_VLOG(2, "Creating directory at %s", path.c_str());
695*ec63e07aSXin Li SAPI_RAW_PCHECK(mkdir(path.c_str(), 0700) == 0 || errno == EEXIST, "");
696*ec63e07aSXin Li break;
697*ec63e07aSXin Li // Intentionally no default to make sure we handle all the cases.
698*ec63e07aSXin Li }
699*ec63e07aSXin Li }
700*ec63e07aSXin Li
701*ec63e07aSXin Li // Perform the actual mounts based on the node type.
702*ec63e07aSXin Li switch (tree.node().node_case()) {
703*ec63e07aSXin Li case MountTree::Node::kDirNode: {
704*ec63e07aSXin Li // Since this directory is bind mounted, it's the users
705*ec63e07aSXin Li // responsibility to make sure that all backing files are in place.
706*ec63e07aSXin Li create_backing_files = false;
707*ec63e07aSXin Li
708*ec63e07aSXin Li auto node = tree.node().dir_node();
709*ec63e07aSXin Li MountWithDefaults(node.outside(), path, "", MS_BIND, nullptr,
710*ec63e07aSXin Li !node.writable());
711*ec63e07aSXin Li break;
712*ec63e07aSXin Li }
713*ec63e07aSXin Li case MountTree::Node::kTmpfsNode: {
714*ec63e07aSXin Li // We can always create backing files under a tmpfs.
715*ec63e07aSXin Li create_backing_files = true;
716*ec63e07aSXin Li
717*ec63e07aSXin Li auto node = tree.node().tmpfs_node();
718*ec63e07aSXin Li MountWithDefaults("", path, "tmpfs", 0, node.tmpfs_options().c_str(),
719*ec63e07aSXin Li /* is_ro */ false);
720*ec63e07aSXin Li break;
721*ec63e07aSXin Li }
722*ec63e07aSXin Li case MountTree::Node::kFileNode: {
723*ec63e07aSXin Li auto node = tree.node().file_node();
724*ec63e07aSXin Li MountWithDefaults(node.outside(), path, "", MS_BIND, nullptr,
725*ec63e07aSXin Li !node.writable());
726*ec63e07aSXin Li
727*ec63e07aSXin Li // A file node has to be a leaf so we can skip traversing here.
728*ec63e07aSXin Li return;
729*ec63e07aSXin Li }
730*ec63e07aSXin Li case MountTree::Node::kRootNode:
731*ec63e07aSXin Li case MountTree::Node::NODE_NOT_SET:
732*ec63e07aSXin Li // Nothing to do, we already created the directory above.
733*ec63e07aSXin Li break;
734*ec63e07aSXin Li // Intentionally no default to make sure we handle all the cases.
735*ec63e07aSXin Li }
736*ec63e07aSXin Li
737*ec63e07aSXin Li // Traverse the subtrees.
738*ec63e07aSXin Li for (const auto& kv : tree.entries()) {
739*ec63e07aSXin Li std::string new_path = sapi::file::JoinPath(path, kv.first);
740*ec63e07aSXin Li CreateMounts(kv.second, new_path, create_backing_files);
741*ec63e07aSXin Li }
742*ec63e07aSXin Li }
743*ec63e07aSXin Li
744*ec63e07aSXin Li } // namespace
745*ec63e07aSXin Li
CreateMounts(const std::string & root_path) const746*ec63e07aSXin Li void Mounts::CreateMounts(const std::string& root_path) const {
747*ec63e07aSXin Li sandbox2::CreateMounts(mount_tree_, root_path, true);
748*ec63e07aSXin Li }
749*ec63e07aSXin Li
750*ec63e07aSXin Li namespace {
751*ec63e07aSXin Li
RecursivelyListMountsImpl(const MountTree & tree,const std::string & tree_path,std::vector<std::string> * outside_entries,std::vector<std::string> * inside_entries)752*ec63e07aSXin Li void RecursivelyListMountsImpl(const MountTree& tree,
753*ec63e07aSXin Li const std::string& tree_path,
754*ec63e07aSXin Li std::vector<std::string>* outside_entries,
755*ec63e07aSXin Li std::vector<std::string>* inside_entries) {
756*ec63e07aSXin Li const MountTree::Node& node = tree.node();
757*ec63e07aSXin Li if (node.has_dir_node()) {
758*ec63e07aSXin Li const char* rw_str = node.dir_node().writable() ? "W " : "R ";
759*ec63e07aSXin Li inside_entries->emplace_back(absl::StrCat(rw_str, tree_path, "/"));
760*ec63e07aSXin Li outside_entries->emplace_back(absl::StrCat(node.dir_node().outside(), "/"));
761*ec63e07aSXin Li } else if (node.has_file_node()) {
762*ec63e07aSXin Li const char* rw_str = node.file_node().writable() ? "W " : "R ";
763*ec63e07aSXin Li inside_entries->emplace_back(absl::StrCat(rw_str, tree_path));
764*ec63e07aSXin Li outside_entries->emplace_back(absl::StrCat(node.file_node().outside()));
765*ec63e07aSXin Li } else if (node.has_tmpfs_node()) {
766*ec63e07aSXin Li inside_entries->emplace_back(tree_path);
767*ec63e07aSXin Li outside_entries->emplace_back(
768*ec63e07aSXin Li absl::StrCat("tmpfs: ", node.tmpfs_node().tmpfs_options()));
769*ec63e07aSXin Li }
770*ec63e07aSXin Li
771*ec63e07aSXin Li for (const auto& subentry : tree.entries()) {
772*ec63e07aSXin Li RecursivelyListMountsImpl(subentry.second,
773*ec63e07aSXin Li absl::StrCat(tree_path, "/", subentry.first),
774*ec63e07aSXin Li outside_entries, inside_entries);
775*ec63e07aSXin Li }
776*ec63e07aSXin Li }
777*ec63e07aSXin Li
778*ec63e07aSXin Li } // namespace
779*ec63e07aSXin Li
RecursivelyListMounts(std::vector<std::string> * outside_entries,std::vector<std::string> * inside_entries) const780*ec63e07aSXin Li void Mounts::RecursivelyListMounts(
781*ec63e07aSXin Li std::vector<std::string>* outside_entries,
782*ec63e07aSXin Li std::vector<std::string>* inside_entries) const {
783*ec63e07aSXin Li RecursivelyListMountsImpl(GetMountTree(), "", outside_entries,
784*ec63e07aSXin Li inside_entries);
785*ec63e07aSXin Li }
786*ec63e07aSXin Li
787*ec63e07aSXin Li } // namespace sandbox2
788