blob: 1944983ec65df11aab7b0ed9faa1f5d023428d0f [file] [log] [blame]
// 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