Skip to content
This repository has been archived by the owner on Aug 8, 2024. It is now read-only.

Commit

Permalink
Revert "Cherry-pick dotnet#37583. (#375)" (#382)
Browse files Browse the repository at this point in the history
This reverts commit c388c71.

This reverts commit f27f69a.
  • Loading branch information
Martin Baulig authored and akoeplinger committed Dec 13, 2019
1 parent 50db8ee commit 796b89e
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ internal static partial class Interop
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CopyFile", SetLastError = true)]
internal static extern int CopyFile(SafeFileHandle source, string srcPath, string destPath, int overwrite);
internal static extern int CopyFile(SafeFileHandle source, SafeFileHandle destination);
}
}
2 changes: 1 addition & 1 deletion src/Native/Unix/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
#cmakedefine01 HAVE_KQUEUE
#cmakedefine01 HAVE_SENDFILE_4
#cmakedefine01 HAVE_SENDFILE_6
#cmakedefine01 HAVE_CLONEFILE
#cmakedefine01 HAVE_FCOPYFILE
#cmakedefine01 HAVE_GETNAMEINFO_SIGNED_FLAGS
#cmakedefine01 HAVE_GETPEEREID
#cmakedefine01 HAVE_SUPPORT_FOR_DUAL_MODE_IPV4_PACKET_INFO
Expand Down
112 changes: 28 additions & 84 deletions src/Native/Unix/System.Native/pal_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@
#include <termios.h>
#include <unistd.h>
#include <limits.h>
#if HAVE_CLONEFILE
#include <sys/attr.h>
#include <sys/clonefile.h>
#endif
#if HAVE_SENDFILE_4
#if HAVE_FCOPYFILE
#include <copyfile.h>
#elif HAVE_SENDFILE_4
#include <sys/sendfile.h>
#endif
#if HAVE_INOTIFY
Expand Down Expand Up @@ -1195,6 +1193,7 @@ int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize)
return (int32_t)count;
}

#if !HAVE_FCOPYFILE
// Read all data from inFd and write it to outFd
static int32_t CopyFile_ReadWrite(int inFd, int outFd)
{
Expand Down Expand Up @@ -1247,15 +1246,25 @@ static int32_t CopyFile_ReadWrite(int inFd, int outFd)
free(buffer);
return 0;
}
#endif // !HAVE_FCOPYFILE

