diff options
author | Michael Meeks <michael.meeks@collabora.com> | 2020-01-18 15:56:01 +0000 |
---|---|---|
committer | Michael Meeks <michael.meeks@collabora.com> | 2020-01-21 13:39:42 +0000 |
commit | 1c6b4be2ce84e43a4646c52d8376c19af53d945c (patch) | |
tree | fe23b3875e8916382874d4f6fb844ecf24fcd786 | |
parent | aadf5af77b74822ebdc52e512b0b856957dc0896 (diff) |
copyFile: de-poco-ize and handle EINTR and short writes.
Change-Id: I2046881c786a9f31f45c53f282de9ddd9a9cebcf
-rw-r--r-- | common/FileUtil.cpp | 76 | ||||
-rw-r--r-- | common/FileUtil.hpp | 3 | ||||
-rwxr-xr-x | test/run_unit.sh.in | 10 | ||||
-rw-r--r-- | wsd/Storage.cpp | 8 | ||||
-rw-r--r-- | wsd/TraceFile.hpp | 4 |
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); |