blob: 83662fc2c2b9ea902b444bab9c2911df01ee11d5 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rootfs.hpp"
#include <vector>
#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/os.hpp>
#include <stout/strings.hpp>
#include <stout/os/realpath.hpp>
#include <stout/os/stat.hpp>
#include "linux/ldd.hpp"
using std::string;
using std::vector;
using process::Owned;
namespace mesos {
namespace internal {
namespace tests {
Rootfs::~Rootfs()
{
if (os::exists(root)) {
os::rmdir(root);
}
}
Try<Nothing> Rootfs::add(const string& path)
{
Option<string> source = None();
// If we are copying a symlink, follow it and copy the
// target instead. While this is a little inefficient on
// disk space, it avoids complexity in dealing with chains
// of symlinks and symlinks in intermediate path components.
if (os::stat::islink(path)) {
Result<string> target = os::realpath(path);
if (target.isNone()) {
return Error("Failed to resolve '" + path + "'");
}
if (target.isError()) {
return Error("Failed to resolve '" + path + "': " + target.error());
}
source = target.get();
}
Try<Nothing> copy = copyPath(source.isSome() ? source.get() : path, path);
if (copy.isError()) {
return Error("Failed to copy '" + path + "' to rootfs: " + copy.error());
}
return Nothing();
}
Try<Nothing> Rootfs::copyPath(const string& source, const string& destination)
{
if (!os::exists(source)) {
return Error("'" + source + "' not found");
}
if (!strings::startsWith(source, "/")) {
return Error("'" + source + "' is not an absolute path");
}
string rootfsDestination = path::join(root, destination);
string rootfsDirectory = Path(rootfsDestination).dirname();
if (!os::exists(rootfsDirectory)) {
Try<Nothing> mkdir = os::mkdir(rootfsDirectory);
if (mkdir.isError()) {
return Error(
"Failed to create directory '" + rootfsDirectory +
"': " + mkdir.error());
}
}
// Copy the files. We preserve all attributes so that e.g., `ping`
// keeps its file-based capabilities.
if (os::spawn(
"cp",
{"cp", "-r", "--preserve=all", source, rootfsDestination}) != 0) {
return Error("Failed to copy '" + source + "' to rootfs");
}
return Nothing();
}
Try<process::Owned<Rootfs>> LinuxRootfs::create(const string& root)
{
process::Owned<Rootfs> rootfs(new LinuxRootfs(root));
if (!os::exists(root)) {
Try<Nothing> mkdir = os::mkdir(root);
if (mkdir.isError()) {
return Error("Failed to create root directory: " + mkdir.error());
}
}
Try<vector<ldcache::Entry>> cache = ldcache::parse();
if (cache.isError()) {
return Error("Failed to parse ld.so cache: " + cache.error());
}
const vector<string> programs = {
"/bin/cat",
"/bin/echo",
"/bin/ls",
"/bin/ping",
"/bin/sh",
"/bin/sleep",
};
hashset<string> files = {
"/etc/passwd"
};
foreach (const string& program, programs) {
Try<hashset<string>> dependencies = ldd(program, cache.get());
if (dependencies.isError()) {
return Error(
"Failed to find dependencies for '" + program + "': " +
dependencies.error());
}
files |= dependencies.get();
files.insert(program);
}
foreach (const string& file, files) {
Try<Nothing> result = rootfs->add(file);
if (result.isError()) {
return Error(result.error());
}
}
const vector<string> directories = {
"/proc",
"/sys",
"/dev",
"/tmp"
};
foreach (const string& directory, directories) {
Try<Nothing> mkdir = os::mkdir(path::join(root, directory));
if (mkdir.isError()) {
return Error(
"Failed to create '" + directory + "' in rootfs: " +
mkdir.error());
}
}
return rootfs;
}
} // namespace tests {
} // namespace internal {
} // namespace mesos {