int32_t SystemNative_CopyFile(intptr_t sourceFd, const char* srcPath, const char* destPath, int32_t overwrite)
int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd)
{
int inFd = ToFileDescriptor(sourceFd);
int outFd;
int outFd = ToFileDescriptor(destinationFd);

#if HAVE_FCOPYFILE
// If fcopyfile is available (OS X), try to use it, as the whole copy
// can be performed in the kernel, without lots of unnecessary copying.
// Copy data and metadata.
return fcopyfile(inFd, outFd, NULL, COPYFILE_ALL) == 0 ? 0 : -1;
#else
// Get the stats on the source file.
int ret;
int tmpErrno;
int openFlags;
struct stat_ sourceStat;
bool copied = false;

// First, stat the source file.
while ((ret = fstat_(inFd, &sourceStat)) < 0 && errno == EINTR);
if (ret != 0)
{
Expand All @@ -1275,74 +1284,9 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, const char* srcPath, const char
}
#endif

struct stat_ destStat;
while ((ret = stat_(destPath, &destStat)) < 0 && errno == EINTR);
if (ret == 0)
{
if (!overwrite)
{
errno = EEXIST;
return -1;
}

if (sourceStat.st_dev == destStat.st_dev && sourceStat.st_ino == destStat.st_ino)
{
// Attempt to copy file over itself. Fail with the same error code as
// open would.
errno = EBUSY;
return -1;
}

#if HAVE_CLONEFILE
// For clonefile we need to unlink the destination file first but we need to
// check permission first to ensure we don't try to unlink read-only file.
if (access(destPath, W_OK) != 0)
{
return -1;
}

ret = unlink(destPath);
if (ret != 0)
{
return ret;
}
#endif
}

#if HAVE_CLONEFILE
while ((ret = clonefile(srcPath, destPath, 0)) < 0 && errno == EINTR);
// EEXIST can happen due to race condition between the stat/unlink above
// and the clonefile here. The file could be (re-)created from another
// thread or process before we have a chance to call clonefile. Handle
// it by falling back to the slow path.
if (ret == 0 || (errno != ENOTSUP && errno != EXDEV && errno != EEXIST))
{
return ret;
}
#else
// Unused variable
(void)srcPath;
#endif

openFlags = O_WRONLY | O_TRUNC | O_CREAT | (overwrite ? 0 : O_EXCL);
#if HAVE_O_CLOEXEC
openFlags |= O_CLOEXEC;
#endif
while ((outFd = open(destPath, openFlags, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) < 0 && errno == EINTR);
if (outFd < 0)
{
return -1;
}
#if !HAVE_O_CLOEXEC
fcntl(outFd, F_SETFD, FD_CLOEXEC);
#endif

// Get the stats on the source file.
bool copied = false;

#if HAVE_SENDFILE_4
// If sendfile is available (Linux), try to use it, as the whole copy
// can be performed in the kernel, without lots of unnecessary copying.
#if HAVE_SENDFILE_4

// On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a
// `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t.
Expand All @@ -1358,9 +1302,6 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, const char* srcPath, const char
{
if (errno != EINVAL && errno != ENOSYS)
{
tmpErrno = errno;
close(outFd);
errno = tmpErrno;
return -1;
}
else
Expand All @@ -1386,9 +1327,6 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, const char* srcPath, const char
// Manually read all data from the source and write it to the destination.
if (!copied && CopyFile_ReadWrite(inFd, outFd) != 0)
{
tmpErrno = errno;
close(outFd);
errno = tmpErrno;
return -1;
}

Expand All @@ -1413,10 +1351,16 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, const char* srcPath, const char
while ((ret = futimes(outFd, origTimes)) < 0 && errno == EINTR);
#endif

tmpErrno = errno;
close(outFd);
errno = tmpErrno;
#if !TARGET_ANDROID
// On Android, the copy should still succeed even if copying the file times didn't.
if (ret != 0)
{
return -1;
}
#endif

return 0;
#endif // HAVE_FCOPYFILE
}

intptr_t SystemNative_INotifyInit(void)
Expand Down
4 changes: 2 additions & 2 deletions src/Native/Unix/System.Native/pal_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,11 +705,11 @@ DLLEXPORT void SystemNative_Sync(void);
DLLEXPORT int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize);

/**
* Copies all data from the source file descriptor/path to the destination file path.
* Copies all data from the source file descriptor to the destination file descriptor.
*
* Returns 0 on success; otherwise, returns -1 and sets errno.
*/
DLLEXPORT int32_t SystemNative_CopyFile(intptr_t sourceFd, const char* srcPath, const char* destPath, int32_t overwrite);
DLLEXPORT int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd);

/**
* Initializes a new inotify instance and returns a file
Expand Down
24 changes: 2 additions & 22 deletions src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,9 @@ public static void CopyFile(string sourceFullPath, string destFullPath, bool ove

// Copy the contents of the file from the source to the destination, creating the destination in the process
using (var src = new FileStream(sourceFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.None))
using (var dst = new FileStream(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, DefaultBufferSize, FileOptions.None))
{
int result = Interop.Sys.CopyFile(src.SafeFileHandle, sourceFullPath, destFullPath, overwrite ? 1 : 0);

if (result < 0)
{
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();

// If we fail to open the file due to a path not existing, we need to know whether to blame
// the file itself or its directory. If we're creating the file, then we blame the directory,
// otherwise we blame the file.
//
// When opening, we need to align with Windows, which considers a missing path to be
// FileNotFound only if the containing directory exists.

bool isDirectory = (error.Error == Interop.Error.ENOENT) &&
(overwrite || !DirectoryExists(Path.GetDirectoryName(PathInternal.TrimEndingDirectorySeparator(destFullPath))));

Interop.CheckIo(
error.Error,
destFullPath,
isDirectory,
errorRewriter: e => (e.Error == Interop.Error.EISDIR) ? Interop.Error.EACCES.Info() : e);
}
Interop.CheckIo(Interop.Sys.CopyFile(src.SafeFileHandle, dst.SafeFileHandle));
}
}

Expand Down

0 comments on commit 796b89e

Please sign in to comment.