// 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