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 <IFileSystem.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifndef FICLONE
|
||||
#define FICLONE _IOW(0x94, 9, int)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
{
|
||||
@ -216,6 +223,31 @@ std::string CreateTempFile()
|
||||
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
|
||||
|
||||
/*********
|
||||
@ -414,6 +446,101 @@ std::string CreateTempFile()
|
||||
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
|
||||
|
||||
/****************************
|
||||
@ -488,60 +615,6 @@ bool CreateAllDirs(const std::string &path)
|
||||
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)
|
||||
{
|
||||
if(PathExists(source)){
|
||||
|
Loading…
Reference in New Issue
Block a user