Commit 8ca8daeb authored by Kitware Robot's avatar Kitware Robot Committed by Brad King

KWSys 2018-12-12 (abb8c680)

Code extracted from:

    https://gitlab.kitware.com/utils/kwsys.git

at commit abb8c680927708b161f87fb596cefb454ef5e97d (master).

Upstream Shortlog
-----------------

Gregor Jasny (1):
      228c60b8 SystemTools: CopyFileAlways: try to create a cheap CoW clone first
parent cbc772b8
......@@ -82,6 +82,10 @@
# include <signal.h> /* sigprocmask */
#endif
#ifdef __linux
# include <linux/fs.h>
#endif
// Windows API.
#if defined(_WIN32)
# include <windows.h>
......@@ -2157,6 +2161,120 @@ bool SystemTools::FilesDiffer(const std::string& source,
return false;
}
/**
* Blockwise copy source to destination file
*/
static bool CopyFileContentBlockwise(const std::string& source,
const std::string& destination)
{
// Open files
#if defined(_WIN32)
kwsys::ifstream fin(
Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)).c_str(),
std::ios::in | std::ios::binary);
#else
kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
#endif
if (!fin) {
return false;
}
// try and remove the destination file so that read only destination files
// can be written to.
// If the remove fails continue so that files in read only directories
// that do not allow file removal can be modified.
SystemTools::RemoveFile(destination);
#if defined(_WIN32)
kwsys::ofstream fout(
Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(destination)).c_str(),
std::ios::out | std::ios::trunc | std::ios::binary);
#else
kwsys::ofstream fout(destination.c_str(),
std::ios::out | std::ios::trunc | std::ios::binary);
#endif
if (!fout) {
return false;
}
// This copy loop is very sensitive on certain platforms with
// slightly broken stream libraries (like HPUX). Normally, it is
// incorrect to not check the error condition on the fin.read()
// before using the data, but the fin.gcount() will be zero if an
// error occurred. Therefore, the loop should be safe everywhere.
while (fin) {
const int bufferSize = 4096;
char buffer[bufferSize];
fin.read(buffer, bufferSize);
if (fin.gcount()) {
fout.write(buffer, fin.gcount());
} else {
break;
}
}
// Make sure the operating system has finished writing the file
// before closing it. This will ensure the file is finished before
// the check below.
fout.flush();
fin.close();
fout.close();
if (!fout) {
return false;
}
return true;
}
/**
* Clone the source file to the destination file
*
* If available, the Linux FICLONE ioctl is used to create a check
* copy-on-write clone of the source file.
*
* The method returns false for the following cases:
* - The code has not been compiled on Linux or the ioctl was unknown
* - The source and destination is on different file systems
* - The underlying filesystem does not support file cloning
* - An unspecified error occurred
*/
static bool CloneFileContent(const std::string& source,
const std::string& destination)
{
#if defined(__linux) && defined(FICLONE)
int in = open(source.c_str(), O_RDONLY);
if (in < 0) {
return false;
}
SystemTools::RemoveFile(destination);
int out =
open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (out < 0) {
close(in);
return false;
}
int result = ioctl(out, FICLONE, in);
close(in);
close(out);
if (result < 0) {
return false;
}
return true;
#else
(void)source;
(void)destination;
return false;
#endif
}
/**
* Copy a file named by "source" to the file named by "destination".
*/
......@@ -2174,9 +2292,6 @@ bool SystemTools::CopyFileAlways(const std::string& source,
if (SystemTools::FileIsDirectory(source)) {
SystemTools::MakeDirectory(destination);
} else {
const int bufferSize = 4096;
char buffer[bufferSize];
// If destination is a directory, try to create a file with the same
// name as the source in that directory.
......@@ -2195,62 +2310,12 @@ bool SystemTools::CopyFileAlways(const std::string& source,
SystemTools::MakeDirectory(destination_dir);
// Open files
#if defined(_WIN32)
kwsys::ifstream fin(
Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)).c_str(),
std::ios::in | std::ios::binary);
#else
kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
#endif
if (!fin) {
return false;
}
// try and remove the destination file so that read only destination files
// can be written to.
// If the remove fails continue so that files in read only directories
// that do not allow file removal can be modified.
SystemTools::RemoveFile(real_destination);
#if defined(_WIN32)
kwsys::ofstream fout(
Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(real_destination))
.c_str(),
std::ios::out | std::ios::trunc | std::ios::binary);
#else
kwsys::ofstream fout(real_destination.c_str(),
std::ios::out | std::ios::trunc | std::ios::binary);
#endif
if (!fout) {
return false;
}
// This copy loop is very sensitive on certain platforms with
// slightly broken stream libraries (like HPUX). Normally, it is
// incorrect to not check the error condition on the fin.read()
// before using the data, but the fin.gcount() will be zero if an
// error occurred. Therefore, the loop should be safe everywhere.
while (fin) {
fin.read(buffer, bufferSize);
if (fin.gcount()) {
fout.write(buffer, fin.gcount());
} else {
break;
if (!CloneFileContent(source, real_destination)) {
// if cloning did not succeed, fall back to blockwise copy
if (!CopyFileContentBlockwise(source, real_destination)) {
return false;
}
}
// Make sure the operating system has finished writing the file
// before closing it. This will ensure the file is finished before
// the check below.
fout.flush();
fin.close();
fout.close();
if (!fout) {
return false;
}
}
if (perms) {
if (!SystemTools::SetPermissions(real_destination, perm)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment