From 40911e3faad280394d8a34bc69af3215b55e5423 Mon Sep 17 00:00:00 2001 From: Gurdal Oruklu Date: Sat, 28 Sep 2024 16:02:58 +0000 Subject: [PATCH 1/4] added mount tunnel based addMount() implementation --- .../OciConfigJson1.0.2-dobby.template | 5 +- .../OciConfigJsonVM1.0.2-dobby.template | 5 +- bundle/runtime-schemas/defs-plugins.json | 24 ++ daemon/lib/source/DobbyManager.cpp | 127 +++++++++- .../lib/include/DobbyRdkPluginManager.h | 5 + rdkPlugins/Storage/CMakeLists.txt | 1 + rdkPlugins/Storage/README.md | 32 +++ rdkPlugins/Storage/source/MountProperties.h | 12 + .../Storage/source/MountTunnelDetails.cpp | 229 ++++++++++++++++++ .../Storage/source/MountTunnelDetails.h | 84 +++++++ rdkPlugins/Storage/source/Storage.cpp | 132 +++++++++- rdkPlugins/Storage/source/Storage.h | 4 + 12 files changed, 648 insertions(+), 12 deletions(-) create mode 100644 rdkPlugins/Storage/source/MountTunnelDetails.cpp create mode 100644 rdkPlugins/Storage/source/MountTunnelDetails.h diff --git a/bundle/lib/source/templates/OciConfigJson1.0.2-dobby.template b/bundle/lib/source/templates/OciConfigJson1.0.2-dobby.template index 9bd0afe2..3b21d2b0 100644 --- a/bundle/lib/source/templates/OciConfigJson1.0.2-dobby.template +++ b/bundle/lib/source/templates/OciConfigJson1.0.2-dobby.template @@ -105,8 +105,6 @@ static const char* ociJsonTemplate = R"JSON( "readonly": true }, - "rootfsPropagation": "rprivate", - "hostname": "dobby", "mounts": [ @@ -377,7 +375,8 @@ static const char* ociJsonTemplate = R"JSON( "/proc/irq", "/proc/sys", "/proc/sysrq-trigger" - ] + ], + "rootfsPropagation": "slave" }, {{#ENABLE_LEGACY_PLUGINS}} "legacyPlugins": { diff --git a/bundle/lib/source/templates/OciConfigJsonVM1.0.2-dobby.template b/bundle/lib/source/templates/OciConfigJsonVM1.0.2-dobby.template index 51c253b4..c6fb1130 100644 --- a/bundle/lib/source/templates/OciConfigJsonVM1.0.2-dobby.template +++ b/bundle/lib/source/templates/OciConfigJsonVM1.0.2-dobby.template @@ -105,8 +105,6 @@ static const char* ociJsonTemplate = R"JSON( "readonly": true }, - "rootfsPropagation": "rprivate", - "hostname": "dobby", "mounts": [ @@ -388,7 +386,8 @@ static const char* ociJsonTemplate = R"JSON( "/proc/irq", "/proc/sys", "/proc/sysrq-trigger" - ] + ], + "rootfsPropagation": "slave" }, {{#ENABLE_LEGACY_PLUGINS}} "legacyPlugins": { diff --git a/bundle/runtime-schemas/defs-plugins.json b/bundle/runtime-schemas/defs-plugins.json index b93abbfb..c0b8fdb9 100644 --- a/bundle/runtime-schemas/defs-plugins.json +++ b/bundle/runtime-schemas/defs-plugins.json @@ -385,6 +385,30 @@ } } }, + "mounttunnel": { + "type": "array", + "items": { + "type": "object", + "required": [ + "destination", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "flags": { + "$ref": "defs.json#/definitions/int32" + }, + "options": { + "$ref": "defs.json#/definitions/ArrayOfStrings" + }, + "source": { + "type": "string" + } + } + } + }, "mountOwner": { "type": "array", "items": { diff --git a/daemon/lib/source/DobbyManager.cpp b/daemon/lib/source/DobbyManager.cpp index ec4b50fc..60749d9d 100644 --- a/daemon/lib/source/DobbyManager.cpp +++ b/daemon/lib/source/DobbyManager.cpp @@ -1849,7 +1849,7 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st AI_LOG_FN_EXIT(); return false; } - + #ifdef HAVE_LINUX_MOUNT_H int fdMnt = syscall(SYS_open_tree, -EBADF, source.c_str(), OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE); if (fdMnt < 0) @@ -1924,9 +1924,128 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st AI_LOG_FN_EXIT(); return true; #else - AI_LOG_ERROR("open_tree and move_mount are not supported on this platform, aborting mount operation inside %s", id.c_str()); - AI_LOG_FN_EXIT(); - return false; + // get the mount tunnel configuration from storage plugin + if(it->second->rdkPluginManager == nullptr || + it->second->rdkPluginManager->getContainerConfig()->rdk_plugins->storage == nullptr || + it->second->rdkPluginManager->getContainerConfig()->rdk_plugins->storage->data->mounttunnel_len == 0) + { + AI_LOG_ERROR("mount tunnel is not configured in %s", id.c_str()); + AI_LOG_FN_EXIT(); + return false; + } + + auto mounttunnel = it->second->rdkPluginManager->getContainerConfig()->rdk_plugins->storage->data->mounttunnel[0]; + if(mounttunnel->source == nullptr || mounttunnel->destination == nullptr) + { + AI_LOG_ERROR("mount tunnel source or destination not found for %s", id.c_str()); + AI_LOG_FN_EXIT(); + return false; + } + + std::string mountPointInsideContainer = destination; + std::string tempMountPointInsideContainer = std::string(mounttunnel->destination) + "/tmpdir"; + std::string tempMountPointOutsideContainer = std::string(mounttunnel->source) + "/tmpdir"; + + AI_LOG_INFO("temp mount point outside container: %s", tempMountPointOutsideContainer.c_str()); + AI_LOG_INFO("temp mount point inside container: %s", tempMountPointInsideContainer.c_str()); + + // create the temporary mount point outside the container + mUtilities->mkdirRecursive(tempMountPointOutsideContainer.c_str(), 0755); + + // mount the source dir on the temporary mount point outside the container + // this is needed to move the mount inside the container namespace later + if(mount(source.c_str(), tempMountPointOutsideContainer.c_str(), nullptr, mountOptions, nullptr)) + { + AI_LOG_WARN("mount failed for %s errno: %d", destination.c_str(), errno); + mUtilities->rmdirRecursive(tempMountPointOutsideContainer.c_str()); + AI_LOG_FN_EXIT(); + return false; + } + + AI_LOG_INFO("%s is mounted on %s inside %s", source.c_str(), tempMountPointOutsideContainer.c_str(), id.c_str()); + + auto doMoveMountLambda = [containerUID, containerGID, tempMountPointInsideContainer, mountPointInsideContainer, mountOptions, mountData]() + { + // switch to uid / gid of the host since we are still in the host user namespace + if (syscall(SYS_setresgid, -1, containerGID, -1) != 0) + { + AI_LOG_ERROR("failed to setresgid for container with GID: %d", containerGID); + return false; + } + + if (syscall(SYS_setresuid, -1, containerUID, -1) != 0) + { + AI_LOG_ERROR("failed to setresuid for container with UID: %d", containerUID); + return false; + } + + if (mkdir(mountPointInsideContainer.c_str(), 0755) != 0) + { + AI_LOG_ERROR("failed to create destination directory %s", mountPointInsideContainer.c_str()); + return false; + } + // revert back to root for the mount + if (syscall(SYS_setresgid, -1, 0, -1) != 0) + { + AI_LOG_ERROR("failed to setresgid for root"); + return false; + } + + if (syscall(SYS_setresuid, -1, 0, -1) != 0) + { + AI_LOG_ERROR("failed to setresuid for root"); + return false; + } + // move the mount from the temporary mount point inside the container to the final mount point + if(mount(tempMountPointInsideContainer.c_str(), mountPointInsideContainer.c_str(), nullptr, mountOptions | MS_MOVE, nullptr)) + { + AI_LOG_WARN("mount failed for src %s, dest %s errno: %d", tempMountPointInsideContainer.c_str(), mountPointInsideContainer.c_str(), errno); + return false; + } + return true; + + }; + + bool success = true; + if(!mUtilities->callInNamespace(containerPid, CLONE_NEWNS, doMoveMountLambda)) + { + AI_LOG_ERROR("failed to addMount for %s in %s", source.c_str(), id.c_str()); + success = false; + } + + // cleanup the temporary mount on the host, we don't need it anymore + if (umount2(tempMountPointOutsideContainer.c_str(), UMOUNT_NOFOLLOW) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to unmount '%s'", + tempMountPointOutsideContainer.c_str()); + } + else + { + AI_LOG_INFO("unmounted temp mount @ '%s', now deleting mount point", + tempMountPointOutsideContainer.c_str()); + + // can now delete the temporary mount point + if (rmdir(tempMountPointOutsideContainer.c_str()) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to delete temp mount point @ '%s'", + tempMountPointOutsideContainer.c_str()); + }else{ + AI_LOG_INFO("deleted temp mount point @ '%s'", tempMountPointOutsideContainer.c_str()); + } + } + + if(success) + { + AI_LOG_INFO("%s is mounted on %s inside %s", source.c_str(), destination.c_str(), id.c_str()); + AI_LOG_FN_EXIT(); + return true; + } + else + { + AI_LOG_ERROR("failed to addMount for %s in %s", source.c_str(), id.c_str()); + AI_LOG_FN_EXIT(); + return false; + } #endif } diff --git a/pluginLauncher/lib/include/DobbyRdkPluginManager.h b/pluginLauncher/lib/include/DobbyRdkPluginManager.h index a74fe1b7..2eeb581d 100644 --- a/pluginLauncher/lib/include/DobbyRdkPluginManager.h +++ b/pluginLauncher/lib/include/DobbyRdkPluginManager.h @@ -62,6 +62,11 @@ class DobbyRdkPluginManager // This is public as RDKPluginManager isn't responsible for handling logging std::shared_ptr getContainerLogger() const; void setExitStatus(int status) const; + + std::shared_ptr getContainerConfig() const + { + return mContainerConfig; + }; private: bool loadPlugins(); diff --git a/rdkPlugins/Storage/CMakeLists.txt b/rdkPlugins/Storage/CMakeLists.txt index 75057f3b..1d033e72 100644 --- a/rdkPlugins/Storage/CMakeLists.txt +++ b/rdkPlugins/Storage/CMakeLists.txt @@ -30,6 +30,7 @@ add_library( ${PROJECT_NAME} source/LoopMountDetails.cpp source/DynamicMountDetails.cpp source/MountOwnerDetails.cpp + source/MountTunnelDetails.cpp ) target_include_directories(${PROJECT_NAME} diff --git a/rdkPlugins/Storage/README.md b/rdkPlugins/Storage/README.md index 38b7a5f4..5f2ddf6e 100644 --- a/rdkPlugins/Storage/README.md +++ b/rdkPlugins/Storage/README.md @@ -54,6 +54,38 @@ It will mount "source" into container "destination" only if the source exists on } } ``` +### Mount tunnels +Add the following section to your OCI runtime configuration `config.json` file to create a mount tunnel. + +This will enable dynamic mounting of host devices/directories inside the container on devices running older linux kernels. + +You need to have `rootfsPropagation` set to `slave` in the OCI runtime configuration for the tunneling to work. +Some references : +- https://lwn.net/Articles/690679/ +- https://brauner.io/2023/02/28/mounting-into-mount-namespaces.html +- https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt + +Please note that devices with kernel 5.4 or higher don't need the mount tunnel for dynamic mounts. + +In the config example below, `/mnt/hostmnttunnel` dir will be created on the host, `/mnt/containermnttunnel` dir will be created inside the container's rootfs before the container is launched. +With propagation set to slave mode, any devices or dirs mounted under `/mnt/hostmnttunnel` directory will be visible under `/mnt/containertunnel` inside the container mount namespace allowing dynamic mount() unmount() operations. +```json +{ + "rdkPlugins": { + "storage": { + "required": true, + "data": { + "mounttunnel": [ + { + "destination": "/mnt/containermnttunnel", + "source": "/mnt/hostmnttunnel" + } + ] + } + } + } +} +``` ### Mount Owners Add the following section to your OCI runtime configuration `config.json` file to configure mount ownership. diff --git a/rdkPlugins/Storage/source/MountProperties.h b/rdkPlugins/Storage/source/MountProperties.h index 527b080e..a351f74c 100644 --- a/rdkPlugins/Storage/source/MountProperties.h +++ b/rdkPlugins/Storage/source/MountProperties.h @@ -68,4 +68,16 @@ typedef struct _MountOwnerProperties } MountOwnerProperties; +/** +* @brief MountTunnelProperties struct used for Storage plugin +*/ +typedef struct _MountTunnelProperties +{ + std::string source; + std::string destination; + std::list mountOptions; + unsigned long mountFlags; + +} MountTunnelProperties; + #endif // !defined(MOUNTPROPERTIES_H) diff --git a/rdkPlugins/Storage/source/MountTunnelDetails.cpp b/rdkPlugins/Storage/source/MountTunnelDetails.cpp new file mode 100644 index 00000000..8b5473a6 --- /dev/null +++ b/rdkPlugins/Storage/source/MountTunnelDetails.cpp @@ -0,0 +1,229 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2022 Sky UK +* +* Licensed 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 "MountTunnelDetails.h" +#include "StorageHelper.h" +#include "DobbyRdkPluginUtils.h" +#include "RefCountFile.h" +#include "RefCountFileLock.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Mount tunnel details class +MountTunnelDetails::MountTunnelDetails(const std::string& rootfsPath, + const MountTunnelProperties& mount, + const uid_t& userId, + const gid_t& groupId, + const std::shared_ptr &utils) + : mMount(mount), + mUserId(userId), + mGroupId(groupId), + mUtils(utils) +{ + AI_LOG_FN_ENTRY(); + + mMountPointInsideContainer = rootfsPath + mount.destination; + mTempMountPointOutsideContainer = mount.source; + + AI_LOG_FN_EXIT(); +} + +MountTunnelDetails::~MountTunnelDetails() +{ + AI_LOG_FN_ENTRY(); + + AI_LOG_FN_EXIT(); +} + +// ----------------------------------------------------------------------------- +/** + * @brief Opens the data.img file and mounts it to temp location inside container + * + * @return true on success, false on failure. + */ +bool MountTunnelDetails::onPreCreate() +{ + AI_LOG_FN_ENTRY(); + + if (!DobbyRdkPluginUtils::mkdirRecursive(mTempMountPointOutsideContainer, 0755)) + { + AI_LOG_WARN("failed to create dir '%s'", mTempMountPointOutsideContainer.c_str()); + return false; + } + + if (!DobbyRdkPluginUtils::mkdirRecursive(mMountPointInsideContainer, 0755)) + { + AI_LOG_WARN("failed to create dir '%s'", mMountPointInsideContainer.c_str()); + return false; + } + + if(mount(mTempMountPointOutsideContainer.c_str(), mTempMountPointOutsideContainer.c_str(), NULL, MS_BIND, NULL) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to bind mount '%s'", mTempMountPointOutsideContainer.c_str()); + return false; + } + + if(mount(NULL, mTempMountPointOutsideContainer.c_str(), NULL, MS_PRIVATE, NULL) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to mount MS_PRIVATE @ '%s'", mTempMountPointOutsideContainer.c_str()); + return false; + } + + if(mount(NULL, mTempMountPointOutsideContainer.c_str(), NULL, MS_SHARED, NULL) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to mount MS_SHARED @ '%s'", mTempMountPointOutsideContainer.c_str()); + return false; + } + + AI_LOG_FN_EXIT(); + return true; +} + +// ----------------------------------------------------------------------------- +/** + * @brief Sets permissions for container to access directories + * + * @return true on success, false on failure. + */ +bool MountTunnelDetails::setPermissions() +{ + AI_LOG_FN_ENTRY(); + + bool success = true; +#if 0 + // We need to check the permissions on the image root dir, they + // should allow full read/write by the user inside the container. + // In an ideal world we wouldn't do this here, instead when the fs + // data.img is created it should be passed '-E root_owner=uid:gid', + // however currently our version of mke2fs doesn't support that. + if (mUserId != 0 && mGroupId != 0) + { + // proper value of mUserId and mGroupId + if (chown(mTempMountPointOutsideContainer.c_str(), mUserId, mGroupId) != 0) + { + AI_LOG_SYS_ERROR_EXIT(errno, "failed to chown '%s' to %u:%u", + mTempMountPointOutsideContainer.c_str(), + mUserId, mGroupId); + } + else + { + success = true; + } + } + else + { + AI_LOG_WARN("Config does not contain proper ID/GID to set file permissions"); + + // config.json has not set mUserId/mGroupId so give access to everyone + // so the container could use this mount point + if (chmod(mTempMountPointOutsideContainer.c_str(), 0777) != 0) + { + AI_LOG_SYS_ERROR_EXIT(errno, "failed to set dir '%s' perms to 0%03o", + mTempMountPointOutsideContainer.c_str(), 0777); + } + else + { + success = true; + } + } +#endif + AI_LOG_FN_EXIT(); + return success; +} + +// ----------------------------------------------------------------------------- +/** + * @brief Mounts temp directory into desired one + * + * @return true on success, false on failure. + */ +bool MountTunnelDetails::remountTempDirectory() +{ + AI_LOG_FN_ENTRY(); + + bool success = false; + + if (mount(mTempMountPointOutsideContainer.c_str(), + mMountPointInsideContainer.c_str(), + "", MS_BIND, nullptr) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to bind mount '%s' -> '%s'", + mTempMountPointOutsideContainer.c_str(), + mMountPointInsideContainer.c_str()); + } + else + { + AI_LOG_INFO("created mount tunnel '%s' -> '%s'", + mTempMountPointOutsideContainer.c_str(), + mMountPointInsideContainer.c_str()); + success = true; + } + + AI_LOG_FN_EXIT(); + return success; +} + + +// ----------------------------------------------------------------------------- +/** + * @brief Cleans up temp mount and directory + * + * @return true on success, false on failure. + */ +bool MountTunnelDetails::removeMountTunnel() +{ + AI_LOG_FN_ENTRY(); + + bool success = false; + + if (umount2(mTempMountPointOutsideContainer.c_str(), UMOUNT_NOFOLLOW) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to unmount '%s'", + mTempMountPointOutsideContainer.c_str()); + } + else + { + AI_LOG_DEBUG("unmounted temp mount @ '%s', now deleting mount point", + mTempMountPointOutsideContainer.c_str()); + + // can now delete the temporary mount point + if (rmdir(mTempMountPointOutsideContainer.c_str()) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to delete temp mount point @ '%s'", + mTempMountPointOutsideContainer.c_str()); + } + else + { + success = true; + } + } + + AI_LOG_FN_EXIT(); + return success; +} diff --git a/rdkPlugins/Storage/source/MountTunnelDetails.h b/rdkPlugins/Storage/source/MountTunnelDetails.h new file mode 100644 index 00000000..61d71e8a --- /dev/null +++ b/rdkPlugins/Storage/source/MountTunnelDetails.h @@ -0,0 +1,84 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2024 Sky UK +* +* Licensed 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. +*/ +/* + * File: MountTunnelDetails.h + * + */ +#ifndef MOUNTTUNNELDETAILS_H +#define MOUNTTUNNELDETAILS_H + +#include "MountProperties.h" + +#include + +#include +#include +#include +#include + + +// ----------------------------------------------------------------------------- +/** + * @class MountTunnelDetails + * @brief Class that represents a single loop mount within a container + * + * This class is only intended to be used internally by Storage plugin + * do not use from external code. + * + * @see Storage + */ +class MountTunnelDetails +{ +public: + MountTunnelDetails() = delete; + MountTunnelDetails(MountTunnelDetails&) = delete; + MountTunnelDetails(MountTunnelDetails&&) = delete; + ~MountTunnelDetails(); + +private: + friend class Storage; + +public: + MountTunnelDetails(const std::string& rootfsPath, + const MountTunnelProperties& mount, + const uid_t& userId, + const gid_t& groupId, + const std::shared_ptr &utils); + +public: + + bool onPreCreate(); + + bool setPermissions(); + + bool remountTempDirectory(); + + bool removeMountTunnel(); + +private: + std::string mMountPointInsideContainer; + std::string mTempMountPointOutsideContainer; + MountTunnelProperties mMount; + uid_t mUserId; + gid_t mGroupId; + + const std::shared_ptr mUtils; +}; + +#endif // !defined(MOUNTTUNNELDETAILS_H) diff --git a/rdkPlugins/Storage/source/Storage.cpp b/rdkPlugins/Storage/source/Storage.cpp index b9ffdda3..b4de2bc7 100644 --- a/rdkPlugins/Storage/source/Storage.cpp +++ b/rdkPlugins/Storage/source/Storage.cpp @@ -24,6 +24,7 @@ #include #include #include +#include /** * Need to do this at the start of every plugin to make sure the correct @@ -89,6 +90,18 @@ bool Storage::preCreation() return false; } } + + std::vector> tunnelDetails = getMountTunnelDetails(); + for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) + { + // Creating loop mount and attaching it to temp mount inside container + if(!(*it)->onPreCreate()) + { + AI_LOG_ERROR_EXIT("failed to execute preCreation hook for loop mount"); + return false; + } + } + AI_LOG_FN_EXIT(); return true; } @@ -107,11 +120,21 @@ bool Storage::createRuntime() // Setting permissions for generated directories if(!(*it)->setPermissions()) { - AI_LOG_ERROR_EXIT("failed to execute createRuntime loop hook"); + AI_LOG_ERROR_EXIT("failed to set permissions for loop mount"); + return false; + } + } + + std::vector> tunnelDetails = getMountTunnelDetails(); + for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) + { + // Setting permissions for generated directories + if(!(*it)->setPermissions()) + { + AI_LOG_ERROR_EXIT("failed to set permissions for loop mount"); return false; } } - // create destination paths for each dynamic mount std::vector> dynamicMountDetails = getDynamicMountDetails(); for(auto it = dynamicMountDetails.begin(); it != dynamicMountDetails.end(); it++) @@ -148,6 +171,17 @@ bool Storage::createContainer() { AI_LOG_FN_ENTRY(); + std::vector> tunnelDetails = getMountTunnelDetails(); + for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) + { + // Remount temp directory into proper place + if(!(*it)->remountTempDirectory()) + { + AI_LOG_ERROR_EXIT("failed to execute createRuntime loop hook"); + return false; + } + } + // Mount temp directory in proper place std::vector> loopMountDetails = getLoopMountDetails(); for(auto it = loopMountDetails.begin(); it != loopMountDetails.end(); it++) @@ -250,6 +284,18 @@ bool Storage::postStop() } } + std::vector> tunnelDetails = getMountTunnelDetails(); + for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) + { + // Clean up temp mount points + if(!(*it)->removeMountTunnel()) + { + AI_LOG_ERROR_EXIT("failed to clean up non persistent image"); + // This is probably to late to fail but do it either way + return false; + } + } + AI_LOG_FN_EXIT(); return true; } @@ -580,6 +626,88 @@ std::vector Storage::getMountOwners() const return mountOwners; } +// ----------------------------------------------------------------------------- +/** + * @brief Create mount owner details vector from all mount owners in config. + * + * + * @return vector of MountOwnerDetails that were in the config + */ +std::vector> Storage::getMountTunnelDetails() const +{ + AI_LOG_FN_ENTRY(); + + const std::vector mountTunnels = getMountTunnel(); + std::vector> tunnelDetails; + + // loop though all the loop mounts for the given container and create individual + // LoopMountDetails objects for each + for (const MountTunnelProperties &properties : mountTunnels) + { + // Setup the user/group IDs + uid_t uid = 0; + gid_t gid = 0; + setupOwnerIds(uid, gid); + + // create the loop mount and make sure it was constructed + auto mountTunnel = std::make_unique(mRootfsPath, + properties, + uid, + gid, + mUtils); + + if (mountTunnel) + { + tunnelDetails.emplace_back(std::move(mountTunnel)); + } + } + + AI_LOG_FN_EXIT(); + return tunnelDetails; + +} +// ----------------------------------------------------------------------------- +/** + * @brief Reads container config and creates the MountTunnelProperties + * type object. + * + * + * @return MountTunnelProperties object that was in the config + */ +std::vector Storage::getMountTunnel() const +{ + AI_LOG_FN_ENTRY(); + + std::vector tunnelProp; + + // Check if container has mount data + if (mContainerConfig->rdk_plugins->storage->data) + { + for (size_t i = 0; i < mContainerConfig->rdk_plugins->storage->data->mounttunnel_len; i++) + { + auto mounttunnel = mContainerConfig->rdk_plugins->storage->data->mounttunnel[i]; + + MountTunnelProperties tunnel; + tunnel.source = std::string(mounttunnel->source); + tunnel.destination = std::string(mounttunnel->destination); + tunnel.mountFlags = mounttunnel->flags; + + for (size_t j = 0; j < mounttunnel->options_len; j++) + { + tunnel.mountOptions.push_back(std::string(mounttunnel->options[j])); + } + + tunnelProp.push_back(tunnel); + } + } + else + { + AI_LOG_ERROR("No storage data in config file"); + } + + AI_LOG_FN_EXIT(); + return tunnelProp; +} // ----------------------------------------------------------------------------- /** * @brief Gets userId and groupId diff --git a/rdkPlugins/Storage/source/Storage.h b/rdkPlugins/Storage/source/Storage.h index 79f4cd20..1b13f9a9 100644 --- a/rdkPlugins/Storage/source/Storage.h +++ b/rdkPlugins/Storage/source/Storage.h @@ -26,6 +26,7 @@ #include "LoopMountDetails.h" #include "DynamicMountDetails.h" #include "MountOwnerDetails.h" +#include "MountTunnelDetails.h" #include @@ -94,6 +95,9 @@ class Storage : public RdkPluginBase std::vector getMountOwners() const; std::vector> getMountOwnerDetails() const; + std::vector getMountTunnel() const; + std::vector> getMountTunnelDetails() const; + void setupOwnerIds(uid_t& uid, gid_t& gid) const; private: From 11350ac9103f10614c5ba52ce06b3dfa87835eed Mon Sep 17 00:00:00 2001 From: Gurdal Oruklu Date: Fri, 25 Oct 2024 05:47:39 +0000 Subject: [PATCH 2/4] cleaned up/simplified the mount tunnel implementation Signed-off-by: Gurdal Oruklu --- CMakeLists.txt | 1 + bundle/runtime-schemas/defs-plugins.json | 24 -- daemon/lib/source/DobbyManager.cpp | 34 +-- .../lib/include/DobbyRdkPluginUtils.h | 3 + rdkPlugins/Storage/CMakeLists.txt | 1 - rdkPlugins/Storage/README.md | 26 +- rdkPlugins/Storage/source/MountProperties.h | 12 - .../Storage/source/MountTunnelDetails.cpp | 229 ------------------ .../Storage/source/MountTunnelDetails.h | 84 ------- rdkPlugins/Storage/source/Storage.cpp | 172 +++++-------- rdkPlugins/Storage/source/Storage.h | 9 +- 11 files changed, 75 insertions(+), 520 deletions(-) delete mode 100644 rdkPlugins/Storage/source/MountTunnelDetails.cpp delete mode 100644 rdkPlugins/Storage/source/MountTunnelDetails.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 832f3a29..9b7df530 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,6 +168,7 @@ find_package( breakpad QUIET ) find_path(LINUXMOUNT NAMES "linux/mount.h") if(NOT LINUXMOUNT) message( "Couldn't find linux/mount.h. You may need to upgrade your kernel to 5.2 or later" ) + add_definitions( -DUSE_MOUNT_TUNNEL ) else() message("Found linux/mount.h") add_definitions( -DHAVE_LINUX_MOUNT_H ) diff --git a/bundle/runtime-schemas/defs-plugins.json b/bundle/runtime-schemas/defs-plugins.json index c0b8fdb9..b93abbfb 100644 --- a/bundle/runtime-schemas/defs-plugins.json +++ b/bundle/runtime-schemas/defs-plugins.json @@ -385,30 +385,6 @@ } } }, - "mounttunnel": { - "type": "array", - "items": { - "type": "object", - "required": [ - "destination", - "source" - ], - "properties": { - "destination": { - "type": "string" - }, - "flags": { - "$ref": "defs.json#/definitions/int32" - }, - "options": { - "$ref": "defs.json#/definitions/ArrayOfStrings" - }, - "source": { - "type": "string" - } - } - } - }, "mountOwner": { "type": "array", "items": { diff --git a/daemon/lib/source/DobbyManager.cpp b/daemon/lib/source/DobbyManager.cpp index 60749d9d..db1838bc 100644 --- a/daemon/lib/source/DobbyManager.cpp +++ b/daemon/lib/source/DobbyManager.cpp @@ -1924,33 +1924,17 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st AI_LOG_FN_EXIT(); return true; #else - // get the mount tunnel configuration from storage plugin - if(it->second->rdkPluginManager == nullptr || - it->second->rdkPluginManager->getContainerConfig()->rdk_plugins->storage == nullptr || - it->second->rdkPluginManager->getContainerConfig()->rdk_plugins->storage->data->mounttunnel_len == 0) - { - AI_LOG_ERROR("mount tunnel is not configured in %s", id.c_str()); - AI_LOG_FN_EXIT(); - return false; - } - - auto mounttunnel = it->second->rdkPluginManager->getContainerConfig()->rdk_plugins->storage->data->mounttunnel[0]; - if(mounttunnel->source == nullptr || mounttunnel->destination == nullptr) - { - AI_LOG_ERROR("mount tunnel source or destination not found for %s", id.c_str()); - AI_LOG_FN_EXIT(); - return false; - } - std::string mountPointInsideContainer = destination; - std::string tempMountPointInsideContainer = std::string(mounttunnel->destination) + "/tmpdir"; - std::string tempMountPointOutsideContainer = std::string(mounttunnel->source) + "/tmpdir"; - - AI_LOG_INFO("temp mount point outside container: %s", tempMountPointOutsideContainer.c_str()); - AI_LOG_INFO("temp mount point inside container: %s", tempMountPointInsideContainer.c_str()); + std::string tempMountPointInsideContainer = std::string(MOUNT_TUNNEL_CONTAINER_PATH) + "/tmpdir"; + std::string tempMountPointOutsideContainer = std::string(MOUNT_TUNNEL_HOST_PATH) + "/tmpdir"; // create the temporary mount point outside the container - mUtilities->mkdirRecursive(tempMountPointOutsideContainer.c_str(), 0755); + if (!mUtilities->mkdirRecursive(tempMountPointOutsideContainer.c_str(), 0755)) + { + AI_LOG_ERROR("failed to create temporary mount point %s", tempMountPointOutsideContainer.c_str()); + AI_LOG_FN_EXIT(); + return false; + } // mount the source dir on the temporary mount point outside the container // this is needed to move the mount inside the container namespace later @@ -1962,8 +1946,6 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st return false; } - AI_LOG_INFO("%s is mounted on %s inside %s", source.c_str(), tempMountPointOutsideContainer.c_str(), id.c_str()); - auto doMoveMountLambda = [containerUID, containerGID, tempMountPointInsideContainer, mountPointInsideContainer, mountOptions, mountData]() { // switch to uid / gid of the host since we are still in the host user namespace diff --git a/pluginLauncher/lib/include/DobbyRdkPluginUtils.h b/pluginLauncher/lib/include/DobbyRdkPluginUtils.h index 0886eb1a..2af4f08b 100644 --- a/pluginLauncher/lib/include/DobbyRdkPluginUtils.h +++ b/pluginLauncher/lib/include/DobbyRdkPluginUtils.h @@ -47,6 +47,9 @@ // but we don't programatically know the workspace dir in this code. #define ADDRESS_FILE_DIR "/tmp/dobby/plugin/networking/" +#define MOUNT_TUNNEL_CONTAINER_PATH "/mnt/.containermnttunnel" +#define MOUNT_TUNNEL_HOST_PATH "/tmp/.hostmnttunnel" + typedef struct ContainerNetworkInfo { std::string vethName; diff --git a/rdkPlugins/Storage/CMakeLists.txt b/rdkPlugins/Storage/CMakeLists.txt index 1d033e72..75057f3b 100644 --- a/rdkPlugins/Storage/CMakeLists.txt +++ b/rdkPlugins/Storage/CMakeLists.txt @@ -30,7 +30,6 @@ add_library( ${PROJECT_NAME} source/LoopMountDetails.cpp source/DynamicMountDetails.cpp source/MountOwnerDetails.cpp - source/MountTunnelDetails.cpp ) target_include_directories(${PROJECT_NAME} diff --git a/rdkPlugins/Storage/README.md b/rdkPlugins/Storage/README.md index 5f2ddf6e..e4698a78 100644 --- a/rdkPlugins/Storage/README.md +++ b/rdkPlugins/Storage/README.md @@ -54,9 +54,8 @@ It will mount "source" into container "destination" only if the source exists on } } ``` -### Mount tunnels -Add the following section to your OCI runtime configuration `config.json` file to create a mount tunnel. - +### Mount tunnel +Storage plugin will create a mount tunnel on devices running older linux kernels. This will enable dynamic mounting of host devices/directories inside the container on devices running older linux kernels. You need to have `rootfsPropagation` set to `slave` in the OCI runtime configuration for the tunneling to work. @@ -65,27 +64,8 @@ Some references : - https://brauner.io/2023/02/28/mounting-into-mount-namespaces.html - https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt -Please note that devices with kernel 5.4 or higher don't need the mount tunnel for dynamic mounts. +Please note that devices with kernel 5.4 or higher don't need the mount tunnel for dynamic mounts and this code will be disabled at build time. -In the config example below, `/mnt/hostmnttunnel` dir will be created on the host, `/mnt/containermnttunnel` dir will be created inside the container's rootfs before the container is launched. -With propagation set to slave mode, any devices or dirs mounted under `/mnt/hostmnttunnel` directory will be visible under `/mnt/containertunnel` inside the container mount namespace allowing dynamic mount() unmount() operations. -```json -{ - "rdkPlugins": { - "storage": { - "required": true, - "data": { - "mounttunnel": [ - { - "destination": "/mnt/containermnttunnel", - "source": "/mnt/hostmnttunnel" - } - ] - } - } - } -} -``` ### Mount Owners Add the following section to your OCI runtime configuration `config.json` file to configure mount ownership. diff --git a/rdkPlugins/Storage/source/MountProperties.h b/rdkPlugins/Storage/source/MountProperties.h index a351f74c..527b080e 100644 --- a/rdkPlugins/Storage/source/MountProperties.h +++ b/rdkPlugins/Storage/source/MountProperties.h @@ -68,16 +68,4 @@ typedef struct _MountOwnerProperties } MountOwnerProperties; -/** -* @brief MountTunnelProperties struct used for Storage plugin -*/ -typedef struct _MountTunnelProperties -{ - std::string source; - std::string destination; - std::list mountOptions; - unsigned long mountFlags; - -} MountTunnelProperties; - #endif // !defined(MOUNTPROPERTIES_H) diff --git a/rdkPlugins/Storage/source/MountTunnelDetails.cpp b/rdkPlugins/Storage/source/MountTunnelDetails.cpp deleted file mode 100644 index 8b5473a6..00000000 --- a/rdkPlugins/Storage/source/MountTunnelDetails.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* -* If not stated otherwise in this file or this component's LICENSE file the -* following copyright and licenses apply: -* -* Copyright 2022 Sky UK -* -* Licensed 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 "MountTunnelDetails.h" -#include "StorageHelper.h" -#include "DobbyRdkPluginUtils.h" -#include "RefCountFile.h" -#include "RefCountFileLock.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Mount tunnel details class -MountTunnelDetails::MountTunnelDetails(const std::string& rootfsPath, - const MountTunnelProperties& mount, - const uid_t& userId, - const gid_t& groupId, - const std::shared_ptr &utils) - : mMount(mount), - mUserId(userId), - mGroupId(groupId), - mUtils(utils) -{ - AI_LOG_FN_ENTRY(); - - mMountPointInsideContainer = rootfsPath + mount.destination; - mTempMountPointOutsideContainer = mount.source; - - AI_LOG_FN_EXIT(); -} - -MountTunnelDetails::~MountTunnelDetails() -{ - AI_LOG_FN_ENTRY(); - - AI_LOG_FN_EXIT(); -} - -// ----------------------------------------------------------------------------- -/** - * @brief Opens the data.img file and mounts it to temp location inside container - * - * @return true on success, false on failure. - */ -bool MountTunnelDetails::onPreCreate() -{ - AI_LOG_FN_ENTRY(); - - if (!DobbyRdkPluginUtils::mkdirRecursive(mTempMountPointOutsideContainer, 0755)) - { - AI_LOG_WARN("failed to create dir '%s'", mTempMountPointOutsideContainer.c_str()); - return false; - } - - if (!DobbyRdkPluginUtils::mkdirRecursive(mMountPointInsideContainer, 0755)) - { - AI_LOG_WARN("failed to create dir '%s'", mMountPointInsideContainer.c_str()); - return false; - } - - if(mount(mTempMountPointOutsideContainer.c_str(), mTempMountPointOutsideContainer.c_str(), NULL, MS_BIND, NULL) != 0) - { - AI_LOG_SYS_ERROR(errno, "failed to bind mount '%s'", mTempMountPointOutsideContainer.c_str()); - return false; - } - - if(mount(NULL, mTempMountPointOutsideContainer.c_str(), NULL, MS_PRIVATE, NULL) != 0) - { - AI_LOG_SYS_ERROR(errno, "failed to mount MS_PRIVATE @ '%s'", mTempMountPointOutsideContainer.c_str()); - return false; - } - - if(mount(NULL, mTempMountPointOutsideContainer.c_str(), NULL, MS_SHARED, NULL) != 0) - { - AI_LOG_SYS_ERROR(errno, "failed to mount MS_SHARED @ '%s'", mTempMountPointOutsideContainer.c_str()); - return false; - } - - AI_LOG_FN_EXIT(); - return true; -} - -// ----------------------------------------------------------------------------- -/** - * @brief Sets permissions for container to access directories - * - * @return true on success, false on failure. - */ -bool MountTunnelDetails::setPermissions() -{ - AI_LOG_FN_ENTRY(); - - bool success = true; -#if 0 - // We need to check the permissions on the image root dir, they - // should allow full read/write by the user inside the container. - // In an ideal world we wouldn't do this here, instead when the fs - // data.img is created it should be passed '-E root_owner=uid:gid', - // however currently our version of mke2fs doesn't support that. - if (mUserId != 0 && mGroupId != 0) - { - // proper value of mUserId and mGroupId - if (chown(mTempMountPointOutsideContainer.c_str(), mUserId, mGroupId) != 0) - { - AI_LOG_SYS_ERROR_EXIT(errno, "failed to chown '%s' to %u:%u", - mTempMountPointOutsideContainer.c_str(), - mUserId, mGroupId); - } - else - { - success = true; - } - } - else - { - AI_LOG_WARN("Config does not contain proper ID/GID to set file permissions"); - - // config.json has not set mUserId/mGroupId so give access to everyone - // so the container could use this mount point - if (chmod(mTempMountPointOutsideContainer.c_str(), 0777) != 0) - { - AI_LOG_SYS_ERROR_EXIT(errno, "failed to set dir '%s' perms to 0%03o", - mTempMountPointOutsideContainer.c_str(), 0777); - } - else - { - success = true; - } - } -#endif - AI_LOG_FN_EXIT(); - return success; -} - -// ----------------------------------------------------------------------------- -/** - * @brief Mounts temp directory into desired one - * - * @return true on success, false on failure. - */ -bool MountTunnelDetails::remountTempDirectory() -{ - AI_LOG_FN_ENTRY(); - - bool success = false; - - if (mount(mTempMountPointOutsideContainer.c_str(), - mMountPointInsideContainer.c_str(), - "", MS_BIND, nullptr) != 0) - { - AI_LOG_SYS_ERROR(errno, "failed to bind mount '%s' -> '%s'", - mTempMountPointOutsideContainer.c_str(), - mMountPointInsideContainer.c_str()); - } - else - { - AI_LOG_INFO("created mount tunnel '%s' -> '%s'", - mTempMountPointOutsideContainer.c_str(), - mMountPointInsideContainer.c_str()); - success = true; - } - - AI_LOG_FN_EXIT(); - return success; -} - - -// ----------------------------------------------------------------------------- -/** - * @brief Cleans up temp mount and directory - * - * @return true on success, false on failure. - */ -bool MountTunnelDetails::removeMountTunnel() -{ - AI_LOG_FN_ENTRY(); - - bool success = false; - - if (umount2(mTempMountPointOutsideContainer.c_str(), UMOUNT_NOFOLLOW) != 0) - { - AI_LOG_SYS_ERROR(errno, "failed to unmount '%s'", - mTempMountPointOutsideContainer.c_str()); - } - else - { - AI_LOG_DEBUG("unmounted temp mount @ '%s', now deleting mount point", - mTempMountPointOutsideContainer.c_str()); - - // can now delete the temporary mount point - if (rmdir(mTempMountPointOutsideContainer.c_str()) != 0) - { - AI_LOG_SYS_ERROR(errno, "failed to delete temp mount point @ '%s'", - mTempMountPointOutsideContainer.c_str()); - } - else - { - success = true; - } - } - - AI_LOG_FN_EXIT(); - return success; -} diff --git a/rdkPlugins/Storage/source/MountTunnelDetails.h b/rdkPlugins/Storage/source/MountTunnelDetails.h deleted file mode 100644 index 61d71e8a..00000000 --- a/rdkPlugins/Storage/source/MountTunnelDetails.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -* If not stated otherwise in this file or this component's LICENSE file the -* following copyright and licenses apply: -* -* Copyright 2024 Sky UK -* -* Licensed 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. -*/ -/* - * File: MountTunnelDetails.h - * - */ -#ifndef MOUNTTUNNELDETAILS_H -#define MOUNTTUNNELDETAILS_H - -#include "MountProperties.h" - -#include - -#include -#include -#include -#include - - -// ----------------------------------------------------------------------------- -/** - * @class MountTunnelDetails - * @brief Class that represents a single loop mount within a container - * - * This class is only intended to be used internally by Storage plugin - * do not use from external code. - * - * @see Storage - */ -class MountTunnelDetails -{ -public: - MountTunnelDetails() = delete; - MountTunnelDetails(MountTunnelDetails&) = delete; - MountTunnelDetails(MountTunnelDetails&&) = delete; - ~MountTunnelDetails(); - -private: - friend class Storage; - -public: - MountTunnelDetails(const std::string& rootfsPath, - const MountTunnelProperties& mount, - const uid_t& userId, - const gid_t& groupId, - const std::shared_ptr &utils); - -public: - - bool onPreCreate(); - - bool setPermissions(); - - bool remountTempDirectory(); - - bool removeMountTunnel(); - -private: - std::string mMountPointInsideContainer; - std::string mTempMountPointOutsideContainer; - MountTunnelProperties mMount; - uid_t mUserId; - gid_t mGroupId; - - const std::shared_ptr mUtils; -}; - -#endif // !defined(MOUNTTUNNELDETAILS_H) diff --git a/rdkPlugins/Storage/source/Storage.cpp b/rdkPlugins/Storage/source/Storage.cpp index b4de2bc7..a7d8dacd 100644 --- a/rdkPlugins/Storage/source/Storage.cpp +++ b/rdkPlugins/Storage/source/Storage.cpp @@ -46,6 +46,10 @@ Storage::Storage(std::shared_ptr &containerSpec, : mName("Storage"), mContainerConfig(containerSpec), mRootfsPath(rootfsPath), +#ifdef USE_MOUNT_TUNNEL + mMountPointInsideContainer(rootfsPath + MOUNT_TUNNEL_CONTAINER_PATH), + mTempMountPointOutsideContainer(MOUNT_TUNNEL_HOST_PATH), +#endif mUtils(utils) { AI_LOG_FN_ENTRY(); @@ -90,17 +94,27 @@ bool Storage::preCreation() return false; } } - - std::vector> tunnelDetails = getMountTunnelDetails(); - for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) + +#ifdef USE_MOUNT_TUNNEL + // Create host directory for the mount tunnel + if (!DobbyRdkPluginUtils::mkdirRecursive(mTempMountPointOutsideContainer, 0755)) { - // Creating loop mount and attaching it to temp mount inside container - if(!(*it)->onPreCreate()) - { - AI_LOG_ERROR_EXIT("failed to execute preCreation hook for loop mount"); - return false; - } + AI_LOG_WARN("failed to create dir '%s'", mTempMountPointOutsideContainer.c_str()); + return false; + } + // Create directory inside the container rootfs for the mount tunnel + if (!DobbyRdkPluginUtils::mkdirRecursive(mMountPointInsideContainer, 0755)) + { + AI_LOG_WARN("failed to create dir '%s'", mMountPointInsideContainer.c_str()); + return false; + } + + if(mount(mTempMountPointOutsideContainer.c_str(), mTempMountPointOutsideContainer.c_str(), NULL, MS_BIND, NULL) != 0) + { + AI_LOG_SYS_ERROR(errno, "failed to bind mount '%s'", mTempMountPointOutsideContainer.c_str()); + return false; } +#endif AI_LOG_FN_EXIT(); return true; @@ -124,17 +138,7 @@ bool Storage::createRuntime() return false; } } - - std::vector> tunnelDetails = getMountTunnelDetails(); - for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) - { - // Setting permissions for generated directories - if(!(*it)->setPermissions()) - { - AI_LOG_ERROR_EXIT("failed to set permissions for loop mount"); - return false; - } - } + // create destination paths for each dynamic mount std::vector> dynamicMountDetails = getDynamicMountDetails(); for(auto it = dynamicMountDetails.begin(); it != dynamicMountDetails.end(); it++) @@ -171,16 +175,24 @@ bool Storage::createContainer() { AI_LOG_FN_ENTRY(); - std::vector> tunnelDetails = getMountTunnelDetails(); - for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) +#ifdef USE_MOUNT_TUNNEL + // create the mount tunnel, the mounts on the host will now be visible inside the container dynamically + if (mount(mTempMountPointOutsideContainer.c_str(), + mMountPointInsideContainer.c_str(), + "", MS_BIND, nullptr) != 0) { - // Remount temp directory into proper place - if(!(*it)->remountTempDirectory()) - { - AI_LOG_ERROR_EXIT("failed to execute createRuntime loop hook"); - return false; - } + AI_LOG_ERROR_EXIT("failed to bind mount '%s' -> '%s'", + mTempMountPointOutsideContainer.c_str(), + mMountPointInsideContainer.c_str()); + return false; + } + else + { + AI_LOG_INFO("created mount tunnel '%s' -> '%s'", + mTempMountPointOutsideContainer.c_str(), + mMountPointInsideContainer.c_str()); } +#endif // Mount temp directory in proper place std::vector> loopMountDetails = getLoopMountDetails(); @@ -284,17 +296,27 @@ bool Storage::postStop() } } - std::vector> tunnelDetails = getMountTunnelDetails(); - for(auto it = tunnelDetails.begin(); it != tunnelDetails.end(); it++) +#ifdef USE_MOUNT_TUNNEL + // cleanup for the mount tunnel + if (umount2(mTempMountPointOutsideContainer.c_str(), UMOUNT_NOFOLLOW) != 0) { - // Clean up temp mount points - if(!(*it)->removeMountTunnel()) + AI_LOG_SYS_ERROR(errno, "failed to unmount '%s'", + mTempMountPointOutsideContainer.c_str()); + } + else + { + AI_LOG_DEBUG("unmounted temp mount @ '%s', now deleting mount point", + mTempMountPointOutsideContainer.c_str()); + + // can now delete the temporary mount point + if (rmdir(mTempMountPointOutsideContainer.c_str()) != 0) { - AI_LOG_ERROR_EXIT("failed to clean up non persistent image"); - // This is probably to late to fail but do it either way + AI_LOG_ERROR_EXIT("failed to delete temp mount point @ '%s'", + mTempMountPointOutsideContainer.c_str()); return false; } } +#endif AI_LOG_FN_EXIT(); return true; @@ -626,88 +648,6 @@ std::vector Storage::getMountOwners() const return mountOwners; } -// ----------------------------------------------------------------------------- -/** - * @brief Create mount owner details vector from all mount owners in config. - * - * - * @return vector of MountOwnerDetails that were in the config - */ -std::vector> Storage::getMountTunnelDetails() const -{ - AI_LOG_FN_ENTRY(); - - const std::vector mountTunnels = getMountTunnel(); - std::vector> tunnelDetails; - - // loop though all the loop mounts for the given container and create individual - // LoopMountDetails objects for each - for (const MountTunnelProperties &properties : mountTunnels) - { - // Setup the user/group IDs - uid_t uid = 0; - gid_t gid = 0; - setupOwnerIds(uid, gid); - - // create the loop mount and make sure it was constructed - auto mountTunnel = std::make_unique(mRootfsPath, - properties, - uid, - gid, - mUtils); - - if (mountTunnel) - { - tunnelDetails.emplace_back(std::move(mountTunnel)); - } - } - - AI_LOG_FN_EXIT(); - return tunnelDetails; - -} -// ----------------------------------------------------------------------------- -/** - * @brief Reads container config and creates the MountTunnelProperties - * type object. - * - * - * @return MountTunnelProperties object that was in the config - */ -std::vector Storage::getMountTunnel() const -{ - AI_LOG_FN_ENTRY(); - - std::vector tunnelProp; - - // Check if container has mount data - if (mContainerConfig->rdk_plugins->storage->data) - { - for (size_t i = 0; i < mContainerConfig->rdk_plugins->storage->data->mounttunnel_len; i++) - { - auto mounttunnel = mContainerConfig->rdk_plugins->storage->data->mounttunnel[i]; - - MountTunnelProperties tunnel; - tunnel.source = std::string(mounttunnel->source); - tunnel.destination = std::string(mounttunnel->destination); - tunnel.mountFlags = mounttunnel->flags; - - for (size_t j = 0; j < mounttunnel->options_len; j++) - { - tunnel.mountOptions.push_back(std::string(mounttunnel->options[j])); - } - - tunnelProp.push_back(tunnel); - } - } - else - { - AI_LOG_ERROR("No storage data in config file"); - } - - AI_LOG_FN_EXIT(); - return tunnelProp; -} // ----------------------------------------------------------------------------- /** * @brief Gets userId and groupId diff --git a/rdkPlugins/Storage/source/Storage.h b/rdkPlugins/Storage/source/Storage.h index 1b13f9a9..5fff86d8 100644 --- a/rdkPlugins/Storage/source/Storage.h +++ b/rdkPlugins/Storage/source/Storage.h @@ -26,7 +26,6 @@ #include "LoopMountDetails.h" #include "DynamicMountDetails.h" #include "MountOwnerDetails.h" -#include "MountTunnelDetails.h" #include @@ -95,9 +94,6 @@ class Storage : public RdkPluginBase std::vector getMountOwners() const; std::vector> getMountOwnerDetails() const; - std::vector getMountTunnel() const; - std::vector> getMountTunnelDetails() const; - void setupOwnerIds(uid_t& uid, gid_t& gid) const; private: @@ -105,7 +101,10 @@ class Storage : public RdkPluginBase std::shared_ptr mContainerConfig; const std::string mRootfsPath; const std::shared_ptr mUtils; - +#ifdef USE_MOUNT_TUNNEL + std::string mMountPointInsideContainer; + std::string mTempMountPointOutsideContainer; +#endif uint32_t getMappedId(uint32_t id, rt_defs_id_mapping **mapping, size_t mapping_len) const; }; From 3dffded55087a51f6fb216346bac312f8dd145e4 Mon Sep 17 00:00:00 2001 From: Gurdal Oruklu Date: Fri, 25 Oct 2024 15:58:26 +0000 Subject: [PATCH 3/4] minor fixes in mount tunnel implementation of addmount() in DobbyManager Signed-off-by: Gurdal Oruklu --- daemon/lib/source/DobbyManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/lib/source/DobbyManager.cpp b/daemon/lib/source/DobbyManager.cpp index db1838bc..6097939a 100644 --- a/daemon/lib/source/DobbyManager.cpp +++ b/daemon/lib/source/DobbyManager.cpp @@ -1938,7 +1938,7 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st // mount the source dir on the temporary mount point outside the container // this is needed to move the mount inside the container namespace later - if(mount(source.c_str(), tempMountPointOutsideContainer.c_str(), nullptr, mountOptions, nullptr)) + if(mount(source.c_str(), tempMountPointOutsideContainer.c_str(), nullptr, mountOptions, mountData.data())) { AI_LOG_WARN("mount failed for %s errno: %d", destination.c_str(), errno); mUtilities->rmdirRecursive(tempMountPointOutsideContainer.c_str()); @@ -1946,7 +1946,7 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st return false; } - auto doMoveMountLambda = [containerUID, containerGID, tempMountPointInsideContainer, mountPointInsideContainer, mountOptions, mountData]() + auto doMoveMountLambda = [containerUID, containerGID, tempMountPointInsideContainer, mountPointInsideContainer]() { // switch to uid / gid of the host since we are still in the host user namespace if (syscall(SYS_setresgid, -1, containerGID, -1) != 0) @@ -1979,7 +1979,7 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st return false; } // move the mount from the temporary mount point inside the container to the final mount point - if(mount(tempMountPointInsideContainer.c_str(), mountPointInsideContainer.c_str(), nullptr, mountOptions | MS_MOVE, nullptr)) + if(mount(tempMountPointInsideContainer.c_str(), mountPointInsideContainer.c_str(), nullptr, MS_MOVE, nullptr)) { AI_LOG_WARN("mount failed for src %s, dest %s errno: %d", tempMountPointInsideContainer.c_str(), mountPointInsideContainer.c_str(), errno); return false; From a1969da49146dedb2be4ffa2dd8d3e47449f51a4 Mon Sep 17 00:00:00 2001 From: Gurdal Oruklu Date: Wed, 30 Oct 2024 00:16:13 +0000 Subject: [PATCH 4/4] added USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS compile option Signed-off-by: Gurdal Oruklu --- CMakeLists.txt | 11 ++++------- daemon/lib/source/DobbyManager.cpp | 4 ++-- rdkPlugins/Storage/source/Storage.cpp | 8 ++++---- rdkPlugins/Storage/source/Storage.h | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b7df530..194334e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,13 +165,10 @@ endif() find_package( breakpad QUIET ) -find_path(LINUXMOUNT NAMES "linux/mount.h") -if(NOT LINUXMOUNT) - message( "Couldn't find linux/mount.h. You may need to upgrade your kernel to 5.2 or later" ) - add_definitions( -DUSE_MOUNT_TUNNEL ) -else() - message("Found linux/mount.h") - add_definitions( -DHAVE_LINUX_MOUNT_H ) +option( USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS "use open_tree() syscall for dynamic mounts" OFF ) +if ( USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS ) + message("will use open_tree syscall for dynamic mounts") + add_definitions( -DUSE_OPEN_TREE_FOR_DYNAMIC_MOUNTS ) endif() # Run libocispec to generate OCI config parsers and necessary C headers diff --git a/daemon/lib/source/DobbyManager.cpp b/daemon/lib/source/DobbyManager.cpp index 6097939a..6f8f7225 100644 --- a/daemon/lib/source/DobbyManager.cpp +++ b/daemon/lib/source/DobbyManager.cpp @@ -62,7 +62,7 @@ #include #include -#ifdef HAVE_LINUX_MOUNT_H +#ifdef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS # include #endif @@ -1850,7 +1850,7 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st return false; } -#ifdef HAVE_LINUX_MOUNT_H +#ifdef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS int fdMnt = syscall(SYS_open_tree, -EBADF, source.c_str(), OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE); if (fdMnt < 0) { diff --git a/rdkPlugins/Storage/source/Storage.cpp b/rdkPlugins/Storage/source/Storage.cpp index a7d8dacd..376bddb0 100644 --- a/rdkPlugins/Storage/source/Storage.cpp +++ b/rdkPlugins/Storage/source/Storage.cpp @@ -46,7 +46,7 @@ Storage::Storage(std::shared_ptr &containerSpec, : mName("Storage"), mContainerConfig(containerSpec), mRootfsPath(rootfsPath), -#ifdef USE_MOUNT_TUNNEL +#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS mMountPointInsideContainer(rootfsPath + MOUNT_TUNNEL_CONTAINER_PATH), mTempMountPointOutsideContainer(MOUNT_TUNNEL_HOST_PATH), #endif @@ -95,7 +95,7 @@ bool Storage::preCreation() } } -#ifdef USE_MOUNT_TUNNEL +#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS // Create host directory for the mount tunnel if (!DobbyRdkPluginUtils::mkdirRecursive(mTempMountPointOutsideContainer, 0755)) { @@ -175,7 +175,7 @@ bool Storage::createContainer() { AI_LOG_FN_ENTRY(); -#ifdef USE_MOUNT_TUNNEL +#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS // create the mount tunnel, the mounts on the host will now be visible inside the container dynamically if (mount(mTempMountPointOutsideContainer.c_str(), mMountPointInsideContainer.c_str(), @@ -296,7 +296,7 @@ bool Storage::postStop() } } -#ifdef USE_MOUNT_TUNNEL +#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS // cleanup for the mount tunnel if (umount2(mTempMountPointOutsideContainer.c_str(), UMOUNT_NOFOLLOW) != 0) { diff --git a/rdkPlugins/Storage/source/Storage.h b/rdkPlugins/Storage/source/Storage.h index 5fff86d8..e35c34ae 100644 --- a/rdkPlugins/Storage/source/Storage.h +++ b/rdkPlugins/Storage/source/Storage.h @@ -101,7 +101,7 @@ class Storage : public RdkPluginBase std::shared_ptr mContainerConfig; const std::string mRootfsPath; const std::shared_ptr mUtils; -#ifdef USE_MOUNT_TUNNEL +#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS std::string mMountPointInsideContainer; std::string mTempMountPointOutsideContainer; #endif