Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
a373c59
Initial implementation of fix for #77835 - cloning files on macOS
hamarb123 Dec 5, 2022
f21a5ae
Fix some build issues from previous commit for #77835
hamarb123 Dec 5, 2022
1890115
Fix some build issues from previous commit for #77835 again
hamarb123 Dec 5, 2022
e490d5e
Fix some build issues from previous commit for #77835 again (2)
hamarb123 Dec 5, 2022
1e43a91
Fix some build issues from previous commit for #77835 again (3)
hamarb123 Dec 5, 2022
4c5a995
Fix some build issues from previous commit for #77835 again (4)
hamarb123 Dec 5, 2022
c65e229
Fix some build issues from previous commit for #77835 again (5)
hamarb123 Dec 5, 2022
5377efb
Fix copying a file onto itself logic for #77835
hamarb123 Dec 6, 2022
8d8328a
Fix some nullability issues from previous commit for #77835
hamarb123 Dec 6, 2022
835fc2f
Fix some nullability issues from previous commit for #77835 (again)
hamarb123 Dec 6, 2022
05f9d26
Fix bug introduced in 'Fix copying a file onto itself logic for #77835'
hamarb123 Dec 6, 2022
5fe4cd2
Merge remote-tracking branch 'upstream'
hamarb123 Dec 8, 2022
28ef104
Merge branch 'main' of https://github.com/dotnet/runtime
hamarb123 Jan 24, 2023
21144e0
Add extra test
hamarb123 Jan 24, 2023
049627f
Use correct comment formatting
hamarb123 Jan 24, 2023
ab18c05
Use clonefile API instead
hamarb123 Jan 24, 2023
776f500
Remove redundant equality
hamarb123 Jan 24, 2023
dcddd8f
Clone ACLs and make CopyOntoLockedFile test conditional
hamarb123 Jan 30, 2023
ac27788
Remove retry logic
hamarb123 Jan 30, 2023
f320a70
Address feedback
hamarb123 May 14, 2023
3d4a962
Implement feedback to remove StartedCopyFileState
hamarb123 May 14, 2023
a908a79
Fix compilation issues by me thinking I was smart
hamarb123 May 14, 2023
16cedc6
Fix nullability
hamarb123 May 14, 2023
d15fed3
Implement feedback
hamarb123 May 16, 2023
617afbf
Merge remote-tracking branch 'upstream/main'
hamarb123 May 16, 2023
eb97585
Fix compilation issues
hamarb123 May 16, 2023
70c6e8b
Fix missed line in FileSystem.CopyFile.OSX.cs
hamarb123 May 16, 2023
f866a3d
Update src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.…
hamarb123 May 16, 2023
bf67649
Update src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.…
hamarb123 May 16, 2023
ff108db
Update src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.…
hamarb123 May 16, 2023
69071eb
Update src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.…
hamarb123 May 16, 2023
20c42f9
Replace var with actual type as per feedback
hamarb123 May 16, 2023
1280a8a
Update comment for clarity
hamarb123 May 16, 2023
f33ae9c
Implement feedback to remove OpenCopyFileDstHandle
hamarb123 May 16, 2023
068a8aa
Fix compilation errors
hamarb123 May 16, 2023
2c48aad
Remove some comments as per feedback
hamarb123 May 17, 2023
ef84010
Changes from feedback
hamarb123 May 17, 2023
8924afe
Remove redundant line as per feedback
hamarb123 May 17, 2023
1649d43
Remove old comment
hamarb123 May 17, 2023
a82767b
Implement some feedback
hamarb123 May 18, 2023
c0c7e33
Implement suggestion that cuts down on duplication
hamarb123 May 18, 2023
18b8c2e
Remove unnecessary using
hamarb123 May 18, 2023
00b68ed
Remove some other unnecessary usings
hamarb123 May 18, 2023
afa73ca
Fix filePermissions missing as per feedback
hamarb123 May 18, 2023
a684785
Fix compilation issues from files having wrong name in projitems
hamarb123 May 18, 2023
61c0576
Make the CopyFile implementation more similar to how it was before
hamarb123 May 18, 2023
7c6c80b
Fix copied code compilation issue
hamarb123 May 18, 2023
9cdb4b3
Remove filePermissions parameter as per feedback
hamarb123 May 23, 2023
2dc08e4
Remove isReadOnly as per feedback
hamarb123 May 23, 2023
02cfaf6
Fix whitespace
hamarb123 May 23, 2023
1fc02f7
Fix compilation errors
hamarb123 May 23, 2023
71bc96e
Fix more compilation issues
hamarb123 May 23, 2023
aa76f24
Try clonefile immediately as per feedback
hamarb123 May 23, 2023
3945a9f
Add error handling to it, instead of always falling back
hamarb123 May 23, 2023
3457dcc
Add missing cast
hamarb123 May 23, 2023
788216e
Remove unneeded filePermissions specification
hamarb123 May 23, 2023
233217a
Change indentation of delete section
hamarb123 May 23, 2023
20caacd
Temporarily comment out the stat section
hamarb123 May 23, 2023
6d40467
Remove some unneeded indentation
hamarb123 May 23, 2023
705aa4f
Remove special casing of EINVAL
hamarb123 May 23, 2023
a5de060
Remove section which does stat as it's unneeded
hamarb123 May 23, 2023
7221560
Improve some of the comments.
hamarb123 May 23, 2023
1db28fd
Add handler for case of EEXIST still
hamarb123 May 23, 2023
d360ae4
Remove partial method definitions
hamarb123 May 23, 2023
323cf67
Add some code to deal with EINVAL
hamarb123 May 23, 2023
44ef8f3
Remove srcStat parameter as it's no longer needed
hamarb123 May 23, 2023
50195af
Revert now-unnecessary changes to OpenReadOnly
hamarb123 May 23, 2023
53f52bc
Use Unlink instead of File.Delete
hamarb123 May 23, 2023
aee3ea8
Remove unnecessary diffs
hamarb123 May 23, 2023
c6174e6
Fix compilation errors
hamarb123 May 24, 2023
0838d9f
Update src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.…
hamarb123 May 24, 2023
013c775
Reorder flags check as per feedback
hamarb123 May 24, 2023
487aee9
Fix test failures
hamarb123 May 24, 2023
37b5183
Extract attempting clonefile to a separate function
hamarb123 May 24, 2023
bc99ec9
Use CreateOpenException when deleting the destination file
hamarb123 May 24, 2023
e1c7739
Use a debug statement to check EINVAL instead
hamarb123 May 24, 2023
aec1eac
Improve & fix comment
hamarb123 May 24, 2023
483858d
Fix compilation errors
hamarb123 May 24, 2023
f67bd18
Implement feedback
hamarb123 May 24, 2023
5cf74af
Move the Debug.Assert calls together
hamarb123 May 24, 2023
10011a5
Implement feedback to use partial functions
hamarb123 Jun 6, 2023
a793bb7
Implement feedback to use pattern matching, and move comments
hamarb123 Jun 6, 2023
550a76a
Implement other feedback
hamarb123 Jun 6, 2023
32c5463
Fix visibility of partial method
hamarb123 Jun 6, 2023
de964d0
Merge branch 'main' of https://github.com/dotnet/runtime
hamarb123 Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use correct comment formatting
• Add a space after //
  • Loading branch information
