forked from Mirrorlandia_minetest/minetest
Optimize fs::CopyFileContents on Linux and Windows
This commit is contained in:
parent
714c9361ea
commit
4259ac96ea
181
src/filesys.cpp
181
src/filesys.cpp
@ -33,6 +33,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <IFileArchive.h>
|
#include <IFileArchive.h>
|
||||||
#include <IFileSystem.h>
|
#include <IFileSystem.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#ifndef FICLONE
|
||||||
|
#define FICLONE _IOW(0x94, 9, int)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace fs
|
namespace fs
|
||||||
{
|
{
|
||||||
@ -216,6 +223,31 @@ std::string CreateTempFile()
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CopyFileContents(const std::string &source, const std::string &target)
|
||||||
|
{
|
||||||
|
BOOL ok = CopyFileEx(source.c_str(), target.c_str(), nullptr, nullptr,
|
||||||
|
nullptr, COPY_FILE_ALLOW_DECRYPTED_DESTINATION);
|
||||||
|
if (!ok) {
|
||||||
|
errorstream << "copying " << source << " to " << target
|
||||||
|
<< " failed: " << GetLastError() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// docs: "File attributes for the existing file are copied to the new file."
|
||||||
|
// This is not our intention so get rid of unwanted attributes:
|
||||||
|
DWORD attr = GetFileAttributes(target.c_str());
|
||||||
|
if (attr == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
errorstream << target << ": file disappeared after copy" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
attr &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
|
||||||
|
SetFileAttributes(target.c_str(), attr);
|
||||||
|
|
||||||
|
tracestream << "copied " << source << " to " << target
|
||||||
|
<< " using CopyFileEx" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
@ -414,6 +446,101 @@ std::string CreateTempFile()
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct FileDeleter {
|
||||||
|
void operator()(FILE *stream) {
|
||||||
|
fclose(stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unique_ptr<FILE, FileDeleter> FileUniquePtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyFileContents(const std::string &source, const std::string &target)
|
||||||
|
{
|
||||||
|
FileUniquePtr sourcefile, targetfile;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
// Try to clone using Copy-on-Write (CoW). This is instant but supported
|
||||||
|
// only by some filesystems.
|
||||||
|
|
||||||
|
int srcfd, tgtfd;
|
||||||
|
srcfd = open(source.c_str(), O_RDONLY);
|
||||||
|
if (srcfd == -1) {
|
||||||
|
errorstream << source << ": can't open for reading: "
|
||||||
|
<< strerror(errno) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tgtfd = open(target.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (tgtfd == -1) {
|
||||||
|
errorstream << target << ": can't open for writing: "
|
||||||
|
<< strerror(errno) << std::endl;
|
||||||
|
close(srcfd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(tgtfd, FICLONE, srcfd) == 0) {
|
||||||
|
tracestream << "copied " << source << " to " << target
|
||||||
|
<< " using FICLONE" << std::endl;
|
||||||
|
close(srcfd);
|
||||||
|
close(tgtfd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback to normal copy, but no need to reopen the files
|
||||||
|
sourcefile.reset(fdopen(srcfd, "rb"));
|
||||||
|
targetfile.reset(fdopen(tgtfd, "wb"));
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sourcefile.reset(fopen(source.c_str(), "rb"));
|
||||||
|
targetfile.reset(fopen(target.c_str(), "wb"));
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
|
||||||
|
if (!sourcefile) {
|
||||||
|
errorstream << source << ": can't open for reading: "
|
||||||
|
<< strerror(errno) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!targetfile) {
|
||||||
|
errorstream << target << ": can't open for writing: "
|
||||||
|
<< strerror(errno) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total = 0;
|
||||||
|
bool done = false;
|
||||||
|
char readbuffer[BUFSIZ];
|
||||||
|
while (!done) {
|
||||||
|
size_t readbytes = fread(readbuffer, 1,
|
||||||
|
sizeof(readbuffer), sourcefile.get());
|
||||||
|
total += readbytes;
|
||||||
|
if (ferror(sourcefile.get())) {
|
||||||
|
errorstream << source << ": IO error: "
|
||||||
|
<< strerror(errno) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (readbytes > 0)
|
||||||
|
fwrite(readbuffer, 1, readbytes, targetfile.get());
|
||||||
|
if (feof(sourcefile.get())) {
|
||||||
|
// flush destination file to catch write errors (e.g. disk full)
|
||||||
|
fflush(targetfile.get());
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
if (ferror(targetfile.get())) {
|
||||||
|
errorstream << target << ": IO error: "
|
||||||
|
<< strerror(errno) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tracestream << "copied " << total << " bytes from "
|
||||||
|
<< source << " to " << target << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/****************************
|
/****************************
|
||||||
@ -488,60 +615,6 @@ bool CreateAllDirs(const std::string &path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CopyFileContents(const std::string &source, const std::string &target)
|
|
||||||
{
|
|
||||||
FILE *sourcefile = fopen(source.c_str(), "rb");
|
|
||||||
if(sourcefile == NULL){
|
|
||||||
errorstream<<source<<": can't open for reading: "
|
|
||||||
<<strerror(errno)<<std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *targetfile = fopen(target.c_str(), "wb");
|
|
||||||
if(targetfile == NULL){
|
|
||||||
errorstream<<target<<": can't open for writing: "
|
|
||||||
<<strerror(errno)<<std::endl;
|
|
||||||
fclose(sourcefile);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t total = 0;
|
|
||||||
bool retval = true;
|
|
||||||
bool done = false;
|
|
||||||
char readbuffer[BUFSIZ];
|
|
||||||
while(!done){
|
|
||||||
size_t readbytes = fread(readbuffer, 1,
|
|
||||||
sizeof(readbuffer), sourcefile);
|
|
||||||
total += readbytes;
|
|
||||||
if(ferror(sourcefile)){
|
|
||||||
errorstream<<source<<": IO error: "
|
|
||||||
<<strerror(errno)<<std::endl;
|
|
||||||
retval = false;
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
if(readbytes > 0){
|
|
||||||
fwrite(readbuffer, 1, readbytes, targetfile);
|
|
||||||
}
|
|
||||||
if(feof(sourcefile) || ferror(sourcefile)){
|
|
||||||
// flush destination file to catch write errors
|
|
||||||
// (e.g. disk full)
|
|
||||||
fflush(targetfile);
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
if(ferror(targetfile)){
|
|
||||||
errorstream<<target<<": IO error: "
|
|
||||||
<<strerror(errno)<<std::endl;
|
|
||||||
retval = false;
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
infostream<<"copied "<<total<<" bytes from "
|
|
||||||
<<source<<" to "<<target<<std::endl;
|
|
||||||
fclose(sourcefile);
|
|
||||||
fclose(targetfile);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CopyDir(const std::string &source, const std::string &target)
|
bool CopyDir(const std::string &source, const std::string &target)
|
||||||
{
|
{
|
||||||
if(PathExists(source)){
|
if(PathExists(source)){
|
||||||
|
Loading…
Reference in New Issue
Block a user