1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Contains the implementation of class MountNamespace for libbrillo.
6
7 #include "brillo/namespaces/mount_namespace.h"
8
9 #include <sched.h>
10 #include <sys/mount.h>
11 #include <sys/types.h>
12
13 #include <string>
14
15 #include <base/files/file_path.h>
16 #include <base/files/file_util.h>
17 #include <base/logging.h>
18 #include <base/strings/stringprintf.h>
19 #include <brillo/namespaces/platform.h>
20
21 namespace brillo {
MountNamespace(const base::FilePath & ns_path,Platform * platform)22 MountNamespace::MountNamespace(const base::FilePath& ns_path,
23 Platform* platform)
24 : ns_path_(ns_path), platform_(platform), exists_(false) {}
25
~MountNamespace()26 MountNamespace::~MountNamespace() {
27 if (exists_)
28 Destroy();
29 }
30
Create()31 bool MountNamespace::Create() {
32 if (platform_->FileSystemIsNsfs(ns_path_)) {
33 LOG(ERROR) << "Mount namespace at " << ns_path_.value()
34 << " already exists.";
35 return false;
36 }
37 int fd_mounted[2];
38 int fd_unshared[2];
39 char byte = '\0';
40 if (pipe(fd_mounted) != 0) {
41 PLOG(ERROR) << "Cannot create mount signalling pipe";
42 return false;
43 }
44 if (pipe(fd_unshared) != 0) {
45 PLOG(ERROR) << "Cannot create unshare signalling pipe";
46 return false;
47 }
48 pid_t pid = platform_->Fork();
49 if (pid < 0) {
50 PLOG(ERROR) << "Fork failed";
51 } else if (pid == 0) {
52 // Child.
53 close(fd_mounted[1]);
54 close(fd_unshared[0]);
55 if (unshare(CLONE_NEWNS) != 0) {
56 PLOG(ERROR) << "unshare(CLONE_NEWNS) failed";
57 exit(1);
58 }
59 base::WriteFileDescriptor(fd_unshared[1], &byte, 1);
60 base::ReadFromFD(fd_mounted[0], &byte, 1);
61 exit(0);
62 } else {
63 // Parent.
64 close(fd_mounted[0]);
65 close(fd_unshared[1]);
66 std::string proc_ns_path = base::StringPrintf("/proc/%d/ns/mnt", pid);
67 bool mount_success = true;
68 base::ReadFromFD(fd_unshared[0], &byte, 1);
69 if (platform_->Mount(proc_ns_path, ns_path_.value(), "", MS_BIND) != 0) {
70 PLOG(ERROR) << "Mount(" << proc_ns_path << ", " << ns_path_.value()
71 << ", MS_BIND) failed";
72 mount_success = false;
73 }
74 base::WriteFileDescriptor(fd_mounted[1], &byte, 1);
75
76 int status;
77 if (platform_->Waitpid(pid, &status) < 0) {
78 PLOG(ERROR) << "waitpid(" << pid << ") failed";
79 return false;
80 }
81 if (!WIFEXITED(status)) {
82 LOG(ERROR) << "Child process did not exit normally.";
83 } else if (WEXITSTATUS(status) != 0) {
84 LOG(ERROR) << "Child process failed.";
85 } else {
86 exists_ = mount_success;
87 }
88 }
89 return exists_;
90 }
91
Destroy()92 bool MountNamespace::Destroy() {
93 if (!exists_) {
94 LOG(ERROR) << "Mount namespace at " << ns_path_.value()
95 << "does not exist, cannot destroy";
96 return false;
97 }
98 bool was_busy;
99 if (!platform_->Unmount(ns_path_, false /*lazy*/, &was_busy)) {
100 PLOG(ERROR) << "Failed to unmount " << ns_path_.value();
101 if (was_busy) {
102 LOG(ERROR) << ns_path_.value().c_str() << " was busy";
103 }
104 // If Unmount() fails, keep the object valid by keeping |exists_|
105 // set to true.
106 return false;
107 } else {
108 VLOG(1) << "Unmounted namespace at " << ns_path_.value();
109 }
110 exists_ = false;
111 return true;
112 }
113
114 } // namespace brillo
115