summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@collabora.com>2020-01-18 15:56:01 +0000
committerMichael Meeks <michael.meeks@collabora.com>2020-01-21 13:39:42 +0000
commit1c6b4be2ce84e43a4646c52d8376c19af53d945c (patch)
treefe23b3875e8916382874d4f6fb844ecf24fcd786
parentaadf5af77b74822ebdc52e512b0b856957dc0896 (diff)
copyFile: de-poco-ize and handle EINTR and short writes.
Change-Id: I2046881c786a9f31f45c53f282de9ddd9a9cebcf
-rw-r--r--common/FileUtil.cpp76
-rw-r--r--common/FileUtil.hpp3
-rwxr-xr-xtest/run_unit.sh.in10
-rw-r--r--wsd/Storage.cpp8
-rw-r--r--wsd/TraceFile.hpp4
5 files changed, 82 insertions, 19 deletions
diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp
index 5066d2938..84f4085a1 100644
--- a/common/FileUtil.cpp
+++ b/common/FileUtil.cpp
@@ -88,6 +88,80 @@ namespace FileUtil
return name;
}
+ void copyFileTo(const std::string &fromPath, const std::string &toPath)
+ {
+ int from = -1, to = -1;
+ try {
+ from = open(fromPath.c_str(), O_RDONLY);
+ if (from < 0)
+ {
+ LOG_SYS("Failed to open src " << anonymizeUrl(fromPath));
+ throw;
+ }
+
+ struct stat st;
+ if (fstat(from, &st) != 0)
+ {
+ LOG_SYS("Failed to fstat src " << anonymizeUrl(fromPath));
+ throw;
+ }
+
+ 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;
+ }
+
+ LOG_INF("Copying " << st.st_size << " bytes from " << anonymizeUrl(fromPath) << " to " << anonymizeUrl(toPath));
+
+ char buffer[64 * 1024];
+
+ int n;
+ off_t bytesIn = 0;
+ 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;
+ }
+ bytesIn += n;
+ if (n == 0) // EOF
+ break;
+ assert (off_t(sizeof (buffer)) >= n);
+ // Handle short writes and EINTR
+ for (int j = 0; j < n;)
+ {
+ int written;
+ while ((written = ::write(to, buffer + j, n - j)) < 0 && errno == EINTR)
+ 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;
+ }
+ j += written;
+ }
+ } while(true);
+ if (bytesIn != st.st_size)
+ {
+ LOG_WRN("Unusual: file " << anonymizeUrl(fromPath) << " changed size "
+ "during copy from " << st.st_size << " to " << bytesIn);
+ }
+ }
+ catch (...)
+ {
+ LOG_SYS("Failed to copy from " << anonymizeUrl(fromPath) << " to " << anonymizeUrl(toPath));
+ close(from);
+ close(to);
+ unlink(toPath.c_str());
+ throw Poco::Exception("failed to copy");
+ }
+ }
+
std::string getTempFilePath(const std::string& srcDir, const std::string& srcFilename, const std::string& dstFilenamePrefix)
{
const std::string srcPath = srcDir + '/' + srcFilename;
@@ -100,7 +174,7 @@ namespace FileUtil
fileDeleter.registerForDeletion(dstPath);
#else
const std::string dstPath = Poco::Path(Poco::Path::temp(), dstFilename).toString();
- Poco::File(srcPath).copyTo(dstPath);
+ copyFileTo(srcPath, dstPath);
Poco::TemporaryFile::registerForDeletion(dstPath);
#endif
diff --git a/common/FileUtil.hpp b/common/FileUtil.hpp
index a57aa5414..864188308 100644
--- a/common/FileUtil.hpp
+++ b/common/FileUtil.hpp
@@ -80,6 +80,9 @@ namespace FileUtil
removeFile(path.toString(), recursive);
}
+ /// Copy a file from @fromPath to @toPath, throws on failure.
+ void copyFileTo(const std::string &fromPath, const std::string &toPath);
+
/// Make a temp copy of a file, and prepend it with a prefix.
std::string getTempFilePath(const std::string& srcDir, const std::string& srcFilename,
const std::string& dstFilenamePrefix);
diff --git a/test/run_unit.sh.in b/test/run_unit.sh.in
index d05e2f48f..5fb8a6936 100755
--- a/test/run_unit.sh.in
+++ b/test/run_unit.sh.in
@@ -56,16 +56,6 @@ echo " $cmd_line"
# drop .la suffix
tst=`echo $tst | sed "s/\.la//"`;
-if test "z$tst" != "z" && test "z$CPPUNIT_TEST_NAME" != "z"; then
- # $tst is not empty, but $CPPUNIT_TEST_NAME is set, exit early if they
- # don't match.
- if test "z$tst" != "z$CPPUNIT_TEST_NAME"; then
- touch $tst_log
- echo ":test-result: SKIP $tst (disabled by CPPUNIT_TEST_NAME)" > $test_output
- exit 0;
- fi
-fi
-
export LOOL_LOGLEVEL=trace
if test "z$enable_debug" != "ztrue"; then
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 0be7c7c7b..8c8a8ddd8 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -331,8 +331,7 @@ std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/,
// Fallback to copying.
if (!Poco::File(getRootFilePath()).exists())
{
- LOG_INF("Copying " << LOOLWSD::anonymizeUrl(publicFilePath) << " to " << getRootFilePathAnonym());
- Poco::File(publicFilePath).copyTo(getRootFilePath());
+ FileUtil::copyFileTo(publicFilePath, getRootFilePath());
_isCopy = true;
}
}
@@ -371,10 +370,7 @@ StorageBase::SaveResult LocalStorage::saveLocalFileToStorage(const Authorization
LOG_TRC("Saving local file to local file storage (isCopy: " << _isCopy << ") for " << getRootFilePathAnonym());
// Copy the file back.
if (_isCopy && Poco::File(getRootFilePath()).exists())
- {
- LOG_INF("Copying " << getRootFilePathAnonym() << " to " << LOOLWSD::anonymizeUrl(getUri().getPath()));
- Poco::File(getRootFilePath()).copyTo(getUri().getPath());
- }
+ FileUtil::copyFileTo(getRootFilePath(), getUri().getPath());
// update its fileinfo object. This is used later to check if someone else changed the
// document while we are/were editing it
diff --git a/wsd/TraceFile.hpp b/wsd/TraceFile.hpp
index 82f5a75ce..e95fbb2e2 100644
--- a/wsd/TraceFile.hpp
+++ b/wsd/TraceFile.hpp
@@ -25,6 +25,7 @@
#include "Protocol.hpp"
#include "Log.hpp"
#include "Util.hpp"
+#include "FileUtil.hpp"
/// Dumps commands and notification trace.
class TraceFileRecord
@@ -141,8 +142,7 @@ public:
filename += '.' + origPath.getExtension();
snapshot = Poco::Path(_path, filename).toString();
- LOG_TRC("TraceFile: Copying local file [" << localPath << "] to snapshot [" << snapshot << "].");
- Poco::File(localPath).copyTo(snapshot);
+ FileUtil::copyFileTo(localPath, snapshot);
snapshot = Poco::URI(Poco::URI("file://"), snapshot).toString();
LOG_TRC("TraceFile: Mapped URL " << url << " to " << snapshot);