Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.5.0
uses: gittools/actions/gitversion/setup@v4.7.0
with:
versionSpec: '6.x'

- name: Determine Version
uses: gittools/actions/gitversion/execute@v4.5.0
uses: gittools/actions/gitversion/execute@v4.7.0
with:
useConfigFile: true
id: gitversion
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ jobs:
# from git history + GitVersion.yml during build. We still run the CLI here to
# capture the SemVer for use in subsequent steps (push, template dispatch).
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.5.0
uses: gittools/actions/gitversion/setup@v4.7.0
with:
versionSpec: '6.x'

- name: Determine Version
uses: gittools/actions/gitversion/execute@v4.5.0
uses: gittools/actions/gitversion/execute@v4.7.0
with:
useConfigFile: true
id: gitversion # step id used as reference for output values
Expand Down
8 changes: 4 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="5.3.0" />
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="5.6.0" />
<PackageVersion Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.203" />
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.14.0" />
Expand All @@ -17,13 +17,13 @@
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.9" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageVersion Include="Spectre.Console" Version="0.57.1" />
<PackageVersion Include="Spectre.Console" Version="0.57.2" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
Expand Down Expand Up @@ -52,7 +52,7 @@
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="ReportGenerator" Version="5.5.9" />
<PackageVersion Include="coverlet.collector" Version="10.0.0" />
<PackageVersion Include="GitVersion.MsBuild" Version="6.7.0" />
<PackageVersion Include="GitVersion.MsBuild" Version="6.8.1" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PackageVersion Include="Terminal.Gui" Version="2.0.0" />
Expand Down
12 changes: 11 additions & 1 deletion Terminal.Gui/FileServices/FileSystemTreeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,15 @@ private IEnumerable<IFileSystemInfo> TryGetChildren (IFileSystemInfo entry)
}
}

internal static bool IsReparsePoint (IFileSystemInfo entry) => (entry.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
internal static bool IsReparsePoint (IFileSystemInfo entry)
{
try
{
return (entry.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
}
catch (Exception)
{
return false; // treat an unreadable entry as not a reparse point / not expandable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail closed when reparse status is unreadable

When Attributes throws for an entry that is actually a symlink/junction, returning false makes both CanExpand/GetChildren and FileDialog.SearchState treat the entry as safe to traverse, so an unreadable reparse point can still be followed and recreate the directory-cycle risk this guard is meant to prevent. In this error path it is safer to treat the status as reparse/unknown and not expand it.

Useful? React with 👍 / 👎.

}
}
}
47 changes: 38 additions & 9 deletions Terminal.Gui/Views/FileDialogs/FileDialogState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ namespace Terminal.Gui.Views;

internal class FileDialogState
{
// AttributesToSkip defaults to Hidden | System; clear it so hidden/system entries (e.g. dotfiles on Unix)
// remain visible, matching the behavior of the GetDirectories ()/GetFileSystemInfos () calls this replaced.
private static readonly EnumerationOptions _ignoreInaccessibleEnumerationOptions = new () { IgnoreInaccessible = true, AttributesToSkip = FileAttributes.None };

public FileDialogState (IDirectoryInfo dir, FileDialog parent)
{
Parent = parent;
Expand Down Expand Up @@ -60,17 +64,31 @@ private void AddReadableChildren (List<FileSystemInfoStats> children, IDirectory
{
try
{
IEnumerable<IFileSystemInfo> entries;

// if directories only
if (Parent.OpenMode == OpenMode.Directory)
{
entries = dir.GetDirectories ();
}
else
foreach (IFileSystemInfo entry in EnumerateReadableEntries (dir))
{
entries = dir.GetFileSystemInfos ();
AddReadableChild (children, entry);
}
}
catch (NotSupportedException)
{
// Some IFileSystem implementations (e.g. System.IO.Abstractions.TestingHelpers MockFileSystem)
// do not support clearing EnumerationOptions.AttributesToSkip; fall back to the eager listing
// methods, which never skip hidden/system entries.
AddReadableChildrenEagerly (children, dir);
}
catch (Exception)
{
// Access permission exceptions, missing directories, etc.
}
}

private void AddReadableChildrenEagerly (List<FileSystemInfoStats> children, IDirectoryInfo dir)
{
try
{
IEnumerable<IFileSystemInfo> entries = Parent.OpenMode == OpenMode.Directory
? dir.GetDirectories ()
: dir.GetFileSystemInfos ();

foreach (IFileSystemInfo entry in entries)
{
Expand All @@ -83,6 +101,17 @@ private void AddReadableChildren (List<FileSystemInfoStats> children, IDirectory
}
}

private IEnumerable<IFileSystemInfo> EnumerateReadableEntries (IDirectoryInfo dir)
{
// if directories only
if (Parent.OpenMode == OpenMode.Directory)
{
return dir.EnumerateDirectories ("*", _ignoreInaccessibleEnumerationOptions);
}

return dir.EnumerateFileSystemInfos ("*", _ignoreInaccessibleEnumerationOptions);
}

private void AddReadableChild (List<FileSystemInfoStats> children, IFileSystemInfo entry)
{
try
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.IO.Abstractions;
using Moq;

namespace FileServicesTests;

public class FileSystemTreeBuilderTests
{
[Fact]
public void CanExpand_DirectoryWithUnreadableAttributes_DoesNotThrowAndReturnsFalse ()
{
Mock<IDirectoryInfo> directory = new ();
directory.SetupGet (d => d.Attributes).Throws (new UnauthorizedAccessException ("Access denied"));
directory.SetupGet (d => d.Exists).Returns (true);
directory.Setup (d => d.GetFileSystemInfos ()).Returns ([]);

FileSystemTreeBuilder builder = new ();

bool canExpand = builder.CanExpand (directory.Object);

Assert.False (canExpand);
}

[Fact]
public void GetChildren_DirectoryWithUnreadableAttributes_DoesNotThrowAndReturnsEmpty ()
{
Mock<IDirectoryInfo> directory = new ();
directory.SetupGet (d => d.Attributes).Throws (new UnauthorizedAccessException ("Access denied"));
directory.SetupGet (d => d.Exists).Returns (true);
directory.Setup (d => d.GetFileSystemInfos ()).Returns ([]);

FileSystemTreeBuilder builder = new ();

IEnumerable<IFileSystemInfo> children = builder.GetChildren (directory.Object);

Assert.Empty (children);
}
}
Loading
Loading