-
-
Notifications
You must be signed in to change notification settings - Fork 10
fix(FileSystem): When creating chained sym links of different types MockFileSystem in windows doesn't throw #833
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3c172d9
f7673ff
4650540
ce6c8db
03eb7f3
8f1dd5c
02c9828
5ac6bab
c9742ec
3f4956b
2c47dba
2525706
56ed6f9
b3cff08
445e41a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -558,19 +558,28 @@ public IStorageContainer GetOrCreateContainer( | |
|
|
||
| #if FEATURE_FILESYSTEM_LINK | ||
| /// <inheritdoc cref="IStorage.ResolveLinkTarget(IStorageLocation, bool)" /> | ||
| public IStorageLocation? ResolveLinkTarget(IStorageLocation location, | ||
| bool returnFinalTarget = false) | ||
| public IStorageLocation? ResolveLinkTarget( | ||
| IStorageLocation location, | ||
| bool returnFinalTarget = false | ||
| ) | ||
| { | ||
| if (!_containers.TryGetValue(location, out IStorageContainer? initialContainer) | ||
| || initialContainer.LinkTarget == null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| IStorageLocation? nextLocation = | ||
| _fileSystem.Storage.GetLocation(initialContainer.LinkTarget); | ||
| IStorageLocation? nextLocation | ||
| = _fileSystem.Storage.GetLocation(initialContainer.LinkTarget); | ||
|
|
||
| if (!_containers.TryGetValue(nextLocation, out IStorageContainer? container)) | ||
| { | ||
| return nextLocation; | ||
| } | ||
|
|
||
| ThrowOnLinkTypeChange(initialContainer, location, container); | ||
|
|
||
| if (returnFinalTarget && _containers.TryGetValue(nextLocation, out IStorageContainer? container) && container.LinkTarget != null) | ||
| if (returnFinalTarget && container.LinkTarget != null) | ||
| { | ||
| nextLocation = ResolveFinalLinkTarget(container, location); | ||
| } | ||
|
|
@@ -755,7 +764,7 @@ private void CheckAndAdjustParentDirectoryTimes(IStorageLocation location) | |
| catch (UnauthorizedAccessException) | ||
| { | ||
| // On Unix, if the parent directory is not writable, we include the child path in the exception. | ||
| throw ExceptionFactory.UnixFileModeAccessDenied(location.FullPath); | ||
| throw ExceptionFactory.AccessDenied(location.FullPath); | ||
| } | ||
| #else | ||
| using (parentContainer.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) | ||
|
|
@@ -1022,11 +1031,14 @@ private bool IncludeItemInEnumeration( | |
| } | ||
|
|
||
| #if FEATURE_FILESYSTEM_LINK | ||
| private IStorageLocation? ResolveFinalLinkTarget(IStorageContainer container, | ||
| IStorageLocation originalLocation) | ||
| private IStorageLocation? ResolveFinalLinkTarget( | ||
| IStorageContainer container, | ||
| IStorageLocation originalLocation | ||
| ) | ||
| { | ||
| int maxResolveLinks = _fileSystem.Execute.IsWindows ? 63 : 40; | ||
| IStorageLocation? nextLocation = null; | ||
|
|
||
| for (int i = 1; i < maxResolveLinks; i++) | ||
| { | ||
| if (container.LinkTarget == null) | ||
|
|
@@ -1035,23 +1047,51 @@ private bool IncludeItemInEnumeration( | |
| } | ||
|
|
||
| nextLocation = _fileSystem.Storage.GetLocation(container.LinkTarget); | ||
| if (!_containers.TryGetValue(nextLocation, | ||
| out IStorageContainer? nextContainer)) | ||
|
|
||
| if (!_containers.TryGetValue(nextLocation, out IStorageContainer? nextContainer)) | ||
| { | ||
| return nextLocation; | ||
| } | ||
|
|
||
| ThrowOnLinkTypeChange(container, originalLocation, nextContainer); | ||
|
|
||
| container = nextContainer; | ||
| } | ||
|
|
||
| if (container.LinkTarget != null) | ||
| { | ||
| throw ExceptionFactory.FileNameCannotBeResolved( | ||
| originalLocation.FullPath); | ||
| originalLocation.FullPath, _fileSystem.Execute.IsWindows ? -2147022975 : -2146232800 | ||
|
||
| ); | ||
| } | ||
|
|
||
| return nextLocation; | ||
| } | ||
|
|
||
| private void ThrowOnLinkTypeChange( | ||
| IStorageContainer previous, | ||
| IStorageLocation previousLocation, | ||
| IStorageContainer next | ||
| ) | ||
| { | ||
| if (!_fileSystem.Execute.IsWindows) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (previous.Type == next.Type) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| switch (previous.Type) | ||
| { | ||
| case FileSystemTypes.File: | ||
| throw ExceptionFactory.AccessDenied(previousLocation.FullPath); | ||
| case FileSystemTypes.Directory: | ||
| throw ExceptionFactory.InvalidDirectoryName(previousLocation.FullPath); | ||
| } | ||
| } | ||
| #endif | ||
|
|
||
| /// <summary> | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,24 +1,51 @@ | ||||||
| #if FEATURE_FILESYSTEM_LINK | ||||||
| using System.IO; | ||||||
| using System.Text.RegularExpressions; | ||||||
|
|
||||||
| namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; | ||||||
|
|
||||||
| [FileSystemTests] | ||||||
| public partial class ResolveLinkTargetTests | ||||||
| { | ||||||
| #region Test Setup | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// The maximum number of symbolic links that are followed.<br /> | ||||||
| /// <see href="https://learn.microsoft.com/en-us/dotnet/api/system.io.directory.resolvelinktarget?view=net-6.0#remarks" /> | ||||||
| /// </summary> | ||||||
| private int MaxResolveLinks => Test.RunsOnWindows ? 63 : 40; | ||||||
|
|
||||||
| #endregion | ||||||
|
|
||||||
| [Theory] | ||||||
| [AutoData] | ||||||
| public async Task ResolveLinkTarget_ShouldThrow(string path) | ||||||
| public async Task ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( | ||||||
| string path, | ||||||
| string pathToFinalTarget | ||||||
| ) | ||||||
| { | ||||||
| IFileSystemInfo link = FileSystem.Directory.CreateSymbolicLink(path, path + "-start"); | ||||||
| int maxLinks = MaxResolveLinks + 1; | ||||||
| FileSystem.Directory.CreateDirectory(pathToFinalTarget); | ||||||
| string previousPath = pathToFinalTarget; | ||||||
|
|
||||||
| for (int i = 0; i < maxLinks; i++) | ||||||
| { | ||||||
| string newPath = $"{path}-{i}"; | ||||||
| IDirectoryInfo linkDirectoryInfo = FileSystem.DirectoryInfo.New(newPath); | ||||||
| linkDirectoryInfo.CreateAsSymbolicLink(previousPath); | ||||||
| previousPath = newPath; | ||||||
| } | ||||||
|
|
||||||
| IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New(previousPath); | ||||||
|
|
||||||
| // UNIX allows 43 and Windows 63 nesting, so 70 is plenty to force the exception | ||||||
| for (int i = 0; i < 70; i++) | ||||||
| void Act() | ||||||
| { | ||||||
| link = FileSystem.Directory.CreateSymbolicLink($"{path}{i}", link.Name); | ||||||
| _ = directoryInfo.ResolveLinkTarget(true); | ||||||
| } | ||||||
|
|
||||||
| await That(() => link.ResolveLinkTarget(true)).Throws<IOException>(); | ||||||
| await That(Act).Throws<IOException>() | ||||||
| .WithHResult(Test.RunsOnWindows ? -2147022975 : -2146232800).And | ||||||
|
||||||
| .WithHResult(Test.RunsOnWindows ? -2147022975 : -2146232800).And | |
| .WithHResult(Test.RunsOnWindows ? TestConstants.HResultTooManySymbolicLinksWindows : TestConstants.HResultTooManySymbolicLinksNonWindows).And |
Uh oh!
There was an error while loading. Please reload this page.