提交 b090f0a7 编写于 作者: S Simon Fels 提交者: GitHub

Merge pull request #42 from morphis/feature/mount-setup

Setup Android rootfs mount on container manager startup
......@@ -34,17 +34,6 @@ load_kernel_modules() {
}
start() {
# Setup the read-only rootfs
mkdir -p $ROOTFS_PATH
mount -o loop,ro $ANDROID_IMG $ROOTFS_PATH
# but certain top-level directories need to be in a writable space
for dir in cache data; do
mkdir -p $DATA_PATH/android-$dir
chown $CONTAINER_BASE_UID:$CONTAINER_BASE_UID $DATA_PATH/android-$dir
mount -o bind $DATA_PATH/android-$dir $ROOTFS_PATH/$dir
done
# Make sure our setup path for the container rootfs
# is present as lxc is statically configured for
# this path.
......@@ -62,7 +51,10 @@ start() {
# Ensure FUSE support for user namespaces is enabled
echo Y | sudo tee /sys/module/fuse/parameters/userns_mounts || echo "WARNING: kernel doesn't support fuse in user namespaces"
exec $SNAP/usr/sbin/aa-exec -p unconfined -- $SNAP/bin/anbox-wrapper.sh container-manager --data-path=$DATA_PATH
exec $SNAP/usr/sbin/aa-exec -p unconfined -- \
$SNAP/bin/anbox-wrapper.sh container-manager \
--data-path=$DATA_PATH \
--android-image=$ANDROID_IMG
}
stop() {
......
......@@ -71,6 +71,9 @@ set(SOURCES
anbox/common/type_traits.h
anbox/common/message_channel.cpp
anbox/common/scope_ptr.h
anbox/common/loop_device.cpp
anbox/common/loop_device_allocator.cpp
anbox/common/mount_entry.cpp
anbox/testing/gtest_utils.h
......
......@@ -17,39 +17,130 @@
#include "anbox/cmds/container_manager.h"
#include "anbox/container/service.h"
#include "anbox/common/loop_device_allocator.h"
#include "anbox/logger.h"
#include "anbox/runtime.h"
#include "anbox/config.h"
#include "core/posix/signal.h"
#include <sys/mount.h>
#include <linux/loop.h>
#include <fcntl.h>
namespace fs = boost::filesystem;
namespace {
constexpr unsigned int unprivileged_user_id{100000};
}
anbox::cmds::ContainerManager::ContainerManager()
: CommandWithFlagsAndAction{
cli::Name{"container-manager"}, cli::Usage{"container-manager"},
cli::Description{"Start the container manager service"}} {
flag(cli::make_flag(cli::Name{"android-image"},
cli::Description{"Path to the Android rootfs image file if not stored in the data path"},
android_img_path_));
flag(cli::make_flag(cli::Name{"data-path"},
cli::Description{"Path where the container and its data is stored"},
data_path_));
action([&](const cli::Command::Context&) {
auto trap = core::posix::trap_signals_for_process(
{core::posix::Signal::sig_term, core::posix::Signal::sig_int});
trap->signal_raised().connect([trap](const core::posix::Signal& signal) {
INFO("Signal %i received. Good night.", static_cast<int>(signal));
trap->stop();
});
try {
auto trap = core::posix::trap_signals_for_process(
{core::posix::Signal::sig_term, core::posix::Signal::sig_int});
trap->signal_raised().connect([trap](const core::posix::Signal& signal) {
INFO("Signal %i received. Good night.", static_cast<int>(signal));
trap->stop();
});
if (!data_path_.empty())
SystemConfiguration::instance().set_data_path(data_path_);
if (!data_path_.empty())
SystemConfiguration::instance().set_data_path(data_path_);
if (!setup_mounts())
return EXIT_FAILURE;
auto rt = Runtime::create();
auto service = container::Service::create(rt);
auto rt = Runtime::create();
auto service = container::Service::create(rt);
rt->start();
trap->run();
rt->stop();
rt->start();
trap->run();
rt->stop();
return 0;
return EXIT_SUCCESS;
} catch (std::exception &err) {
ERROR("%s", err.what());
return EXIT_FAILURE;
}
});
}
anbox::cmds::ContainerManager::~ContainerManager() {}
bool anbox::cmds::ContainerManager::setup_mounts() {
fs::path android_img_path = android_img_path_;
if (android_img_path.empty())
android_img_path = SystemConfiguration::instance().data_dir() / "android.img";
if (!fs::exists(android_img_path)) {
ERROR("Android image does not exist at path %s", android_img_path);
return false;
}
const auto android_rootfs_dir = SystemConfiguration::instance().rootfs_dir();
if (utils::is_mounted(android_rootfs_dir)) {
ERROR("Androd rootfs is already mounted!?");
return false;
}
if (!fs::exists(android_rootfs_dir))
fs::create_directory(android_rootfs_dir);
auto loop_device = common::LoopDeviceAllocator::new_device();
if (!loop_device)
return false;
if (!loop_device->attach_file(android_img_path)) {
ERROR("Failed to attach Android rootfs image to loopback device");
return false;
}
auto m = common::MountEntry::create(loop_device, android_rootfs_dir, "squashfs", MS_MGC_VAL | MS_RDONLY);
if (!m) {
ERROR("Failed to mount Android rootfs");
return false;
}
mounts_.push_back(m);
for (const auto &dir_name : std::vector<std::string>{"cache", "data"}) {
auto target_dir_path = fs::path(android_rootfs_dir) / dir_name;
auto src_dir_path = SystemConfiguration::instance().data_dir() / dir_name;
if (!fs::exists(src_dir_path)) {
if (!fs::create_directory(src_dir_path)) {
ERROR("Failed to create Android %s directory", dir_name);
mounts_.clear();
return false;
}
if (::chown(src_dir_path.c_str(), unprivileged_user_id, unprivileged_user_id) != 0) {
ERROR("Failed to allow access for unprivileged user on %s directory of the rootfs", dir_name);
mounts_.clear();
return false;
}
}
auto m = common::MountEntry::create(src_dir_path, target_dir_path, "", MS_MGC_VAL | MS_BIND);
if (!m) {
ERROR("Failed to mount Android %s directory", dir_name);
mounts_.clear();
return false;
}
mounts_.push_back(m);
}
// Unmounting needs to happen in reverse order
std::reverse(mounts_.begin(), mounts_.end());
return true;
}
......@@ -24,14 +24,23 @@
#include "anbox/cli.h"
#include "anbox/common/loop_device.h"
#include "anbox/common/mount_entry.h"
namespace anbox {
namespace cmds {
class ContainerManager : public cli::CommandWithFlagsAndAction {
public:
ContainerManager();
~ContainerManager();
private:
bool setup_mounts();
std::string android_img_path_;
std::string data_path_;
std::shared_ptr<common::LoopDevice> android_img_loop_dev_;
std::vector<std::shared_ptr<common::MountEntry>> mounts_;
};
} // namespace cmds
} // namespace anbox
......
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/common/loop_device.h"
#include "anbox/defer_action.h"
#include <linux/loop.h>
#include <fcntl.h>
#include <sys/ioctl.h>
namespace anbox {
namespace common {
std::shared_ptr<LoopDevice> LoopDevice::create(const boost::filesystem::path &path) {
const auto fd = ::open(path.c_str(), O_RDWR);
if (fd < 0)
return nullptr;
return std::shared_ptr<LoopDevice>(new LoopDevice(Fd{fd}, path));
}
LoopDevice::LoopDevice(Fd fd, const boost::filesystem::path &path) :
fd_{fd}, path_{path} {}
LoopDevice::~LoopDevice() {
if (fd_ < 0)
return;
::ioctl(fd_, LOOP_CLR_FD);
::close(fd_);
}
bool LoopDevice::attach_file(const boost::filesystem::path &file_path) {
if (fd_ < 0)
return false;
int file_fd = ::open(file_path.c_str(), O_RDONLY);
if (file_fd < 0)
return false;
DeferAction close_file_fd{[&]() { ::close(file_fd); }};
if (::ioctl(fd_, LOOP_SET_FD, file_fd) < 0)
return false;
return true;
}
} // namespace common
} // namespace anbox
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_COMMON_LOOP_DEVICE_H_
#define ANBOX_COMMON_LOOP_DEVICE_H_
#include "anbox/common/fd.h"
#include <boost/filesystem/path.hpp>
namespace anbox {
namespace common {
class LoopDevice {
public:
static std::shared_ptr<LoopDevice> create(const boost::filesystem::path &path);
~LoopDevice();
bool attach_file(const boost::filesystem::path &file_path);
boost::filesystem::path path() const { return path_; }
private:
LoopDevice(Fd fd, const boost::filesystem::path &path);
Fd fd_;
boost::filesystem::path path_;
};
} // namespace common
} // namespace anbox
#endif
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/common/loop_device_allocator.h"
#include "anbox/common/loop_device.h"
#include "anbox/defer_action.h"
#include "anbox/utils.h"
#include <boost/filesystem.hpp>
#include <linux/loop.h>
#include <fcntl.h>
#include <sys/ioctl.h>
namespace fs = boost::filesystem;
namespace {
const constexpr char *loop_control_path{"/dev/loop-control"};
const constexpr char *base_loop_path{"/dev/loop"};
}
namespace anbox {
namespace common {
std::shared_ptr<LoopDevice> LoopDeviceAllocator::new_device() {
const auto ctl_fd = ::open(loop_control_path, O_RDWR);
if (ctl_fd < 0)
return nullptr;
DeferAction close_ctl_fd{[&]() { ::close(ctl_fd); }};
const auto device_nr = ::ioctl(ctl_fd, LOOP_CTL_GET_FREE);
if (device_nr < 0)
return nullptr;
const auto path = utils::string_format("%s%d", base_loop_path, device_nr);
if (!fs::exists(path))
return nullptr;
return LoopDevice::create(path);
}
} // namespace common
} // namespace anbox
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_COMMON_LOOP_DEVICE_ALLOCATOR_H_
#define ANBOX_COMMON_LOOP_DEVICE_ALLOCATOR_H_
#include <memory>
namespace anbox {
namespace common {
class LoopDevice;
class LoopDeviceAllocator {
public:
static std::shared_ptr<LoopDevice> new_device();
};
} // namespace common
} // namespace anbox
#endif
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/common/mount_entry.h"
#include "anbox/common/loop_device.h"
#include <sys/mount.h>
namespace anbox {
namespace common {
std::shared_ptr<MountEntry> MountEntry::create(const boost::filesystem::path &src, const boost::filesystem::path &target,
const std::string &fs_type, unsigned long flags) {
auto entry = std::shared_ptr<MountEntry>(new MountEntry(target));
if (!entry)
return nullptr;
if (::mount(src.c_str(), target.c_str(), !fs_type.empty() ? fs_type.c_str() : nullptr, flags, nullptr) != 0)
return nullptr;
entry->active_ = true;
return entry;
}
std::shared_ptr<MountEntry> MountEntry::create(const std::shared_ptr<LoopDevice> &loop, const boost::filesystem::path &target,
const std::string &fs_type, unsigned long flags) {
auto entry = create(loop->path(), target, fs_type, flags);
if (!entry)
return nullptr;
entry->loop_ = loop;
return entry;
}
MountEntry::MountEntry(const boost::filesystem::path &target) :
active_{false}, target_{target} {}
MountEntry::~MountEntry() {
if (!active_)
return;
::umount(target_.c_str());
}
} // namespace common
} // namespace anbox
/*
* Copyright (C) 2017 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_COMMON_MOUNT_ENTRY_H_
#define ANBOX_COMMON_MOUNT_ENTRY_H_
#include <boost/filesystem/path.hpp>
namespace anbox {
namespace common {
class LoopDevice;
class MountEntry {
public:
static std::shared_ptr<MountEntry> create(const boost::filesystem::path &src, const boost::filesystem::path &target,
const std::string &fs_type = "", unsigned long flags = 0);
static std::shared_ptr<MountEntry> create(const std::shared_ptr<LoopDevice> &loop, const boost::filesystem::path &target,
const std::string &fs_type = "", unsigned long flags = 0);
~MountEntry();
private:
MountEntry(const boost::filesystem::path &target);
bool active_;
std::shared_ptr<LoopDevice> loop_;
boost::filesystem::path target_;
};
} // namespace common
} // namespace anbox
#endif
......@@ -40,6 +40,10 @@ void anbox::SystemConfiguration::set_data_path(const std::string &path) {
data_path = path;
}
fs::path anbox::SystemConfiguration::data_dir() const {
return data_path;
}
std::string anbox::SystemConfiguration::rootfs_dir() const {
return (data_path / "rootfs").string();
}
......
......@@ -32,6 +32,7 @@ class SystemConfiguration {
void set_data_path(const std::string &path);
boost::filesystem::path data_dir() const;
std::string rootfs_dir() const;
std::string log_dir() const;
std::string socket_dir() const;
......
......@@ -56,7 +56,7 @@ Service::Service(const std::shared_ptr<Runtime> &rt)
std::make_shared<network::Connections<network::SocketConnection>>()) {
}
Service::~Service() { DEBUG(""); }
Service::~Service() {}
int Service::next_id() { return next_connection_id_++; }
......
......@@ -36,9 +36,9 @@ Daemon::Daemon()
: cmd{cli::Name{"anbox"}, cli::Usage{"anbox"},
cli::Description{"The Android in a Box runtime"}} {
cmd.command(std::make_shared<cmds::Version>())
.command(std::make_shared<cmds::SessionManager>())
.command(std::make_shared<cmds::Launch>())
.command(std::make_shared<cmds::ContainerManager>());
.command(std::make_shared<cmds::SessionManager>())
.command(std::make_shared<cmds::Launch>())
.command(std::make_shared<cmds::ContainerManager>());
}
int Daemon::Run(const std::vector<std::string> &arguments) try {
......@@ -47,6 +47,7 @@ int Daemon::Run(const std::vector<std::string> &arguments) try {
return cmd.run({std::cin, std::cout, argv});
} catch (std::exception &err) {
ERROR("%s", err.what());
return EXIT_FAILURE;
}
} // namespace anbox
......@@ -24,6 +24,7 @@
#include <stdexcept>
#include <fcntl.h>
#include <mntent.h>
#include "anbox/utils.h"
......@@ -163,5 +164,20 @@ std::string process_get_exe_path(const pid_t &pid) {
return boost::filesystem::read_symlink(exe_path).string();
}
bool is_mounted(const std::string &path) {
FILE *mtab = nullptr;
struct mntent *part = nullptr;
bool is_mounted = false;
if ((mtab = setmntent("/etc/mtab", "r")) != nullptr) {
while ((part = getmntent(mtab)) != nullptr) {
if ((part->mnt_fsname != nullptr) && (strcmp(part->mnt_fsname, path.c_str())) == 0)
is_mounted = true;
}
endmntent(mtab);
}
return is_mounted;
}
} // namespace utils
} // namespace anbox
......@@ -50,6 +50,8 @@ std::string prefix_dir_from_env(const std::string &path,
std::string process_get_exe_path(const pid_t &pid);
bool is_mounted(const std::string &path);
template <typename... Types>
static std::string string_format(const std::string &fmt_str, Types &&... args);
} // namespace utils
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册