xref: /aosp_15_r20/external/libbrillo/brillo/namespaces/mount_namespace.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
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