diff options
author | Michael Meeks <michael.meeks@collabora.com> | 2021-01-04 11:49:58 +0000 |
---|---|---|
committer | Michael Meeks <michael.meeks@collabora.com> | 2021-01-04 15:05:36 +0000 |
commit | 317dffb8176926cb6891fa8ca9cb332050fb3a60 (patch) | |
tree | 3defda45026447c5586f8627e8919dd9a80c0b94 /kit | |
parent | updated .gitignore for docker (diff) | |
download | online-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.cpp | 2 | ||||
-rw-r--r-- | kit/Kit.cpp | 60 |
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)) |