Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStephen Toub <stoub@microsoft.com>2017-04-20 02:10:23 +0300
committerGitHub <noreply@github.com>2017-04-20 02:10:23 +0300
commit0ad947709f6629cf965a17ab94d9a67c3faf85b2 (patch)
treeff2a5d951aceccca10ac87faa4b5086fe76a42fb /src
parentddac822687e0321ee31391dddf838773daa7e4a5 (diff)
parentb6bbe8a43984f291e01113161efe1bc6e322815f (diff)
Merge pull request #18619 from stephentoub/file_move_fix
Fix UnixFileSystem.MoveFile on macOS when just changing file casing
Diffstat (limited to 'src')
-rw-r--r--src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs2
-rw-r--r--src/Native/Unix/System.Native/pal_io.cpp2
-rw-r--r--src/Native/Unix/System.Native/pal_io.h2
-rw-r--r--src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs20
-rw-r--r--src/System.IO.FileSystem/tests/File/Move.cs28
5 files changed, 52 insertions, 2 deletions
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs b/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
index a8bc2ec7d1..cda00ac6c3 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
@@ -27,6 +27,8 @@ internal static partial class Interop
internal long MTime;
internal long CTime;
internal long BirthTime;
+ internal long Dev;
+ internal long Ino;
}
internal static class FileTypes
diff --git a/src/Native/Unix/System.Native/pal_io.cpp b/src/Native/Unix/System.Native/pal_io.cpp
index a672c1e91e..a1c41e37d6 100644
--- a/src/Native/Unix/System.Native/pal_io.cpp
+++ b/src/Native/Unix/System.Native/pal_io.cpp
@@ -140,6 +140,8 @@ static_assert(PAL_IN_ISDIR == IN_ISDIR, "");
static void ConvertFileStatus(const struct stat_& src, FileStatus* dst)
{
+ dst->Dev = static_cast<int64_t>(src.st_dev);
+ dst->Ino = static_cast<int64_t>(src.st_ino);
dst->Flags = FILESTATUS_FLAGS_NONE;
dst->Mode = static_cast<int32_t>(src.st_mode);
dst->Uid = src.st_uid;
diff --git a/src/Native/Unix/System.Native/pal_io.h b/src/Native/Unix/System.Native/pal_io.h
index 083efa04ee..deace9ca30 100644
--- a/src/Native/Unix/System.Native/pal_io.h
+++ b/src/Native/Unix/System.Native/pal_io.h
@@ -24,6 +24,8 @@ struct FileStatus
int64_t MTime; // time of last modification
int64_t CTime; // time of last status change
int64_t BirthTime; // time the file was created
+ int64_t Dev; // ID of the device containing the file
+ int64_t Ino; // inode number of the file
};
/************
diff --git a/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs b/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
index a74578005f..59319fe26e 100644
--- a/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
+++ b/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
@@ -82,8 +82,24 @@ namespace System.IO
{
// The desired behavior for Move(source, dest) is to not overwrite the destination file
// if it exists. Since rename(source, dest) will replace the file at 'dest' if it exists,
- // link/unlink are used instead. Note that the Unix FileSystemWatcher will treat a Move
- // as a Creation and Deletion instead of a Rename and thus differ from Windows.
+ // link/unlink are used instead. However, if the source path and the dest path refer to
+ // the same file, then do a rename rather than a link and an unlink. This is important
+ // for case-insensitive file systems (e.g. renaming a file in a way that just changes casing),
+ // so that we support changing the casing in the naming of the file. If this fails in any
+ // way (e.g. source file doesn't exist, dest file doesn't exist, rename fails, etc.), we
+ // just fall back to trying the link/unlink approach and generating any exceptional messages
+ // from there as necessary.
+ Interop.Sys.FileStatus sourceStat, destStat;
+ if (Interop.Sys.LStat(sourceFullPath, out sourceStat) == 0 && // source file exists
+ Interop.Sys.LStat(destFullPath, out destStat) == 0 && // dest file exists
+ sourceStat.Dev == destStat.Dev && // source and dest are on the same device
+ sourceStat.Ino == destStat.Ino && // and source and dest are the same file on that device
+ Interop.Sys.Rename(sourceFullPath, destFullPath) == 0) // try the rename
+ {
+ // Renamed successfully.
+ return;
+ }
+
if (Interop.Sys.Link(sourceFullPath, destFullPath) < 0)
{
// If link fails, we can fall back to doing a full copy, but we'll only do so for
diff --git a/src/System.IO.FileSystem/tests/File/Move.cs b/src/System.IO.FileSystem/tests/File/Move.cs
index eeb4c914b7..995754ba5e 100644
--- a/src/System.IO.FileSystem/tests/File/Move.cs
+++ b/src/System.IO.FileSystem/tests/File/Move.cs
@@ -121,6 +121,34 @@ namespace System.IO.Tests
}
[Fact]
+ public void MoveToSameName()
+ {
+ string testDir = GetTestFilePath();
+ Directory.CreateDirectory(testDir);
+
+ FileInfo testFileSource = new FileInfo(Path.Combine(testDir, GetTestFileName()));
+ testFileSource.Create().Dispose();
+
+ Move(testFileSource.FullName, testFileSource.FullName);
+ Assert.True(File.Exists(testFileSource.FullName));
+ }
+
+ [Fact]
+ public void MoveToSameNameDifferentCasing()
+ {
+ string testDir = GetTestFilePath();
+ Directory.CreateDirectory(testDir);
+
+ FileInfo testFileSource = new FileInfo(Path.Combine(testDir, Path.GetRandomFileName().ToLowerInvariant()));
+ testFileSource.Create().Dispose();
+
+ FileInfo testFileDest = new FileInfo(Path.Combine(testFileSource.DirectoryName, testFileSource.Name.ToUpperInvariant()));
+
+ Move(testFileSource.FullName, testFileDest.FullName);
+ Assert.True(File.Exists(testFileDest.FullName));
+ }
+
+ [Fact]
public void MultipleMoves()
{
FileInfo testFileSource = new FileInfo(GetTestFilePath());