wsd: support read-only systemplate
For various reasons, systemplate may be read-only or under a different owner and therefore impossible to update the dynamic files in it. To support such a scenario, we first link the eight dynamic files in /etc when creating systemplate. If this fails, we copy the files. When creating jails, we always check that all the dynamic files are up-to-date. If they are, nothing further is necessary and we bind-mount, if enabled and possible. However, if the dynamic files are not up-to-date, we disable bind-mounting and force linking the files in the jails. Failing that, we copy them, which is not ideal, but allows us to ensure the dynamic files are up-to-date as we copy them too. Ideally, the dynamic files in question would be hard-link (or at least soft-linked) in systemplate at creation. From then on we would bind-mount the jails and everything would work perfectly and no files would need updating. This patch is fallback for when this scheme fails, which should be exceedingly rare anyway, but which still ensures correct operation. Change-Id: I09c6f057c49396579aaddb1b8bf4af0930dd4247 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/100834 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Tested-by: Jenkins Reviewed-by: Andras Timar <andras.timar@collabora.com>feature/calc-canvas
parent
ad031401b5
commit
bc8da0cb33
|
@ -12,7 +12,9 @@
|
|||
#include "FileUtil.hpp"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <exception>
|
||||
#include <ftw.h>
|
||||
#include <stdexcept>
|
||||
#include <sys/time.h>
|
||||
#ifdef __linux
|
||||
#include <sys/vfs.h>
|
||||
|
@ -89,27 +91,19 @@ namespace FileUtil
|
|||
bool copy(const std::string& fromPath, const std::string& toPath, bool log, bool throw_on_error)
|
||||
{
|
||||
int from = -1, to = -1;
|
||||
try {
|
||||
try
|
||||
{
|
||||
from = open(fromPath.c_str(), O_RDONLY);
|
||||
if (from < 0)
|
||||
{
|
||||
LOG_SYS("Failed to open src " << anonymizeUrl(fromPath));
|
||||
throw;
|
||||
}
|
||||
throw std::runtime_error("Failed to open src " + anonymizeUrl(fromPath));
|
||||
|
||||
struct stat st;
|
||||
if (fstat(from, &st) != 0)
|
||||
{
|
||||
LOG_SYS("Failed to fstat src " << anonymizeUrl(fromPath));
|
||||
throw;
|
||||
}
|
||||
throw std::runtime_error("Failed to fstat src " + anonymizeUrl(fromPath));
|
||||
|
||||
to = open(toPath.c_str(), O_CREAT | O_TRUNC | O_WRONLY, st.st_mode);
|
||||
if (to < 0)
|
||||
{
|
||||
LOG_SYS("Failed to fstat dest " << anonymizeUrl(toPath));
|
||||
throw;
|
||||
}
|
||||
throw std::runtime_error("Failed to open dest " + anonymizeUrl(toPath));
|
||||
|
||||
// Logging may be redundant and/or noisy.
|
||||
if (log)
|
||||
|
@ -120,14 +114,14 @@ namespace FileUtil
|
|||
|
||||
int n;
|
||||
off_t bytesIn = 0;
|
||||
do {
|
||||
do
|
||||
{
|
||||
while ((n = ::read(from, buffer, sizeof(buffer))) < 0 && errno == EINTR)
|
||||
LOG_TRC("EINTR reading from " << anonymizeUrl(fromPath));
|
||||
if (n < 0)
|
||||
{
|
||||
LOG_SYS("Failed to read from " << anonymizeUrl(fromPath) << " at " << bytesIn << " bytes in");
|
||||
throw;
|
||||
}
|
||||
throw std::runtime_error("Failed to read from " + anonymizeUrl(fromPath)
|
||||
+ " at " + std::to_string(bytesIn) + " bytes in");
|
||||
|
||||
bytesIn += n;
|
||||
if (n == 0) // EOF
|
||||
break;
|
||||
|
@ -140,13 +134,14 @@ namespace FileUtil
|
|||
LOG_TRC("EINTR writing to " << anonymizeUrl(toPath));
|
||||
if (written < 0)
|
||||
{
|
||||
LOG_SYS("Failed to write " << n << " bytes to " << anonymizeUrl(toPath) << " at " <<
|
||||
bytesIn << " bytes into " << anonymizeUrl(fromPath));
|
||||
throw;
|
||||
throw std::runtime_error("Failed to write " + std::to_string(n)
|
||||
+ " bytes to " + anonymizeUrl(toPath) + " at "
|
||||
+ std::to_string(bytesIn) + " bytes into "
|
||||
+ anonymizeUrl(fromPath));
|
||||
}
|
||||
j += written;
|
||||
}
|
||||
} while(true);
|
||||
} while (true);
|
||||
if (bytesIn != st.st_size)
|
||||
{
|
||||
LOG_WRN("Unusual: file " << anonymizeUrl(fromPath) << " changed size "
|
||||
|
@ -156,19 +151,18 @@ namespace FileUtil
|
|||
close(to);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Failed to copy from " << anonymizeUrl(fromPath) << " to "
|
||||
<< anonymizeUrl(toPath);
|
||||
oss << "Error while copying from " << anonymizeUrl(fromPath) << " to "
|
||||
<< anonymizeUrl(toPath) << ": " << ex.what();
|
||||
const std::string err = oss.str();
|
||||
|
||||
LOG_SYS(err);
|
||||
close(from);
|
||||
close(to);
|
||||
unlink(toPath.c_str());
|
||||
if (throw_on_error)
|
||||
throw Poco::Exception(err);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -247,17 +247,38 @@ namespace SysTemplate
|
|||
/// that systemplate will get re-generated after installation.
|
||||
static const auto DynamicFilePaths
|
||||
= { "/etc/passwd", "/etc/group", "/etc/host.conf", "/etc/hosts",
|
||||
"/etc/nsswitch.conf", "/etc/resolv.conf", "etc/timezone", "etc/localtime" };
|
||||
"/etc/nsswitch.conf", "/etc/resolv.conf", "/etc/timezone", "/etc/localtime" };
|
||||
|
||||
/// Copy (false) by default for KIT_IN_PROCESS.
|
||||
static bool LinkDynamicFiles = false;
|
||||
|
||||
static bool updateDynamicFilesImpl(const std::string& sysTemplate);
|
||||
|
||||
void setupDynamicFiles(const std::string& sysTemplate)
|
||||
{
|
||||
LOG_INF("Setting up systemplate dynamic files in [" << sysTemplate << "].");
|
||||
|
||||
const std::string etcSysTemplatePath = Poco::Path(sysTemplate, "etc").toString();
|
||||
LinkDynamicFiles = true; // Prefer linking, unless it fails.
|
||||
|
||||
if (!updateDynamicFilesImpl(sysTemplate))
|
||||
{
|
||||
// Can't copy!
|
||||
LOG_WRN("Failed to update the dynamic files in ["
|
||||
<< sysTemplate
|
||||
<< "]. Will disable bind-mounting in this run and clone systemplate into the "
|
||||
"jails, which is more resource intensive.");
|
||||
unsetenv("LOOL_BIND_MOUNT"); // We can't mount from incomplete systemplate.
|
||||
}
|
||||
|
||||
if (LinkDynamicFiles)
|
||||
LOG_INF("Systemplate dynamic files in ["
|
||||
<< sysTemplate << "] are linked and will remain up-to-date.");
|
||||
}
|
||||
|
||||
bool updateDynamicFilesImpl(const std::string& sysTemplate)
|
||||
{
|
||||
LOG_INF("Updating systemplate dynamic files in [" << sysTemplate << "].");
|
||||
for (const auto& srcFilename : DynamicFilePaths)
|
||||
{
|
||||
const Poco::File srcFilePath(srcFilename);
|
||||
|
@ -275,6 +296,7 @@ void setupDynamicFiles(const std::string& sysTemplate)
|
|||
continue;
|
||||
}
|
||||
|
||||
LOG_INF("File [" << dstFilename << "] needs to be updated.");
|
||||
if (LinkDynamicFiles)
|
||||
{
|
||||
LOG_INF("Linking [" << srcFilename << "] -> [" << dstFilename << "].");
|
||||
|
@ -290,8 +312,12 @@ void setupDynamicFiles(const std::string& sysTemplate)
|
|||
const int linkerr = errno;
|
||||
|
||||
// With parallel tests, another test might have linked already.
|
||||
if (Poco::File(dstFilename).exists()) // stat again.
|
||||
FileUtil::Stat dstStat2(dstFilename);
|
||||
if (dstStat2.isUpToDate(srcStat))
|
||||
{
|
||||
LOG_INF("File [" << dstFilename << "] now seems to be up-to-date.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Failed to link a file. Disable linking and copy instead.
|
||||
LOG_WRN("Failed to link ["
|
||||
|
@ -301,55 +327,27 @@ void setupDynamicFiles(const std::string& sysTemplate)
|
|||
}
|
||||
|
||||
// Linking failed, just copy.
|
||||
LOG_INF("Copying [" << srcFilename << "] -> [" << dstFilename << "].");
|
||||
if (!FileUtil::copyAtomic(srcFilename, dstFilename, true))
|
||||
if (!LinkDynamicFiles)
|
||||
{
|
||||
if (!Poco::File(dstFilename).exists()) // stat again.
|
||||
LOG_ERR("Failed to copy [" << srcFilename << "] -> [" << dstFilename
|
||||
<< "], some functionality may be missing.");
|
||||
}
|
||||
}
|
||||
|
||||
if (LinkDynamicFiles)
|
||||
LOG_INF("Successfully linked the systemplate dynamic files in ["
|
||||
<< sysTemplate << "] and will not need to update them again.");
|
||||
}
|
||||
|
||||
void updateDynamicFiles(const std::string& sysTemplate)
|
||||
{
|
||||
// If the files are linked, they are always up-to-date.
|
||||
if (!LinkDynamicFiles)
|
||||
{
|
||||
LOG_INF("Updating systemplate dynamic files in [" << sysTemplate << "].");
|
||||
|
||||
const std::string etcSysTemplatePath = Poco::Path(sysTemplate, "etc").toString();
|
||||
for (const auto& srcFilename : DynamicFilePaths)
|
||||
{
|
||||
const Poco::File srcFilePath(srcFilename);
|
||||
FileUtil::Stat srcStat(srcFilename);
|
||||
if (!srcStat.exists())
|
||||
continue;
|
||||
|
||||
const std::string dstFilename = Poco::Path(sysTemplate, srcFilename).toString();
|
||||
FileUtil::Stat dstStat(dstFilename);
|
||||
|
||||
// Is it outdated?
|
||||
if (dstStat.isUpToDate(srcStat))
|
||||
LOG_INF("Copying [" << srcFilename << "] -> [" << dstFilename << "].");
|
||||
if (!FileUtil::copyAtomic(srcFilename, dstFilename, true))
|
||||
{
|
||||
LOG_INF("File [" << dstFilename << "] is already up-to-date.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INF("Copying [" << srcFilename << "] -> [" << dstFilename << "].");
|
||||
if (!FileUtil::copyAtomic(srcFilename, dstFilename, true))
|
||||
FileUtil::Stat dstStat2(dstFilename); // Stat again.
|
||||
if (!dstStat2.isUpToDate(srcStat))
|
||||
{
|
||||
if (!Poco::File(dstFilename).exists()) // stat again.
|
||||
LOG_ERR("Failed to copy [" << srcFilename << "] -> [" << dstFilename
|
||||
<< "], some functionality may be missing.");
|
||||
return false; // No point in trying the remaining files.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateDynamicFiles(const std::string& sysTemplate)
|
||||
{
|
||||
// If the files are linked, they are always up-to-date.
|
||||
return LinkDynamicFiles ? true : updateDynamicFilesImpl(sysTemplate);
|
||||
}
|
||||
|
||||
void setupLoSymlink(const std::string& sysTemplate, const std::string& loTemplate,
|
||||
|
|
|
@ -61,7 +61,8 @@ void setupRandomDeviceLinks(const std::string& root);
|
|||
void setupDynamicFiles(const std::string& sysTemplate);
|
||||
|
||||
/// Update the dynamic files within the sysTemplate before each child fork.
|
||||
void updateDynamicFiles(const std::string& sysTemplate);
|
||||
/// Returns false on failure.
|
||||
bool updateDynamicFiles(const std::string& sysTemplate);
|
||||
|
||||
} // namespace SysTemplate
|
||||
|
||||
|
|
15
kit/Kit.cpp
15
kit/Kit.cpp
|
@ -2135,10 +2135,10 @@ void lokit_main(
|
|||
if (std::getenv("LOOL_BIND_MOUNT"))
|
||||
{
|
||||
const std::string destPath = jailPath.toString();
|
||||
LOG_DBG("Mounting " << sysTemplate << " -> " << destPath);
|
||||
LOG_INF("Mounting " << sysTemplate << " -> " << destPath);
|
||||
if (!JailUtil::bind(sysTemplate, destPath))
|
||||
{
|
||||
LOG_INF("Failed to mount [" << sysTemplate << "] -> [" << destPath
|
||||
LOG_WRN("Failed to mount [" << sysTemplate << "] -> [" << destPath
|
||||
<< "], will link/copy contents.");
|
||||
linkOrCopy(sysTemplate, destPath, LinkOrCopyType::All);
|
||||
}
|
||||
|
@ -2147,11 +2147,11 @@ void lokit_main(
|
|||
|
||||
// Mount lotemplate inside it.
|
||||
const std::string loDestPath = Poco::Path(jailPath, "lo").toString();
|
||||
LOG_DBG("Mounting " << loTemplate << " -> " << loDestPath);
|
||||
LOG_INF("Mounting " << loTemplate << " -> " << loDestPath);
|
||||
Poco::File(loDestPath).createDirectories();
|
||||
if (!JailUtil::bind(loTemplate, loDestPath))
|
||||
{
|
||||
LOG_INF("Failed to mount [" << loTemplate << "] -> [" << loDestPath
|
||||
LOG_WRN("Failed to mount [" << loTemplate << "] -> [" << loDestPath
|
||||
<< "], will link/copy contents.");
|
||||
linkOrCopy(sysTemplate, loDestPath, LinkOrCopyType::LO);
|
||||
}
|
||||
|
@ -2180,6 +2180,13 @@ void lokit_main(
|
|||
linkOrCopy(loTemplate, jailLOInstallation, LinkOrCopyType::LO);
|
||||
|
||||
JailUtil::setupJailDevNodes(Poco::Path(jailPath, "/tmp").toString());
|
||||
|
||||
// Update the dynamic files inside the jail.
|
||||
if (!JailUtil::SysTemplate::updateDynamicFiles(jailPath.toString()))
|
||||
{
|
||||
LOG_WRN("Failed to update the dynamic files in the jail ["
|
||||
<< jailPath.toString() << "]. Some functionality may be missing.");
|
||||
}
|
||||
}
|
||||
|
||||
::setenv("TMPDIR", "/tmp", 1);
|
||||
|
|
|
@ -58,13 +58,18 @@ grep -v dynamic | cut -d " " -f 3 | grep -E '^(/lib|/usr)' | sort -u | sed -e 's
|
|||
# This will now copy the file a symlink points to, but whatever.
|
||||
cpio -p -d -L $CHROOT
|
||||
|
||||
# Remove the dynamic files, which are linked by loolwsd.
|
||||
rm -f $CHROOT/etc/{hosts,nsswitch.conf,resolv.conf,passwd,group,host.conf,timezone,localtime}
|
||||
|
||||
mkdir -p $CHROOT/lo
|
||||
mkdir -p $CHROOT/dev
|
||||
mkdir -p $CHROOT/tmp/dev
|
||||
|
||||
# Link the dynamic files, replacing any existing.
|
||||
for file in hosts nsswitch.conf resolv.conf passwd group host.conf timezone localtime
|
||||
do
|
||||
#echo "Linking/Copying /etc/$file"
|
||||
# Prefer hard linking, fallback to soft linking, and finally to just copying.
|
||||
ln -f /etc/$file $CHROOT/etc/ 2> /dev/null || ln -f -s /etc/$file $CHROOT/etc/ || cp /etc/$file $CHROOT/etc/ || echo "Failed to link or copy $file"
|
||||
done
|
||||
|
||||
# /usr/share/fonts needs to be taken care of separately because the
|
||||
# directory time stamps must be preserved for fontconfig to trust
|
||||
# its cache.
|
||||
|
|
Loading…
Reference in New Issue