From 9835f4d247b058c498328f7e792bdd72e5a05fcc Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 12 Jan 2022 13:54:29 +0100 Subject: [PATCH 1/5] can we reduce the number of sys-calls? --- .../src/System/IO/FileSystem.Unix.cs | 68 ++++++++++++------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 32eedaa50d06d..fd7425ba6aa32 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -389,32 +389,8 @@ private static void CreateParentsAndDirectory(string fullPath) private static void MoveDirectory(string sourceFullPath, string destFullPath, bool sameDirectoryDifferentCase, bool? sourceDirectoryExists) { - sourceDirectoryExists ??= DirectoryExists(sourceFullPath); - - // Windows will throw if the source file/directory doesn't exist, we preemptively check - // to make sure our cross platform behavior matches .NET Framework behavior. - if (!sourceDirectoryExists.Value) + if (!Path.EndsInDirectorySeparator(sourceFullPath)) { - if (!FileExists(sourceFullPath)) - { - throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - } - - // Windows doesn't care if you try and copy a file via "MoveDirectory"... - // ... but it doesn't like the source to have a trailing slash ... - - // On Windows we end up with ERROR_INVALID_NAME, which is - // "The filename, directory name, or volume label syntax is incorrect." - // - // This surfaces as an IOException, if we let it go beyond here it would - // give DirectoryNotFound. - - if (Path.EndsInDirectorySeparator(sourceFullPath)) - { - throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - } - - // ... but it doesn't care if the destination has a trailing separator. destFullPath = Path.TrimEndingDirectorySeparator(destFullPath); } @@ -422,6 +398,32 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo { if (DirectoryExists(destFullPath)) { + sourceDirectoryExists ??= DirectoryExists(sourceFullPath); + + // Windows will throw if the source file/directory doesn't exist, we preemptively check + // to make sure our cross platform behavior matches .NET Framework behavior. + if (!sourceDirectoryExists.Value) + { + if (!FileExists(sourceFullPath)) + { + throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); + } + + // Windows doesn't care if you try and copy a file via "MoveDirectory"... + // ... but it doesn't like the source to have a trailing slash ... + + // On Windows we end up with ERROR_INVALID_NAME, which is + // "The filename, directory name, or volume label syntax is incorrect." + // + // This surfaces as an IOException, if we let it go beyond here it would + // give DirectoryNotFound. + + if (Path.EndsInDirectorySeparator(sourceFullPath)) + { + throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); + } + } + throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, destFullPath)); } else if (FileExists(destFullPath)) @@ -439,6 +441,22 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo { case Interop.Error.EACCES: // match Win32 exception throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, sourceFullPath), errorInfo.RawErrno); + case Interop.Error.ENOENT: + sourceDirectoryExists ??= DirectoryExists(sourceFullPath); + if (sourceDirectoryExists.Value) // the source directory exists, so destination does not + { + throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, destFullPath)); + } + else if (Path.EndsInDirectorySeparator(sourceFullPath) || !FileExists(sourceFullPath)) + { + throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); + } + else + { + throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); + } + case Interop.Error.ENOTDIR: // sourceFullPath exists and it's not a directory + throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); default: throw Interop.GetExceptionForIoErrno(errorInfo, isDirectory: true); } From f9c5c787e0f915a3a1494baa843a90825b68e2c9 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 12 Jan 2022 14:46:42 +0100 Subject: [PATCH 2/5] use single sys-call to check whether directory or a file exists --- .../src/System/IO/FileSystem.Unix.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index fd7425ba6aa32..92a27012749b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -389,6 +389,8 @@ private static void CreateParentsAndDirectory(string fullPath) private static void MoveDirectory(string sourceFullPath, string destFullPath, bool sameDirectoryDifferentCase, bool? sourceDirectoryExists) { + ReadOnlySpan destNoDirectorySeparator = Path.TrimEndingDirectorySeparator(destFullPath.AsSpan()); + if (!Path.EndsInDirectorySeparator(sourceFullPath)) { destFullPath = Path.TrimEndingDirectorySeparator(destFullPath); @@ -396,7 +398,7 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo if (!sameDirectoryDifferentCase) // This check is to allow renaming of directories { - if (DirectoryExists(destFullPath)) + if (Interop.Sys.Stat(destNoDirectorySeparator, out _) >= 0) { sourceDirectoryExists ??= DirectoryExists(sourceFullPath); @@ -424,10 +426,6 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo } } - throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, destFullPath)); - } - else if (FileExists(destFullPath)) - { // Some Unix distros will overwrite the destination file if it already exists. // Throwing IOException to match Windows behavior. throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, destFullPath)); From d5ba22f1e46a5b218a17023b8069598d860fb623 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 12 Jan 2022 15:27:26 +0100 Subject: [PATCH 3/5] more --- .../src/System/IO/DirectoryInfo.cs | 3 +- .../src/System/IO/FileSystem.Unix.cs | 58 +++++++------------ .../src/System/IO/FileSystem.cs | 4 +- 3 files changed, 24 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs index 3ca339fc40eab..c0cb71d705b39 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs @@ -196,8 +196,7 @@ public void MoveTo(string destDirName) string destination = Path.GetFullPath(destDirName); string destinationWithSeparator = PathInternal.EnsureTrailingSeparator(destination); - FileSystem.MoveDirectory(FullPath, destination, destinationWithSeparator, - OperatingSystem.IsWindows() ? null : Exists); // on Windows we don't need to perform the extra check + FileSystem.MoveDirectory(FullPath, destination, destinationWithSeparator); // on Windows we don't need to perform the extra check Init(originalPath: destDirName, fullPath: destinationWithSeparator, diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 92a27012749b0..473d8539d50ec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -387,43 +387,26 @@ private static void CreateParentsAndDirectory(string fullPath) } } - private static void MoveDirectory(string sourceFullPath, string destFullPath, bool sameDirectoryDifferentCase, bool? sourceDirectoryExists) + private static void MoveDirectory(string sourceFullPath, string destFullPath, bool sameDirectoryDifferentCase) { + ReadOnlySpan srcNoDirectorySeparator = Path.TrimEndingDirectorySeparator(sourceFullPath.AsSpan()); ReadOnlySpan destNoDirectorySeparator = Path.TrimEndingDirectorySeparator(destFullPath.AsSpan()); - if (!Path.EndsInDirectorySeparator(sourceFullPath)) - { - destFullPath = Path.TrimEndingDirectorySeparator(destFullPath); - } - if (!sameDirectoryDifferentCase) // This check is to allow renaming of directories { if (Interop.Sys.Stat(destNoDirectorySeparator, out _) >= 0) { - sourceDirectoryExists ??= DirectoryExists(sourceFullPath); - + // destination exists, but before we throw we need to check whether source exists or not // Windows will throw if the source file/directory doesn't exist, we preemptively check // to make sure our cross platform behavior matches .NET Framework behavior. - if (!sourceDirectoryExists.Value) + if (Interop.Sys.Stat(srcNoDirectorySeparator, out Interop.Sys.FileStatus sourceFileStatus) < 0) { - if (!FileExists(sourceFullPath)) - { - throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - } - - // Windows doesn't care if you try and copy a file via "MoveDirectory"... - // ... but it doesn't like the source to have a trailing slash ... - - // On Windows we end up with ERROR_INVALID_NAME, which is - // "The filename, directory name, or volume label syntax is incorrect." - // - // This surfaces as an IOException, if we let it go beyond here it would - // give DirectoryNotFound. - - if (Path.EndsInDirectorySeparator(sourceFullPath)) - { - throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - } + throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); + } + else if ((sourceFileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFDIR + && Path.EndsInDirectorySeparator(sourceFullPath)) + { + throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); } // Some Unix distros will overwrite the destination file if it already exists. @@ -432,7 +415,7 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo } } - if (Interop.Sys.Rename(sourceFullPath, destFullPath) < 0) + if (Interop.Sys.Rename(sourceFullPath, destNoDirectorySeparator.ToString()) < 0) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); switch (errorInfo.Error) @@ -440,19 +423,20 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo case Interop.Error.EACCES: // match Win32 exception throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, sourceFullPath), errorInfo.RawErrno); case Interop.Error.ENOENT: - sourceDirectoryExists ??= DirectoryExists(sourceFullPath); - if (sourceDirectoryExists.Value) // the source directory exists, so destination does not + if (Interop.Sys.Stat(srcNoDirectorySeparator, out Interop.Sys.FileStatus sourceFileStatus) >= 0) { + // the source directory exists, so destination does not + // example: Move("/tmp/existing/", "/tmp/nonExisting1/nonExisting2/") throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, destFullPath)); } - else if (Path.EndsInDirectorySeparator(sourceFullPath) || !FileExists(sourceFullPath)) - { + // if (Path.EndsInDirectorySeparator(sourceFullPath)) + // { throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - } - else - { - throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - } + // } + // else + // { + // throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); + // } case Interop.Error.ENOTDIR: // sourceFullPath exists and it's not a directory throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); default: diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs index 1eb5971a3a056..b3ec369b7888b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs @@ -21,7 +21,7 @@ internal static void VerifyValidPath(string path, string argName) } } - internal static void MoveDirectory(string sourceFullPath, string destFullPath, string destinationWithSeparator, bool? sourceDirectoryExists = default) + internal static void MoveDirectory(string sourceFullPath, string destFullPath, string destinationWithSeparator) { string sourcePath = PathInternal.EnsureTrailingSeparator(sourceFullPath); @@ -47,7 +47,7 @@ internal static void MoveDirectory(string sourceFullPath, string destFullPath, s if (!sourceRoot.Equals(destinationRoot, StringComparison.OrdinalIgnoreCase)) throw new IOException(SR.IO_SourceDestMustHaveSameRoot); - MoveDirectory(sourceFullPath, destFullPath, sameDirectoryDifferentCase, sourceDirectoryExists); + MoveDirectory(sourceFullPath, destFullPath, sameDirectoryDifferentCase); } } } From 5db0cf761c04ea61eceef496105eca03b2195311 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 12 Jan 2022 15:42:56 +0100 Subject: [PATCH 4/5] avoid memory allocations --- .../Unix/System.Native/Interop.Rename.cs | 16 ++++++++++++++ .../src/System/IO/Directory.cs | 5 +---- .../src/System/IO/DirectoryInfo.cs | 5 ++--- .../src/System/IO/FileSystem.Unix.cs | 21 ++++++------------- .../src/System/IO/FileSystem.cs | 11 +++++----- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Rename.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Rename.cs index 8670f15a7ba40..741f4000627c0 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Rename.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Rename.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; +using System.Text; internal static partial class Interop { @@ -18,5 +19,20 @@ internal static partial class Sys /// [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Rename", CharSet = CharSet.Ansi, SetLastError = true)] internal static partial int Rename(string oldPath, string newPath); + + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Rename", SetLastError = true)] + internal static partial int Rename(ref byte oldPath, ref byte newPath); + + internal static int Rename(ReadOnlySpan oldPath, ReadOnlySpan newPath) + { + ValueUtf8Converter converterNewPath = new(stackalloc byte[DefaultPathBufferSize]); + ValueUtf8Converter converterOldPath = new(stackalloc byte[DefaultPathBufferSize]); + int result = Rename( + ref MemoryMarshal.GetReference(converterOldPath.ConvertAndTerminateString(oldPath)), + ref MemoryMarshal.GetReference(converterNewPath.ConvertAndTerminateString(newPath))); + converterNewPath.Dispose(); + converterOldPath.Dispose(); + return result; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs index 2e2a4b8d72dd3..b434c2f8ba512 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs @@ -248,10 +248,7 @@ public static void Move(string sourceDirName, string destDirName) if (destDirName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destDirName)); - string destinationFullPath = Path.GetFullPath(destDirName); - string destinationWithSeparator = PathInternal.EnsureTrailingSeparator(destinationFullPath); - - FileSystem.MoveDirectory(Path.GetFullPath(sourceDirName), destinationFullPath, destinationWithSeparator); + FileSystem.MoveDirectory(Path.GetFullPath(sourceDirName), Path.GetFullPath(destDirName)); } public static void Delete(string path) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs index c0cb71d705b39..0f0d72cefe840 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs @@ -194,12 +194,11 @@ public void MoveTo(string destDirName) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destDirName)); string destination = Path.GetFullPath(destDirName); - string destinationWithSeparator = PathInternal.EnsureTrailingSeparator(destination); - FileSystem.MoveDirectory(FullPath, destination, destinationWithSeparator); // on Windows we don't need to perform the extra check + FileSystem.MoveDirectory(FullPath, destination); Init(originalPath: destDirName, - fullPath: destinationWithSeparator, + fullPath: PathInternal.EnsureTrailingSeparator(destination), fileName: null, isNormalized: true); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 473d8539d50ec..8f4775d09e418 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -397,6 +397,7 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo if (Interop.Sys.Stat(destNoDirectorySeparator, out _) >= 0) { // destination exists, but before we throw we need to check whether source exists or not + // Windows will throw if the source file/directory doesn't exist, we preemptively check // to make sure our cross platform behavior matches .NET Framework behavior. if (Interop.Sys.Stat(srcNoDirectorySeparator, out Interop.Sys.FileStatus sourceFileStatus) < 0) @@ -415,7 +416,7 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo } } - if (Interop.Sys.Rename(sourceFullPath, destNoDirectorySeparator.ToString()) < 0) + if (Interop.Sys.Rename(sourceFullPath, destNoDirectorySeparator) < 0) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); switch (errorInfo.Error) @@ -423,20 +424,10 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo case Interop.Error.EACCES: // match Win32 exception throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, sourceFullPath), errorInfo.RawErrno); case Interop.Error.ENOENT: - if (Interop.Sys.Stat(srcNoDirectorySeparator, out Interop.Sys.FileStatus sourceFileStatus) >= 0) - { - // the source directory exists, so destination does not - // example: Move("/tmp/existing/", "/tmp/nonExisting1/nonExisting2/") - throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, destFullPath)); - } - // if (Path.EndsInDirectorySeparator(sourceFullPath)) - // { - throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - // } - // else - // { - // throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); - // } + throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, + Interop.Sys.Stat(srcNoDirectorySeparator, out _) >= 0 + ? destFullPath // the source directory exists, so destination does not. Example: Move("/tmp/existing/", "/tmp/nonExisting1/nonExisting2/") + : sourceFullPath)); case Interop.Error.ENOTDIR: // sourceFullPath exists and it's not a directory throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); default: diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs index b3ec369b7888b..99821e0f9e120 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs @@ -21,9 +21,10 @@ internal static void VerifyValidPath(string path, string argName) } } - internal static void MoveDirectory(string sourceFullPath, string destFullPath, string destinationWithSeparator) + internal static void MoveDirectory(string sourceFullPath, string destFullPath) { - string sourcePath = PathInternal.EnsureTrailingSeparator(sourceFullPath); + ReadOnlySpan srcNoDirectorySeparator = Path.TrimEndingDirectorySeparator(sourceFullPath.AsSpan()); + ReadOnlySpan destNoDirectorySeparator = Path.TrimEndingDirectorySeparator(destFullPath.AsSpan()); ReadOnlySpan sourceDirNameFromFullPath = Path.GetFileName(sourceFullPath.AsSpan()); ReadOnlySpan destDirNameFromFullPath = Path.GetFileName(destFullPath.AsSpan()); @@ -37,11 +38,11 @@ internal static void MoveDirectory(string sourceFullPath, string destFullPath, s destDirNameFromFullPath.Equals(sourceDirNameFromFullPath, fileSystemSensitivity); // If the destination directories are the exact same name - if (!sameDirectoryDifferentCase && string.Equals(sourcePath, destinationWithSeparator, fileSystemSensitivity)) + if (!sameDirectoryDifferentCase && srcNoDirectorySeparator.Equals(destNoDirectorySeparator, fileSystemSensitivity)) throw new IOException(SR.IO_SourceDestMustBeDifferent); - ReadOnlySpan sourceRoot = Path.GetPathRoot(sourcePath.AsSpan()); - ReadOnlySpan destinationRoot = Path.GetPathRoot(destinationWithSeparator.AsSpan()); + ReadOnlySpan sourceRoot = Path.GetPathRoot(srcNoDirectorySeparator); + ReadOnlySpan destinationRoot = Path.GetPathRoot(destNoDirectorySeparator); // Compare paths for the same, skip this step if we already know the paths are identical. if (!sourceRoot.Equals(destinationRoot, StringComparison.OrdinalIgnoreCase)) From 2ca44b2f5fc9070241df176eccaef62617dd036b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 12 Jan 2022 16:10:03 +0100 Subject: [PATCH 5/5] build fix --- .../System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index eb7ad9b0d0b0b..a64f82b5ed498 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -154,7 +154,7 @@ public static DateTimeOffset GetLastWriteTime(string fullPath) return data.ftLastWriteTime.ToDateTimeOffset(); } - private static void MoveDirectory(string sourceFullPath, string destFullPath, bool sameDirectoryDifferentCase, bool? sourceDirectoryExists) + private static void MoveDirectory(string sourceFullPath, string destFullPath, bool sameDirectoryDifferentCase) { if (!Interop.Kernel32.MoveFile(sourceFullPath, destFullPath, overwrite: false)) {