diff options
Diffstat (limited to 'brillo/namespaces/mount_namespace.cc')
-rw-r--r-- | brillo/namespaces/mount_namespace.cc | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/brillo/namespaces/mount_namespace.cc b/brillo/namespaces/mount_namespace.cc new file mode 100644 index 0000000..1944983 --- /dev/null +++ b/brillo/namespaces/mount_namespace.cc @@ -0,0 +1,114 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Contains the implementation of class MountNamespace for libbrillo. + +#include "brillo/namespaces/mount_namespace.h" + +#include <sched.h> +#include <sys/mount.h> +#include <sys/types.h> + +#include <string> + +#include <base/files/file_path.h> +#include <base/files/file_util.h> +#include <base/logging.h> +#include <base/strings/stringprintf.h> +#include <brillo/namespaces/platform.h> + +namespace brillo { +MountNamespace::MountNamespace(const base::FilePath& ns_path, + Platform* platform) + : ns_path_(ns_path), platform_(platform), exists_(false) {} + +MountNamespace::~MountNamespace() { + if (exists_) + Destroy(); +} + +bool MountNamespace::Create() { + if (platform_->FileSystemIsNsfs(ns_path_)) { + LOG(ERROR) << "Mount namespace at " << ns_path_.value() + << " already exists."; + return false; + } + int fd_mounted[2]; + int fd_unshared[2]; + char byte = '\0'; + if (pipe(fd_mounted) != 0) { + PLOG(ERROR) << "Cannot create mount signalling pipe"; + return false; + } + if (pipe(fd_unshared) != 0) { + PLOG(ERROR) << "Cannot create unshare signalling pipe"; + return false; + } + pid_t pid = platform_->Fork(); + if (pid < 0) { + PLOG(ERROR) << "Fork failed"; + } else if (pid == 0) { + // Child. + close(fd_mounted[1]); + close(fd_unshared[0]); + if (unshare(CLONE_NEWNS) != 0) { + PLOG(ERROR) << "unshare(CLONE_NEWNS) failed"; + exit(1); + } + base::WriteFileDescriptor(fd_unshared[1], &byte, 1); + base::ReadFromFD(fd_mounted[0], &byte, 1); + exit(0); + } else { + // Parent. + close(fd_mounted[0]); + close(fd_unshared[1]); + std::string proc_ns_path = base::StringPrintf("/proc/%d/ns/mnt", pid); + bool mount_success = true; + base::ReadFromFD(fd_unshared[0], &byte, 1); + if (platform_->Mount(proc_ns_path, ns_path_.value(), "", MS_BIND) != 0) { + PLOG(ERROR) << "Mount(" << proc_ns_path << ", " << ns_path_.value() + << ", MS_BIND) failed"; + mount_success = false; + } + base::WriteFileDescriptor(fd_mounted[1], &byte, 1); + + int status; + if (platform_->Waitpid(pid, &status) < 0) { + PLOG(ERROR) << "waitpid(" << pid << ") failed"; + return false; + } + if (!WIFEXITED(status)) { + LOG(ERROR) << "Child process did not exit normally."; + } else if (WEXITSTATUS(status) != 0) { + LOG(ERROR) << "Child process failed."; + } else { + exists_ = mount_success; + } + } + return exists_; +} + +bool MountNamespace::Destroy() { + if (!exists_) { + LOG(ERROR) << "Mount namespace at " << ns_path_.value() + << "does not exist, cannot destroy"; + return false; + } + bool was_busy; + if (!platform_->Unmount(ns_path_, false /*lazy*/, &was_busy)) { + PLOG(ERROR) << "Failed to unmount " << ns_path_.value(); + if (was_busy) { + LOG(ERROR) << ns_path_.value().c_str() << " was busy"; + } + // If Unmount() fails, keep the object valid by keeping |exists_| + // set to true. + return false; + } else { + VLOG(1) << "Unmounted namespace at " << ns_path_.value(); + } + exists_ = false; + return true; +} + +} // namespace brillo |