File: Pipelines\PipelineOutputDirectory.cs
Web Access
Project: src\src\Aspire.Hosting\Aspire.Hosting.csproj (Aspire.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
 
namespace Aspire.Hosting.Pipelines;
 
/// <summary>
/// Presents directory links as file entries so glob traversal never follows them.
/// </summary>
internal sealed class PipelineOutputDirectory(DirectoryInfo directory, CancellationToken cancellationToken) : DirectoryInfoWrapper(directory)
{
    private readonly DirectoryInfo _directory = directory;
 
    public override IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos()
    {
        cancellationToken.ThrowIfCancellationRequested();
        if (!_directory.Exists || _directory.IsJunctionOrSymlink())
        {
            yield break;
        }
        foreach (var entry in _directory.EnumerateFileSystemInfos())
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (entry is DirectoryInfo child && !child.IsJunctionOrSymlink())
            {
                yield return new PipelineOutputDirectory(child, cancellationToken);
            }
            else
            {
                yield return new FileInfoWrapper(new FileInfo(entry.FullName));
            }
        }
    }
 
    public override DirectoryInfoBase GetDirectory(string path) =>
        new PipelineOutputDirectory(new DirectoryInfo(Path.GetFullPath(path, _directory.FullName)), cancellationToken);
}
 
internal static class PipelineOutputFileSystemExtensions
{
    // FileSystemInfo.Attributes can return -1 for a missing entry, which would look like a link.
    // Use the throwing API so capture preserves file-open failures rather than inventing link state.
    internal static bool IsJunctionOrSymlink(this FileSystemInfo entry) => File.GetAttributes(entry.FullName).IsJunctionOrSymlink();
 
    internal static bool IsJunctionOrSymlink(this FileAttributes attributes) => (attributes & FileAttributes.ReparsePoint) != 0;
 
    internal static bool IsDirectory(this FileAttributes attributes) => (attributes & FileAttributes.Directory) != 0;
}