summaryrefslogtreecommitdiffstats
path: root/kit
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@collabora.com>2021-01-04 11:49:58 +0000
committerMichael Meeks <michael.meeks@collabora.com>2021-01-04 15:05:36 +0000
commit317dffb8176926cb6891fa8ca9cb332050fb3a60 (patch)
tree3defda45026447c5586f8627e8919dd9a80c0b94 /kit
parentupdated .gitignore for docker (diff)
downloadonline-317dffb8176926cb6891fa8ca9cb332050fb3a60.tar.gz
online-317dffb8176926cb6891fa8ca9cb332050fb3a60.zip
Optimize copy of jails to hard-linking with new capability.
In some cases we cannot do a fast bind-mount of the files we want in our jail since we don't have cap_sys_admin for loolmount inside eg. docker. Thus we need to fallback to hard-linking, however various security systems namespace parts of our tree, such that link() fails with EXDEV even across the (apparently) same file-system. As such we need to assemble a copy of what we want to hard-link close to our jails. However, this needs to be owned by root / the system to avoid having writable files shared between jails. Hence we need cap_chown in addition to cap_fowner, to get ownership right and then hard-link. Change-Id: Iba0ef46ddbc1c03f3dc7177bc1ec1755624135db Signed-off-by: Michael Meeks <michael.meeks@collabora.com>
Diffstat (limited to 'kit')
-rw-r--r--kit/ForKit.cpp2
-rw-r--r--kit/Kit.cpp60
2 files changed, 59 insertions, 3 deletions
diff --git a/kit/ForKit.cpp b/kit/ForKit.cpp
index 1654c78049..dc46dd293b 100644
--- a/kit/ForKit.cpp
+++ b/kit/ForKit.cpp
@@ -253,6 +253,8 @@ static bool haveCorrectCapabilities()
result = false;
if (!haveCapability(CAP_FOWNER))
result = false;
+ if (!haveCapability(CAP_CHOWN))
+ result = false;
return result;
}
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 32b5266414..be44f28a34 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -150,6 +150,7 @@ namespace
LinkOrCopyType linkOrCopyType;
std::string sourceForLinkOrCopy;
Path destinationForLinkOrCopy;
+ std::string linkableForLinkOrCopy; // Place to stash copies that we can hard-link from
std::chrono::time_point<std::chrono::steady_clock> linkOrCopyStartTime;
bool linkOrCopyVerboseLogging = false;
unsigned linkOrCopyFileCount = 0; // Track to help quantify the link-or-copy performance.
@@ -247,7 +248,56 @@ namespace
if (linkOrCopyVerboseLogging)
LOG_INF("Linking file \"" << fpath << "\" to \"" << newPath << '"');
- if (!FileUtil::linkOrCopyFile(fpath, newPath.c_str()))
+ // first try a simple hard-link
+ if (link(fpath, newPath.c_str()) == 0)
+ return;
+
+ // incrementally build our 'linkable/' copy nearby
+ static bool canChown = true; // only if we can get permissions right
+ if (errno == EXDEV && canChown)
+ {
+ // then copy somewhere closer and hard link from there
+ LOG_TRC("link(\"" << fpath << "\", \"" << newPath << "\") failed: " << strerror(errno)
+ << ". Will try to link template.");
+
+ std::string linkableCopy = linkableForLinkOrCopy + fpath;
+ if (::link(linkableCopy.c_str(), newPath.c_str()) == 0)
+ return;
+
+ if (errno == ENOENT)
+ {
+ File(Path(linkableCopy).parent()).createDirectories();
+ if (!FileUtil::copy(fpath, linkableCopy.c_str(), /*log=*/false, /*throw_on_error=*/false))
+ LOG_TRC("Failed to create linkable copy [" << fpath << "] to [" << linkableCopy.c_str() << "]");
+ else {
+ // Match system permissions, so a file we can write is not shared across jails.
+ struct stat ownerInfo;
+ if (::stat(fpath, &ownerInfo) != 0 ||
+ ::chown(linkableCopy.c_str(), ownerInfo.st_uid, ownerInfo.st_gid) != 0)
+ {
+ LOG_WRN("Failed to stat or chown " << ownerInfo.st_uid << ":" << ownerInfo.st_gid <<
+ " " << linkableCopy << ": " << strerror(errno) << " missing cap_chown?, disabling linkable");
+ unlink(linkableCopy.c_str());
+ canChown = false;
+ }
+ else if (::link(linkableCopy.c_str(), newPath.c_str()) == 0)
+ return;
+ }
+ }
+ LOG_TRC("link(\"" << linkableCopy << "\", \"" << newPath << "\") failed: " << strerror(errno)
+ << ". Cannot create linkable copy.");
+ }
+
+ static bool warned = false;
+ if (!warned)
+ {
+ LOG_ERR("link(\"" << fpath << "\", \"" << newPath.c_str() << "\") failed: " << strerror(errno)
+ << ". Very slow copying path triggered.");
+ warned = true;
+ } else
+ LOG_TRC("link(\"" << fpath << "\", \"" << newPath.c_str() << "\") failed: " << strerror(errno)
+ << ". Will copy.");
+ if (!FileUtil::copy(fpath, newPath.c_str(), /*log=*/false, /*throw_on_error=*/false))
{
LOG_FTL("Failed to copy or link [" << fpath << "] to [" << newPath << "]. Exiting.");
Log::shutdown();
@@ -355,6 +405,7 @@ namespace
void linkOrCopy(std::string source,
const Path& destination,
+ std::string linkable,
LinkOrCopyType type)
{
std::string resolved = FileUtil::realpath(source);
@@ -373,6 +424,7 @@ namespace
if (sourceForLinkOrCopy.back() == '/')
sourceForLinkOrCopy.pop_back();
destinationForLinkOrCopy = destination;
+ linkableForLinkOrCopy = linkable;
linkOrCopyFileCount = 0;
linkOrCopyStartTime = std::chrono::steady_clock::now();
@@ -2251,9 +2303,11 @@ void lokit_main(
LOG_INF("Mounting is disabled, will link/copy " << sysTemplate << " -> "
<< jailPathStr);
- linkOrCopy(sysTemplate, jailPath, LinkOrCopyType::All);
+ const std::string linkablePath = childRoot + "/linkable";
+
+ linkOrCopy(sysTemplate, jailPath, linkablePath, LinkOrCopyType::All);
- linkOrCopy(loTemplate, loJailDestPath, LinkOrCopyType::LO);
+ linkOrCopy(loTemplate, loJailDestPath, linkablePath, LinkOrCopyType::LO);
// Update the dynamic files inside the jail.
if (!JailUtil::SysTemplate::updateDynamicFiles(jailPathStr))