Skip to content

Commit

Permalink
Merge pull request #343 from goruklu/RDK-52502-mountForOldKernels
Browse files Browse the repository at this point in the history
[RDK-53519] added mount tunnel based addMount() implementation
  • Loading branch information
goruklu authored Oct 30, 2024
2 parents 59c8437 + a1969da commit b99ddcd
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 20 deletions.
10 changes: 4 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +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" )
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
Expand Down
5 changes: 2 additions & 3 deletions bundle/lib/source/templates/OciConfigJson1.0.2-dobby.template
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ static const char* ociJsonTemplate = R"JSON(
"readonly": true
},

"rootfsPropagation": "rprivate",

"hostname": "dobby",

"mounts": [
Expand Down Expand Up @@ -377,7 +375,8 @@ static const char* ociJsonTemplate = R"JSON(
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
],
"rootfsPropagation": "slave"
},
{{#ENABLE_LEGACY_PLUGINS}}
"legacyPlugins": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ static const char* ociJsonTemplate = R"JSON(
"readonly": true
},

"rootfsPropagation": "rprivate",

"hostname": "dobby",

"mounts": [
Expand Down Expand Up @@ -388,7 +386,8 @@ static const char* ociJsonTemplate = R"JSON(
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
],
"rootfsPropagation": "slave"
},
{{#ENABLE_LEGACY_PLUGINS}}
"legacyPlugins": {
Expand Down
113 changes: 107 additions & 6 deletions daemon/lib/source/DobbyManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
#include <thread>
#include <sys/syscall.h>

#ifdef HAVE_LINUX_MOUNT_H
#ifdef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS
# include <linux/mount.h>
#endif

Expand Down Expand Up @@ -1849,8 +1849,8 @@ bool DobbyManager::addMount(int32_t cd, const std::string &source, const std::st
AI_LOG_FN_EXIT();
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)
{
Expand Down Expand Up @@ -1924,9 +1924,110 @@ 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;
std::string mountPointInsideContainer = destination;
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
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
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());
AI_LOG_FN_EXIT();
return false;
}

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)
{
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, 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
}

Expand Down
5 changes: 5 additions & 0 deletions pluginLauncher/lib/include/DobbyRdkPluginManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ class DobbyRdkPluginManager
// This is public as RDKPluginManager isn't responsible for handling logging
std::shared_ptr<IDobbyRdkLoggingPlugin> getContainerLogger() const;
void setExitStatus(int status) const;

std::shared_ptr<rt_dobby_schema> getContainerConfig() const
{
return mContainerConfig;
};

private:
bool loadPlugins();
Expand Down
3 changes: 3 additions & 0 deletions pluginLauncher/lib/include/DobbyRdkPluginUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions rdkPlugins/Storage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ It will mount "source" into container "destination" only if the source exists on
}
}
```
### 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.
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 and this code will be disabled at build time.


### Mount Owners
Add the following section to your OCI runtime configuration `config.json` file to configure mount ownership.
Expand Down
70 changes: 69 additions & 1 deletion rdkPlugins/Storage/source/Storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <errno.h>
#include <string>
#include <memory>
#include <sys/stat.h>

/**
* Need to do this at the start of every plugin to make sure the correct
Expand All @@ -45,6 +46,10 @@ Storage::Storage(std::shared_ptr<rt_dobby_schema> &containerSpec,
: mName("Storage"),
mContainerConfig(containerSpec),
mRootfsPath(rootfsPath),
#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS
mMountPointInsideContainer(rootfsPath + MOUNT_TUNNEL_CONTAINER_PATH),
mTempMountPointOutsideContainer(MOUNT_TUNNEL_HOST_PATH),
#endif
mUtils(utils)
{
AI_LOG_FN_ENTRY();
Expand Down Expand Up @@ -89,6 +94,28 @@ bool Storage::preCreation()
return false;
}
}

#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS
// Create host directory for the mount tunnel
if (!DobbyRdkPluginUtils::mkdirRecursive(mTempMountPointOutsideContainer, 0755))
{
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;
}
Expand All @@ -107,7 +134,7 @@ 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;
}
}
Expand Down Expand Up @@ -148,6 +175,25 @@ bool Storage::createContainer()
{
AI_LOG_FN_ENTRY();

#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(),
"", MS_BIND, nullptr) != 0)
{
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<std::unique_ptr<LoopMountDetails>> loopMountDetails = getLoopMountDetails();
for(auto it = loopMountDetails.begin(); it != loopMountDetails.end(); it++)
Expand Down Expand Up @@ -250,6 +296,28 @@ bool Storage::postStop()
}
}

#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS
// cleanup for the mount tunnel
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_ERROR_EXIT("failed to delete temp mount point @ '%s'",
mTempMountPointOutsideContainer.c_str());
return false;
}
}
#endif

AI_LOG_FN_EXIT();
return true;
}
Expand Down
5 changes: 4 additions & 1 deletion rdkPlugins/Storage/source/Storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ class Storage : public RdkPluginBase
std::shared_ptr<rt_dobby_schema> mContainerConfig;
const std::string mRootfsPath;
const std::shared_ptr<DobbyRdkPluginUtils> mUtils;

#ifndef USE_OPEN_TREE_FOR_DYNAMIC_MOUNTS
std::string mMountPointInsideContainer;
std::string mTempMountPointOutsideContainer;
#endif
uint32_t getMappedId(uint32_t id, rt_defs_id_mapping **mapping, size_t mapping_len) const;
};

Expand Down

0 comments on commit b99ddcd

Please sign in to comment.