115 lines
3.2 KiB
115 lines
3.2 KiB
// 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
|