From ac2181d92bfcfd487e7c6b28dea657ad8771dcdb Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Wed, 12 Jul 2017 09:26:21 +0200 Subject: [PATCH 1/4] Add simple helper function to find program in PATH --- src/anbox/utils.cpp | 15 ++++++++++++++- src/anbox/utils.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/anbox/utils.cpp b/src/anbox/utils.cpp index a4f0c8f..df3a815 100644 --- a/src/anbox/utils.cpp +++ b/src/anbox/utils.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include "anbox/utils.h" @@ -34,7 +36,6 @@ namespace fs = boost::filesystem; namespace anbox { namespace utils { - std::vector collect_arguments(int argc, char **argv) { std::vector result; for (int i = 1; i < argc; i++) result.push_back(argv[i]); @@ -193,5 +194,17 @@ bool is_mounted(const std::string &path) { } +std::string find_program_on_path(const std::string &name) { + struct stat sb; + std::string path = std::string(getenv("PATH")); + size_t start_pos = 0, end_pos = 0; + while ((end_pos = path.find(':', start_pos)) != std::string::npos) { + const auto current_path = path.substr(start_pos, end_pos - start_pos) + "/" + name; + if ((::stat(current_path.c_str(), &sb) == 0) && (sb.st_mode & S_IXOTH)) + return current_path; + start_pos = end_pos + 1; + } + return ""; +} } // namespace utils } // namespace anbox diff --git a/src/anbox/utils.h b/src/anbox/utils.h index 28b95a1..4b45a08 100644 --- a/src/anbox/utils.h +++ b/src/anbox/utils.h @@ -56,6 +56,8 @@ std::string process_get_exe_path(const pid_t &pid); bool is_mounted(const std::string &path); +std::string find_program_on_path(const std::string &name); + template static std::string string_format(const std::string &fmt_str, Types &&... args); } // namespace utils From 7cfef55e2d91e82dcbdb73a81588b3ee9b50e629 Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Wed, 12 Jul 2017 09:26:47 +0200 Subject: [PATCH 2/4] Allow a mount entry to be construct with just the mounted path --- src/anbox/common/mount_entry.cpp | 10 ++++++++++ src/anbox/common/mount_entry.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/anbox/common/mount_entry.cpp b/src/anbox/common/mount_entry.cpp index fce7831..3e9d953 100644 --- a/src/anbox/common/mount_entry.cpp +++ b/src/anbox/common/mount_entry.cpp @@ -46,6 +46,16 @@ std::shared_ptr MountEntry::create(const std::shared_ptr return entry; } +std::shared_ptr MountEntry::create(const boost::filesystem::path &target) { + auto entry = std::shared_ptr(new MountEntry(target)); + if (!entry) + return nullptr; + + entry->active_ = true; + + return entry; +} + MountEntry::MountEntry(const boost::filesystem::path &target) : active_{false}, target_{target} {} diff --git a/src/anbox/common/mount_entry.h b/src/anbox/common/mount_entry.h index 5b0be38..cc703e3 100644 --- a/src/anbox/common/mount_entry.h +++ b/src/anbox/common/mount_entry.h @@ -31,6 +31,8 @@ class MountEntry { static std::shared_ptr create(const std::shared_ptr &loop, const boost::filesystem::path &target, const std::string &fs_type = "", unsigned long flags = 0); + static std::shared_ptr create(const boost::filesystem::path &target); + ~MountEntry(); private: From aecf88fb2d7186c4085a3a4cab1a1e6d30d7444b Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Wed, 12 Jul 2017 09:27:08 +0200 Subject: [PATCH 3/4] Pass optional additonal user data to mount syscall --- src/anbox/common/mount_entry.cpp | 13 +++++++++---- src/anbox/common/mount_entry.h | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/anbox/common/mount_entry.cpp b/src/anbox/common/mount_entry.cpp index 3e9d953..675778d 100644 --- a/src/anbox/common/mount_entry.cpp +++ b/src/anbox/common/mount_entry.cpp @@ -17,18 +17,23 @@ #include "anbox/common/mount_entry.h" #include "anbox/common/loop_device.h" +#include "anbox/logger.h" #include namespace anbox { namespace common { std::shared_ptr MountEntry::create(const boost::filesystem::path &src, const boost::filesystem::path &target, - const std::string &fs_type, unsigned long flags) { + const std::string &fs_type, unsigned long flags, const std::string &data) { auto entry = std::shared_ptr(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) + const void *mount_data = nullptr; + if (!data.empty()) + mount_data = reinterpret_cast(data.c_str()); + + if (::mount(src.c_str(), target.c_str(), !fs_type.empty() ? fs_type.c_str() : nullptr, flags, mount_data) != 0) return nullptr; entry->active_ = true; @@ -37,8 +42,8 @@ std::shared_ptr MountEntry::create(const boost::filesystem::path &sr } std::shared_ptr MountEntry::create(const std::shared_ptr &loop, const boost::filesystem::path &target, - const std::string &fs_type, unsigned long flags) { - auto entry = create(loop->path(), target, fs_type, flags); + const std::string &fs_type, unsigned long flags, const std::string &data) { + auto entry = create(loop->path(), target, fs_type, flags, data); if (!entry) return nullptr; diff --git a/src/anbox/common/mount_entry.h b/src/anbox/common/mount_entry.h index cc703e3..2ea95f9 100644 --- a/src/anbox/common/mount_entry.h +++ b/src/anbox/common/mount_entry.h @@ -26,10 +26,10 @@ class LoopDevice; class MountEntry { public: static std::shared_ptr create(const boost::filesystem::path &src, const boost::filesystem::path &target, - const std::string &fs_type = "", unsigned long flags = 0); + const std::string &fs_type = "", unsigned long flags = 0, const std::string &data = ""); static std::shared_ptr create(const std::shared_ptr &loop, const boost::filesystem::path &target, - const std::string &fs_type = "", unsigned long flags = 0); + const std::string &fs_type = "", unsigned long flags = 0, const std::string &data = ""); static std::shared_ptr create(const boost::filesystem::path &target); From f6e421a0897c1f8579b1e62956da338e350a7f2a Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Wed, 12 Jul 2017 09:28:01 +0200 Subject: [PATCH 4/4] Add support to mount rootfs image via squashfuse --- src/anbox/cmds/container_manager.cpp | 78 +++++++++++++++++++++------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/src/anbox/cmds/container_manager.cpp b/src/anbox/cmds/container_manager.cpp index a71f07b..3356fd7 100644 --- a/src/anbox/cmds/container_manager.cpp +++ b/src/anbox/cmds/container_manager.cpp @@ -23,6 +23,7 @@ #include "anbox/config.h" #include "core/posix/signal.h" +#include "core/posix/exec.h" #include #include @@ -121,30 +122,67 @@ bool anbox::cmds::ContainerManager::setup_mounts() { if (!fs::exists(android_rootfs_dir)) fs::create_directory(android_rootfs_dir); - std::shared_ptr loop_device; + // We prefer using the kernel for mounting the squashfs image but + // for some cases (unprivileged containers) where no loop support + // is available we do the mount instead via squashfuse which will + // work entirely in userspace. + if (fs::exists("/dev/loop-control")) { + std::shared_ptr loop_device; - try { - loop_device = common::LoopDeviceAllocator::new_device(); - } catch (const std::exception& e) { - ERROR("Could not create loopback device: %s", e.what()); - return false; - } catch (...) { - ERROR("Could not create loopback device"); + try { + loop_device = common::LoopDeviceAllocator::new_device(); + } catch (const std::exception& e) { + ERROR("Could not create loopback device: %s", e.what()); + return false; + } catch (...) { + ERROR("Could not create loopback 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 | MS_PRIVATE); + if (!m) { + ERROR("Failed to mount Android rootfs"); + return false; + } + mounts_.push_back(m); + } else if (fs::exists("/dev/fuse") && !utils::find_program_on_path("squashfuse").empty()) { + std::vector args = { + "-t", "fuse.squashfuse", + // Allow other users than root to access the rootfs + "-o", "allow_other", + android_img_path.string(), + android_rootfs_dir, + }; + + // Easiest is here to go with the standard mount program as that + // will handle everything for us which is relevant to get the + // squashfs via squashfuse properly mount without having to + // reimplement all the details. Once the mount call comes back + // without an error we can expect the image to be mounted. + auto child = core::posix::exec("/bin/mount", args, {}, core::posix::StandardStream::empty, []() {}); + const auto result = child.wait_for(core::posix::wait::Flags::untraced); + if (result.status != core::posix::wait::Result::Status::exited || + result.detail.if_exited.status != core::posix::exit::Status::success) { + ERROR("Failed to mount squashfs Android image"); + return false; + } + + auto m = common::MountEntry::create(android_rootfs_dir); + if (!m) { + ERROR("Failed to create mount entry for Android rootfs"); + return false; + } + mounts_.push_back(m); + } else { + ERROR("No loop device or FUSE support found. Can't setup Android rootfs!"); 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 | MS_PRIVATE); - if (!m) { - ERROR("Failed to mount Android rootfs"); - return false; - } - mounts_.push_back(m); - for (const auto &dir_name : std::vector{"cache", "data"}) { auto target_dir_path = fs::path(android_rootfs_dir) / dir_name; auto src_dir_path = SystemConfiguration::instance().data_dir() / dir_name;