hamarb123 committed Jan 24, 2023
commit 049627fa50da55ceb4f1126eb48df693bfc8ecf1
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ internal static partial class FileSystem
{
public static partial void CopyFile(string sourceFullPath, string destFullPath, bool overwrite)
{
//Attempt to clone the file:
// Attempt to clone the file:

//Simplify the destination path (i.e. unlink all the links except the last one itself,
//i.e. for /link1/link2, you could get /folder1/link2).
// Simplify the destination path (i.e. unlink all the links except the last one itself,
// i.e. for /link1/link2, you could get /folder1/link2).
string destFullPath_Full = Path.GetFullPath(destFullPath);
string? destPathFolder = Path.GetDirectoryName(destFullPath_Full);
string destPath;
Expand Down Expand Up @@ -42,49 +42,49 @@ public static partial void CopyFile(string sourceFullPath, string destFullPath,
}
}

//Get the full path of the source path and verify that we're not copying the source file onto itself
// Get the full path of the source path and verify that we're not copying the source file onto itself
string fullSource = TryGetLinkTarget(sourceFullPath, destPath, overwrite) ?? sourceFullPath;

//Start the file copy and prepare for finalization
// Start the file copy and prepare for finalization
StartedCopyFileState startedCopyFile = StartCopyFile(fullSource, destPath, overwrite, openDst: false);

//Attempt counter just in case we somehow loop infinite times e.g. on a
//filesystem that doesn't actually delete files but pretends it does.
//Declare error variable here since it can be used after some jumping around.
// Attempt counter just in case we somehow loop infinite times e.g. on a
// filesystem that doesn't actually delete files but pretends it does.
// Declare error variable here since it can be used after some jumping around.
int attempts = 0;
int error;

try
{
//Don't need to re-read the link on our first attempt
// Don't need to re-read the link on our first attempt
bool failOnRereadDoesntChange = false;
if (overwrite)
{
//Ensure file is deleted on first try.
//Get a lock to the dest file for compat reasons, and then delete it.
// Ensure file is deleted on first try.
// Get a lock to the dest file for compat reasons, and then delete it.
using SafeFileHandle? dstHandle = OpenCopyFileDstHandle(destPath, true, startedCopyFile, false);
File.Delete(destPath);
}
goto tryAgain;

//We may want to re-read the link to see if its path has changed
// We may want to re-read the link to see if its path has changed
tryAgainWithReadLink:
if (++attempts >= 5) goto throwError;
string fullSource2 = TryGetLinkTarget(sourceFullPath, destPath, overwrite) ?? sourceFullPath;
if (fullSource != fullSource2)
{
//Path has changed
// Path has changed
startedCopyFile.Dispose();
startedCopyFile = StartCopyFile(fullSource, destPath, overwrite, openDst: false);
}
else if (failOnRereadDoesntChange)
{
//Path hasn't changed and we want to throw the error we got earlier
// Path hasn't changed and we want to throw the error we got earlier
goto throwError;
}
failOnRereadDoesntChange = false;

//Attempt to clone the file
// Attempt to clone the file
tryAgain:
unsafe
{
Expand All @@ -94,15 +94,15 @@ public static partial void CopyFile(string sourceFullPath, string destFullPath,
}
}

//Check the error
// Check the error
error = Marshal.GetLastWin32Error();
const int ENOTSUP = 45;
const int EEXIST = 17;
const int ENOENT = 2;
bool directoryExist = false;
if ((error == ENOTSUP && FileOrDirectoryExists(destPath)) || error == EEXIST)
{
//This means the destination existed, try again with the destination deleted if appropriate
// This means the destination existed, try again with the destination deleted if appropriate
error = EEXIST;
if (Directory.Exists(destPath))
{
Expand All @@ -111,55 +111,55 @@ public static partial void CopyFile(string sourceFullPath, string destFullPath,
}
if (overwrite)
{
//Get a lock to the dest file for compat reasons, and then delete it.
// Get a lock to the dest file for compat reasons, and then delete it.
using SafeFileHandle? dstHandle = OpenCopyFileDstHandle(destPath, true, startedCopyFile, false);
File.Delete(destPath);
goto tryAgainWithReadLink;
}
}
else if (error == ENOTSUP)
{
//This probably means cloning is not supported, try the standard implementation
// This probably means cloning is not supported, try the standard implementation
goto fallback;
}
else if (error == ENOENT)
{
//This can happen if the source is a symlink and it has been changed to a different file, and the first has been deleted or renamed, for example.
//failOnRereadDoesntChange means we want to fail if the link didn't change, indicating the source actually doesn't exist.
// This can happen if the source is a symlink and it has been changed to a different file, and the first has been deleted or renamed, for example.
// failOnRereadDoesntChange means we want to fail if the link didn't change, indicating the source actually doesn't exist.
failOnRereadDoesntChange = true;
goto tryAgainWithReadLink;
}

//Throw an appropriate error
// Throw an appropriate error
throwError:
if (directoryExist)
{
throw new IOException(SR.Format(SR.Arg_FileIsDirectory_Name, destFullPath));
}
throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(error));

//Fallback to the standard unix implementation for when cloning is not supported
// Fallback to the standard unix implementation for when cloning is not supported
fallback:

//Open the dst handle
// Open the dst handle
startedCopyFile.dst = OpenCopyFileDstHandle(destFullPath, overwrite, startedCopyFile, true);

//Copy the file using the standard unix implementation
// Copy the file using the standard unix implementation
StandardCopyFile(startedCopyFile);
}
finally
{
startedCopyFile.Dispose();
}

//Attempts to read the path's link target, or returns null even if the path doesn't exist rather than throwing.
//Throws an error if 'path' is at any point equal to 'destPath', since it means we're copying onto itself.
//Throws an error if 'path' has too many symbolic link levels.
// Attempts to read the path's link target, or returns null even if the path doesn't exist rather than throwing.
// Throws an error if 'path' is at any point equal to 'destPath', since it means we're copying onto itself.
// Throws an error if 'path' has too many symbolic link levels.
static string? TryGetLinkTarget(string path, string destPath, bool overwrite)
{
if (path == destPath)
{
//Throw an appropriate error
// Throw an appropriate error
if (overwrite) throw new IOException(SR.Format(SR.IO_SharingViolation_File, destPath));
else throw new IOException(SR.Format(SR.IO_FileExists_Name, destPath));
}
Expand All @@ -169,24 +169,24 @@ public static partial void CopyFile(string sourceFullPath, string destFullPath,
string? newTarget;
try
{
//Attempt to unlink the current link
// Attempt to unlink the current link
newTarget = ResolveLinkTargetString(currentTarget ?? path, false, false);
}
catch
{
//This means path's target doesn't exist, stop unlinking
// This means path's target doesn't exist, stop unlinking
return currentTarget;
}

//Check the new target path
// Check the new target path
if (newTarget == destPath)
{
//Throw an appropriate error
// Throw an appropriate error
if (overwrite) throw new IOException(SR.Format(SR.IO_SharingViolation_File, destPath));
else throw new IOException(SR.Format(SR.IO_FileExists_Name, destPath));
}

//Store the unlinked path, otherwise return our current path
// Store the unlinked path, otherwise return our current path
if (string.IsNullOrEmpty(newTarget))
{
return currentTarget;
Expand All @@ -197,11 +197,11 @@ public static partial void CopyFile(string sourceFullPath, string destFullPath,
}
}

//If we get here, we've gone through MaxFollowedLinks iterations
// If we get here, we've gone through MaxFollowedLinks iterations
throw new IOException(SR.Format(SR.IO_TooManySymbolicLinkLevels, path));
}

//Checks if a file or directory exists without caring which it was
// Checks if a file or directory exists without caring which it was
static bool FileOrDirectoryExists(string path)
{
return Interop.Sys.Stat(path, out _) >= 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ internal static partial class FileSystem
{
public static partial void CopyFile(string sourceFullPath, string destFullPath, bool overwrite)
{
//Start the file copy and prepare for finalization
// Start the file copy and prepare for finalization
using StartedCopyFileState startedCopyFile = StartCopyFile(sourceFullPath, destFullPath, overwrite);

//Copy the file using the standard unix implementation
// Copy the file using the standard unix implementation
StandardCopyFile(startedCopyFile);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ internal static partial class FileSystem
UnixFileMode.OtherWrite |
UnixFileMode.OtherExecute;

//Helper type to facilitate returning values from StartCopyFile without having
//to declare a massive tuple multiple times, and making it easier to dispose.
// Helper type to facilitate returning values from StartCopyFile without having
// to declare a massive tuple multiple times, and making it easier to dispose.
private struct StartedCopyFileState : IDisposable
{
public long fileLength;
Expand All @@ -54,9 +54,9 @@ public void Dispose()

private static StartedCopyFileState StartCopyFile(string sourceFullPath, string destFullPath, bool overwrite, bool openDst = true)
{
//The return value is expected to be Disposed by the caller (unless this method throws) once the copy is complete.
//Begins 'CopyFile' by locking and creating the relevant file handles.
//If 'openDst' is false, it doesn't open the destination file handle, nor check anything to do with it (used in macOS implementation).
// The return value is expected to be Disposed by the caller (unless this method throws) once the copy is complete.
// Begins 'CopyFile' by locking and creating the relevant file handles.
// If 'openDst' is false, it doesn't open the destination file handle, nor check anything to do with it (used in macOS implementation).

StartedCopyFileState startedCopyFile = default;
try
Expand All @@ -70,15 +70,15 @@ private static StartedCopyFileState StartCopyFile(string sourceFullPath, string
throw;
}

//Return the collection of information we have gotten back.
// Return the collection of information we have gotten back.
return startedCopyFile;
}

private static SafeFileHandle? OpenCopyFileDstHandle(string destFullPath, bool overwrite, StartedCopyFileState startedCopyFile, bool openNewFile)
{
//This function opens the 'dst' file handle for 'CopyFile', it is
//split out since the logic on OSX-like OSes is a bit different.
//'openNewFile' = false is used when we want to try to find the file only.
// This function opens the 'dst' file handle for 'CopyFile', it is
// split out since the logic on OSX-like OSes is a bit different.
// 'openNewFile' = false is used when we want to try to find the file only.
if (!openNewFile)
{
try
Expand Down Expand Up @@ -110,17 +110,17 @@ private static StartedCopyFileState StartCopyFile(string sourceFullPath, string

private static void StandardCopyFile(StartedCopyFileState startedCopyFile)
{
//Copy the file in a way that works on all Unix Operating Systems.
//The 'startedCopyFile' parameter should take the output from 'StartCopyFile'.
//'startedCopyFile' should be disposed by the caller. Assumes src and dst
//are non-null, caller must check this, return values from StartCopyFile
//are non-null (except dst when openDst is true), and from return values from
//OpenCopyFileDstHandle are non-null (except possibly when openNewFile is false).
// Copy the file in a way that works on all Unix Operating Systems.
// The 'startedCopyFile' parameter should take the output from 'StartCopyFile'.
// 'startedCopyFile' should be disposed by the caller. Assumes src and dst
// are non-null, caller must check this, return values from StartCopyFile
// are non-null (except dst when openDst is true), and from return values from
// OpenCopyFileDstHandle are non-null (except possibly when openNewFile is false).
Interop.CheckIo(Interop.Sys.CopyFile(startedCopyFile.src!, startedCopyFile.dst!, startedCopyFile.fileLength));
}

//CopyFile is defined in either FileSystem.CopyFile.OSX.cs or FileSystem.CopyFile.OtherUnix.cs
//The implementations on OSX-like Operating Systems attempts to clone the file first.
// CopyFile is defined in either FileSystem.CopyFile.OSX.cs or FileSystem.CopyFile.OtherUnix.cs
// The implementations on OSX-like Operating Systems attempts to clone the file first.
public static partial void CopyFile(string sourceFullPath, string destFullPath, bool overwrite);

#pragma warning disable IDE0060
Expand Down