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:
authorIan Hays <ianha@microsoft.com>2017-08-05 00:23:19 +0300
committerIan Hays <ianha@microsoft.com>2017-08-05 00:23:19 +0300
commit541f8e1535466c94b798e4e0177b834d1c3210da (patch)
treeda071965b5aa3ba162701bb3a74730d1535cfe76 /src
parent5580a80d8deaeae31b5a5021296af90be28559be (diff)
Add FSW file/directory event filtering to Linux
Because we are using inotify as the underlying implemenation for FileSystemWatcher on Linux, we can not only raise events for files or only raise events for directories, so we have to allow events for both to be raised regardless of NotifyFilters. However, inotify does tell us that if an event occurred on a directory or on a file after the event has been raised. Since this is after filtering has already been done, we forward that event regardless of the NotifyFilters. This commit passes NotifyFilters to the RunningInstance that forwards the events and adds in some manual filtering after the fact. It isn't quite the same as filtering through the native call, but it does bring us closer to the Windows behavior.
Diffstat (limited to 'src')
-rw-r--r--src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs29
-rw-r--r--src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs32
-rw-r--r--src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs32
3 files changed, 80 insertions, 13 deletions
diff --git a/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs b/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs
index 6de6aba533..04cdfb9c2a 100644
--- a/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs
+++ b/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs
@@ -69,7 +69,7 @@ namespace System.IO
// active operations to all be outstanding at the same time.
var runner = new RunningInstance(
this, handle, _directory,
- IncludeSubdirectories, TranslateFilters(NotifyFilter), cancellation.Token);
+ IncludeSubdirectories, NotifyFilter, cancellation.Token);
// Now that we've created the runner, store the cancellation object and mark the instance
// as running. We wait to do this so that if there was a failure, StartRaisingEvents
@@ -256,7 +256,8 @@ namespace System.IO
/// <summary>
/// Filters to use when adding a watch on directories.
/// </summary>
- private readonly Interop.Sys.NotifyEvents _notifyFilters;
+ private readonly NotifyFilters _notifyFilters;
+ private readonly Interop.Sys.NotifyEvents _watchFilters;
/// <summary>
/// Whether to monitor subdirectories. Unlike Win32, inotify does not implicitly monitor subdirectories;
/// watches must be explicitly added for those subdirectories.
@@ -282,7 +283,7 @@ namespace System.IO
/// <summary>Initializes the instance with all state necessary to operate a watch.</summary>
internal RunningInstance(
FileSystemWatcher watcher, SafeFileHandle inotifyHandle, string directoryPath,
- bool includeSubdirectories, Interop.Sys.NotifyEvents notifyFilters, CancellationToken cancellationToken)
+ bool includeSubdirectories, NotifyFilters notifyFilters, CancellationToken cancellationToken)
{
Debug.Assert(watcher != null);
Debug.Assert(inotifyHandle != null && !inotifyHandle.IsInvalid && !inotifyHandle.IsClosed);
@@ -295,6 +296,7 @@ namespace System.IO
Debug.Assert(_buffer != null && _buffer.Length > (c_INotifyEventSize + NAME_MAX + 1));
_includeSubdirectories = includeSubdirectories;
_notifyFilters = notifyFilters;
+ _watchFilters = TranslateFilters(notifyFilters);
_cancellationToken = cancellationToken;
// Add a watch for this starting directory. We keep track of the watch descriptor => directory information
@@ -358,7 +360,7 @@ namespace System.IO
// the existing descriptor. This works even in the case of a rename. We also add the DONT_FOLLOW
// and EXCL_UNLINK flags to keep parity with Windows where we don't pickup symlinks or unlinked
// files (which don't exist in Windows)
- int wd = Interop.Sys.INotifyAddWatch(_inotifyHandle, fullPath, (uint)(this._notifyFilters | Interop.Sys.NotifyEvents.IN_DONT_FOLLOW | Interop.Sys.NotifyEvents.IN_EXCL_UNLINK));
+ int wd = Interop.Sys.INotifyAddWatch(_inotifyHandle, fullPath, (uint)(this._watchFilters | Interop.Sys.NotifyEvents.IN_DONT_FOLLOW | Interop.Sys.NotifyEvents.IN_EXCL_UNLINK));
if (wd == -1)
{
// If we get an error when trying to add the watch, don't let that tear down processing. Instead,
@@ -637,10 +639,21 @@ namespace System.IO
AddDirectoryWatch(associatedDirectoryEntry, nextEvent.name);
}
- const Interop.Sys.NotifyEvents switchMask =
- Interop.Sys.NotifyEvents.IN_IGNORED |Interop.Sys.NotifyEvents.IN_CREATE | Interop.Sys.NotifyEvents.IN_DELETE |
- Interop.Sys.NotifyEvents.IN_ACCESS | Interop.Sys.NotifyEvents.IN_MODIFY | Interop.Sys.NotifyEvents.IN_ATTRIB |
- Interop.Sys.NotifyEvents.IN_MOVED_FROM | Interop.Sys.NotifyEvents.IN_MOVED_TO;
+ // Check if the event should have been filtered but was unable because of inotify's inability
+ // to filter files vs directories.
+ const Interop.Sys.NotifyEvents fileDirEvents = Interop.Sys.NotifyEvents.IN_CREATE |
+ Interop.Sys.NotifyEvents.IN_DELETE |
+ Interop.Sys.NotifyEvents.IN_MOVED_FROM |
+ Interop.Sys.NotifyEvents.IN_MOVED_TO;
+ if ((((uint)fileDirEvents & mask) > 0) &&
+ (isDir && ((_notifyFilters & NotifyFilters.DirectoryName) == 0) ||
+ (!isDir && ((_notifyFilters & NotifyFilters.FileName) == 0))))
+ {
+ continue;
+ }
+
+ const Interop.Sys.NotifyEvents switchMask = fileDirEvents | Interop.Sys.NotifyEvents.IN_IGNORED |
+ Interop.Sys.NotifyEvents.IN_ACCESS | Interop.Sys.NotifyEvents.IN_MODIFY | Interop.Sys.NotifyEvents.IN_ATTRIB;
switch ((Interop.Sys.NotifyEvents)(mask & (uint)switchMask))
{
case Interop.Sys.NotifyEvents.IN_CREATE:
diff --git a/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs b/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs
index f390e58913..9cb43b40a9 100644
--- a/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs
+++ b/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs
@@ -87,8 +87,6 @@ namespace System.IO.Tests
WatcherChangeTypes expected = 0;
if (filter == NotifyFilters.DirectoryName)
expected |= WatcherChangeTypes.Renamed;
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && (filter == NotifyFilters.FileName))
- expected |= WatcherChangeTypes.Renamed;
ExpectEvent(watcher, expected, action, cleanup, targetPath);
}
@@ -255,5 +253,35 @@ namespace System.IO.Tests
ExpectEvent(watcher, expected, action, cleanup, new string[] { otherDir, dir.Path });
}
}
+
+ [Fact]
+ public void FileSystemWatcher_Directory_NotifyFilter_DirectoryNameDoesntTriggerOnFileEvent()
+ {
+ using (var testDirectory = new TempDirectory(GetTestFilePath()))
+ using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir")))
+ using (var watcher = new FileSystemWatcher(testDirectory.Path, "*"))
+ {
+ watcher.NotifyFilter = NotifyFilters.FileName;
+ string renameDirSource = Path.Combine(testDirectory.Path, "dir2_source");
+ string renameDirDest = Path.Combine(testDirectory.Path, "dir2_dest");
+ string otherDir = Path.Combine(testDirectory.Path, "dir3");
+ Directory.CreateDirectory(renameDirSource);
+
+ Action action = () =>
+ {
+ Directory.CreateDirectory(otherDir);
+ Directory.Move(renameDirSource, renameDirDest);
+ Directory.SetLastWriteTime(dir.Path, DateTime.Now + TimeSpan.FromSeconds(10));
+ Directory.Delete(otherDir);
+ };
+ Action cleanup = () =>
+ {
+ Directory.Move(renameDirDest, renameDirSource);
+ };
+
+ WatcherChangeTypes expected = 0;
+ ExpectEvent(watcher, expected, action, cleanup, new string[] { otherDir, dir.Path });
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs b/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs
index 30789b2546..fa88b20704 100644
--- a/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs
+++ b/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs
@@ -91,9 +91,6 @@ namespace System.IO.Tests
if (filter == NotifyFilters.DirectoryName)
expected |= WatcherChangeTypes.Renamed;
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && (filter == NotifyFilters.FileName))
- expected |= WatcherChangeTypes.Renamed;
-
ExpectEvent(watcher, expected, action, cleanup, targetPath);
}
}
@@ -281,5 +278,34 @@ namespace System.IO.Tests
ExpectEvent(watcher, expected, action, cleanup, new string[] { otherFile, file.Path });
}
}
+
+ [Fact]
+ public void FileSystemWatcher_File_NotifyFilter_FileNameDoesntTriggerOnDirectoryEvent()
+ {
+ using (var testDirectory = new TempDirectory(GetTestFilePath()))
+ using (var file = new TempFile(Path.Combine(testDirectory.Path, "file")))
+ using (var sourcePath = new TempFile(Path.Combine(testDirectory.Path, "sourceFile")))
+ using (var watcher = new FileSystemWatcher(testDirectory.Path, "*"))
+ {
+ watcher.NotifyFilter = NotifyFilters.DirectoryName;
+ string otherFile = Path.Combine(testDirectory.Path, "file2");
+ string destPath = Path.Combine(testDirectory.Path, "destFile");
+
+ Action action = () =>
+ {
+ File.Create(otherFile).Dispose();
+ File.SetLastWriteTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10));
+ File.Delete(otherFile);
+ File.Move(sourcePath.Path, destPath);
+ };
+ Action cleanup = () =>
+ {
+ File.Move(destPath, sourcePath.Path);
+ };
+
+ WatcherChangeTypes expected = 0;
+ ExpectEvent(watcher, expected, action, cleanup, new string[] { otherFile, file.Path });
+ }
+ }
}
} \ No newline at end of file