From: David CantĂș Date: Tue, 10 Aug 2021 14:37:24 +0000 (-0700) Subject: Handle FileNotFound for symlinks when using polling (#56915) X-Git-Tag: submit/tizen/20220110.044913~519 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a57a9ece44b3424d626a9bb24e7d3c122f55f901;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Handle FileNotFound for symlinks when using polling (#56915) * Handle FileNotFound for symlinks when using polling * Re-enable tests * Use CTS instead of Task.Wait(TimeSpan) to fix issues in browser. --- diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs index 5ae854ae6ef..ddd21332c73 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs @@ -45,16 +45,28 @@ namespace Microsoft.Extensions.FileProviders.Physical // If file is a link, and link target does not exists, return DateTime.MinValue // since the link's LastWriteTimeUtc doesn't convey anything for this scenario. // If file is not a link, return null to inform the caller that file is not a link. - public static DateTime? GetFileLinkTargetLastWriteTimeUtc(FileInfo fileInfo) + public static DateTime? GetFileLinkTargetLastWriteTimeUtc(FileInfo fileInfo, bool isSecondTry = false) { #if NETCOREAPP Debug.Assert(fileInfo.Exists); if (fileInfo.LinkTarget != null) { - FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); - if (targetInfo.Exists) + try { - return targetInfo.LastWriteTimeUtc; + FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); + if (targetInfo.Exists) + { + return targetInfo.LastWriteTimeUtc; + } + } + catch (FileNotFoundException) + { + // The file ceased to exist between LinkTarget and ResolveLinkTarget. + // Try one more time, if it fails again just give up. + if (!isSecondTry) + { + GetFileLinkTargetLastWriteTimeUtc(fileInfo, isSecondTry: true); + } } return DateTime.MinValue; diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 80e94fc31f9..e1480ab8686 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1540,7 +1540,6 @@ namespace Microsoft.Extensions.FileProviders [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) { // Arrange @@ -1550,25 +1549,27 @@ namespace Microsoft.Extensions.FileProviders File.WriteAllText(filePath, "v1.1"); using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; - IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); + IChangeToken changeToken = provider.Watch(useWildcard ? "*" : fileName); - var tcs = new TaskCompletionSource(); - token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); + var tcs = new TaskCompletionSource(); + changeToken.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + cts.Token.Register(() => tcs.TrySetCanceled()); // Act await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + Assert.True(await tcs.Task, $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}"); } [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) { // Arrange using var root = new DisposableFileSystem(); @@ -1578,16 +1579,19 @@ namespace Microsoft.Extensions.FileProviders string filter = useWildcard ? "*" : fileName; using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; - IChangeToken token = provider.Watch(filter); + IChangeToken changeToken = provider.Watch(filter); + + var tcs = new TaskCompletionSource(); + changeToken.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null); - var tcs = new TaskCompletionSource(); - token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + cts.Token.Register(() => tcs.TrySetCanceled()); // Act File.Delete(filePath); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + Assert.True(await tcs.Task, $"Change event was not raised - current time: {DateTime.UtcNow:O}, file Exists: {File.Exists(filePath)}."); } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index 7ccfafb2818..c25b3b5e0e3 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Primitives; using Xunit; @@ -14,7 +15,6 @@ namespace Microsoft.Extensions.FileProviders [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) { // Arrange @@ -30,23 +30,25 @@ namespace Microsoft.Extensions.FileProviders using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); - var tcs = new TaskCompletionSource(); - token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + cts.Token.Register(() => tcs.TrySetCanceled()); // Act await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + Assert.True(await tcs.Task, $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); } [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard) { // Arrange using var rootOfLink = new DisposableFileSystem(); @@ -59,11 +61,12 @@ namespace Microsoft.Extensions.FileProviders IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); var tcs = new TaskCompletionSource(); - token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + token.RegisterChangeCallback(_ => { Assert.True(false, "Change event was raised when it was not expected."); }, null); - // Assert - Assert.False(tcs.Task.Wait(TimeSpan.FromSeconds(30)), - "Change event was raised when it was not expected."); + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + cts.Token.Register(() => tcs.TrySetCanceled()); + + await Assert.ThrowsAsync(() => tcs.Task); } [Theory] @@ -71,7 +74,6 @@ namespace Microsoft.Extensions.FileProviders [InlineData(false, true)] [InlineData(true, false)] [InlineData(true, true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool linkWasBroken) { // Arrange @@ -96,23 +98,25 @@ namespace Microsoft.Extensions.FileProviders using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(filter); - var tcs = new TaskCompletionSource(); - token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + cts.Token.Register(() => tcs.TrySetCanceled()); // Act - Change link target to file 2. File.Delete(linkPath); File.CreateSymbolicLink(linkPath, file2Path); // Assert - It should report the change regardless of the timestamp being older. - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + Assert.True(await tcs.Task, $"Change event was not raised - current time: {DateTime.UtcNow:O}, file1 LastWriteTimeUtc: {File.GetLastWriteTimeUtc(file1Path):O}, file2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path):O}."); } [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) { // Arrange using var rootOfFile = new DisposableFileSystem(); @@ -129,14 +133,17 @@ namespace Microsoft.Extensions.FileProviders using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(filter); - var tcs = new TaskCompletionSource(); - token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + cts.Token.Register(() => tcs.TrySetCanceled()); // Act File.Delete(linkPath); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + Assert.True(await tcs.Task, $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); } }