diff options
author | Mitchell Hwang <mitchhwang1418@gmail.com> | 2020-04-02 21:13:42 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-02 21:13:42 +0300 |
commit | bec934370587bdbe8a9e4305a5e9167dd7b63677 (patch) | |
tree | 0235ff7f1413edb34863f4ff1800d3b2aebd6555 | |
parent | 7c9e2158925339a5a96ff8d09d21c600a0b27e5a (diff) |
FileSystem.Unix.File.Move uses "rename" in more cases (#40611) (#395)
* FileSystem.Unix.File.Move uses "rename" in more cases (#40611)
* FileSystem.Unix.File.Move use rename in more cases, avoid link/copy when possible, improve performance on file systems that do not support hard links, such as FAT
* Adapt Unit Tests for accounting FileSystemWatcher events fired by FileSystem.Unix.File.Move implementation that use rename in more cases, avoiding link/copy when possible
* [FileSystem] ReAdd check for same device
Co-authored-by: Sylvain <sf@ellisys.com>
Co-authored-by: Mitchell Hwang <mihw@microsoft.com>
-rw-r--r-- | src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs | 8 | ||||
-rw-r--r-- | src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs | 21 |
2 files changed, 15 insertions, 14 deletions
diff --git a/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs b/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs index be9d25490f..e772c85dae 100644 --- a/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs +++ b/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs @@ -22,7 +22,7 @@ namespace System.IO.Tests [PlatformSpecific(TestPlatforms.AnyUnix)] // Expected WatcherChangeTypes are different based on OS public void Unix_File_Move_To_Same_Directory() { - FileMove_SameDirectory(WatcherChangeTypes.Created | WatcherChangeTypes.Deleted); + FileMove_SameDirectory(WatcherChangeTypes.Renamed); } [Fact] @@ -73,7 +73,7 @@ namespace System.IO.Tests [PlatformSpecific(TestPlatforms.OSX)] // Expected WatcherChangeTypes are different based on OS public void OSX_File_Move_To_Different_Watched_Directory() { - FileMove_DifferentWatchedDirectory(WatcherChangeTypes.Changed); + FileMove_DifferentWatchedDirectory(0); } [Fact] @@ -104,7 +104,7 @@ namespace System.IO.Tests [PlatformSpecific(TestPlatforms.AnyUnix)] // Expected WatcherChangeTypes are different based on OS public void Unix_File_Move_In_Nested_Directory(bool includeSubdirectories) { - FileMove_NestedDirectory(includeSubdirectories ? WatcherChangeTypes.Created | WatcherChangeTypes.Deleted : 0, includeSubdirectories); + FileMove_NestedDirectory(includeSubdirectories ? WatcherChangeTypes.Renamed : 0, includeSubdirectories); } [Fact] @@ -118,7 +118,7 @@ namespace System.IO.Tests [PlatformSpecific(TestPlatforms.AnyUnix)] // Expected WatcherChangeTypes are different based on OS public void Unix_File_Move_With_Set_NotifyFilter() { - FileMove_WithNotifyFilter(WatcherChangeTypes.Deleted); + FileMove_WithNotifyFilter(WatcherChangeTypes.Renamed); } #region Test Helpers diff --git a/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs b/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs index b2007ca32d..94b4557643 100644 --- a/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs +++ b/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs @@ -160,18 +160,19 @@ 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. 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. + // link/unlink are used instead. Rename is more efficient than link/unlink on file systems + // where hard links are not supported (such as FAT). Therefore, given that source file exists, + // rename is used in 2 cases: when dest file does not exist or when source path and dest + // path refer to the same file (on the same device). 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.LStat(destFullPath, out destStat) != 0 || // dest file does not exist + (sourceStat.Dev == destStat.Dev && // source and dest are on the same device + sourceStat.Ino == destStat.Ino)) && // source and dest are the same file on that device Interop.Sys.Rename(sourceFullPath, destFullPath) == 0) // try the rename { // Renamed successfully. |