Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 5.6k
Clone files on OSX-like platforms when possible, instead of copying the whole file#79243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
a373c59f21a5ae1890115e490d5e1e43a914c5a995c65e2295377efb8d8328a835fc2f05f9d265fe4cd228ef10421144e0049627fab18c05776f500dcddd8fac27788f320a703d4a962a908a7916cedc6d15fed3617afbfeb9758570c6e8bf866a3dbf67649ff108db69071eb20c42f91280a8af33ae9c068a8aa2c48aadef840108924afe1649d43a82767bc0c7e3318b8c2e00b68edafa73caa68478561c05767c6c80b9cdb4b32dc08e402cfaf61fc02f771bc96eaa76f243945a9f3457dcc788216e233217a20caacd6d40467705aa4fa5de06072215601db28fdd360ae4323cf6744ef8f350195af53f52bcaee3ea8c6174e60838d9f013c775487aee937b5183bc99ec9e1c7739aec1eac483858df67bd185cf74af10011a5a793bb7550a76a32c5463de964d0File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| using Microsoft.Win32.SafeHandles; | ||
| using System.Diagnostics; | ||
| namespace System.IO | ||
| { | ||
| internal static partial class FileSystem | ||
| { | ||
| static partial void TryCloneFile(string sourceFullPath, string destFullPath, bool overwrite, ref bool cloned) | ||
| { | ||
| // This helper function calls out to clonefile, and returns the error. | ||
| static bool TryCloneFile(string sourceFullPath, string destFullPath, int flags, out Interop.Error error) | ||
| { | ||
| if (Interop.@libc.clonefile(sourceFullPath, destFullPath, flags) == 0) | ||
| { | ||
| // Success. | ||
| error = Interop.Error.SUCCESS; | ||
| return true; | ||
| } | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| error = Interop.Sys.GetLastError(); | ||
| return false; | ||
| } | ||
| // Try to clone the file immediately, this will only succeed if the | ||
| // destination doesn't exist, so we don't worry about locking for this one. | ||
| int flags = Interop.@libc.CLONE_ACL; | ||
| Interop.Error error; | ||
| if (TryCloneFile(sourceFullPath, destFullPath, flags, out error)) | ||
| { | ||
| cloned = true; | ||
| return; | ||
| } | ||
| // Some filesystems don't support ACLs, so may fail due to trying to copy ACLs. | ||
| // This will disable them and allow trying again (a maximum of 1 time). | ||
| if (error == Interop.Error.EINVAL) | ||
| { | ||
| flags = 0; | ||
| if (TryCloneFile(sourceFullPath, destFullPath, flags, out error)) | ||
| { | ||
| cloned = true; | ||
| return; | ||
| } | ||
| } | ||
| // Try to delete the destination file if we're overwriting. | ||
| if (error == Interop.Error.EEXIST && overwrite) | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| { | ||
| // Delete the destination. This should fail on directories. Get a lock to the dest file to ensure we don't copy onto it when | ||
| // it's locked by something else, and then delete it. It should also fail if destination == source since it's already locked. | ||
danmoseley marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| try | ||
| { | ||
| using SafeFileHandle? dstHandle = SafeFileHandle.Open(destFullPath, FileMode.Open, FileAccess.ReadWrite, | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| FileShare.None, FileOptions.None, preallocationSize: 0, createOpenException: CreateOpenExceptionForCopyFile); | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (Interop.Sys.Unlink(destFullPath) < 0 && | ||
| Interop.Sys.GetLastError() != Interop.Error.ENOENT) | ||
| { | ||
| // Fall back to standard copy as an unexpected error has occurred. | ||
| return; | ||
| } | ||
| } | ||
| catch (FileNotFoundException) | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| { | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // We don't want to throw if it's just the file not existing, since we're trying to delete it. | ||
| } | ||
| // Try clonefile now we've deleted the destination file. | ||
| if (TryCloneFile(sourceFullPath, destFullPath, flags, out error)) | ||
| { | ||
| cloned = true; | ||
| return; | ||
| } | ||
| } | ||
| if (error is Interop.Error.ENOTSUP // Check if it's not supported, | ||
| or Interop.Error.EXDEV // if files are on different filesystems, | ||
| or Interop.Error.EEXIST) // or if the destination file still exists. | ||
| { | ||
| // Fall back to normal copy. | ||
| return; | ||
| } | ||
| // Throw the appropriate exception. | ||
| Debug.Assert(error != Interop.Error.EINVAL); // We shouldn't fail due to an invalid parameter. | ||
| Debug.Assert(error != Interop.Error.SUCCESS); // We shouldn't fail with success. | ||
| throw Interop.GetExceptionForIoErrno(error.Info(), destFullPath); | ||
hamarb123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.