copyFile: de-poco-ize and handle EINTR and short writes.

Change-Id: I2046881c786a9f31f45c53f282de9ddd9a9cebcf
This commit is contained in:
Michael Meeks
2020-01-18 15:56:01 +00:00
parent c187f414c5
commit 0599db3576
5 changed files with 82 additions and 19 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);