Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void FileAttributesAreExpected()
fileOne.Attributes &= ~(FileAttributes.Archive | FileAttributes.NotContentIndexed);
}

using (var enumerator = new DefaultFileAttributes(testDirectory.FullName, new EnumerationOptions()))
using (var enumerator = new DefaultFileAttributes(testDirectory.FullName, new EnumerationOptions() { AttributesToSkip = 0 }))
{
Assert.True(enumerator.MoveNext());
Assert.Equal(fileOne.Name, enumerator.Current);
Expand Down Expand Up @@ -104,7 +104,7 @@ public void DirectoryAttributesAreExpected()
subDirectory.Attributes &= ~FileAttributes.NotContentIndexed;
}

using (var enumerator = new DefaultDirectoryAttributes(testDirectory.FullName, new EnumerationOptions()))
using (var enumerator = new DefaultDirectoryAttributes(testDirectory.FullName, new EnumerationOptions() { AttributesToSkip = 0 }))
{
Assert.True(enumerator.MoveNext());
Assert.Equal(subDirectory.Name, enumerator.Current);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace System.IO.Enumeration
/// </summary>
public unsafe ref partial struct FileSystemEntry
{
internal Interop.Sys.DirectoryEntry _directoryEntry;
private Interop.Sys.DirectoryEntry _directoryEntry;
private FileStatus _status;
private Span<char> _pathBuffer;
private ReadOnlySpan<char> _fullPath;
Expand All @@ -32,38 +32,34 @@ internal static FileAttributes Initialize(
entry._pathBuffer = pathBuffer;
entry._fullPath = ReadOnlySpan<char>.Empty;
entry._fileName = ReadOnlySpan<char>.Empty;

entry._status.InvalidateCaches();
entry._status.InitiallyDirectory = false;

bool isDirectory = directoryEntry.InodeType == Interop.Sys.NodeType.DT_DIR;
bool isSymlink = directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK;
bool isUnknown = directoryEntry.InodeType == Interop.Sys.NodeType.DT_UNKNOWN;

// Some operating systems don't have the inode type in the dirent structure,
// so we use DT_UNKNOWN as a sentinel value. As such, check if the dirent is a
// symlink or a directory.
if (isUnknown)
if (isDirectory)
{
isSymlink = entry.IsSymbolicLink;
// Need to fail silently in case we are enumerating
isDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
entry._status.InitiallyDirectory = true;
}
// Same idea as the directory check, just repeated for (and tweaked due to the
// nature of) symlinks.
// Whether we had the dirent structure or not, we treat a symlink to a directory as a directory,
// so we need to reflect that in our isDirectory variable.
else if (isSymlink)
{
// Need to fail silently in case we are enumerating
isDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
entry._status.InitiallyDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
}
else if (isUnknown)
{
entry._status.InitiallyDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
if (entry._status.IsSymbolicLink(entry.FullPath, continueOnError: true))
{
entry._directoryEntry.InodeType = Interop.Sys.NodeType.DT_LNK;
}
}

entry._status.InitiallyDirectory = isDirectory;

FileAttributes attributes = default;
if (isSymlink)
if (entry.IsSymbolicLink)
attributes |= FileAttributes.ReparsePoint;
if (isDirectory)
if (entry.IsDirectory)
attributes |= FileAttributes.Directory;

return attributes;
Expand Down Expand Up @@ -119,15 +115,41 @@ public ReadOnlySpan<char> FileName

// Windows never fails getting attributes, length, or time as that information comes back
// with the native enumeration struct. As such we must not throw here.
public FileAttributes Attributes => _status.GetAttributes(FullPath, FileName);
public FileAttributes Attributes
{
get
{
FileAttributes attributes = _status.GetAttributes(FullPath, FileName, continueOnError: true);
if (attributes != (FileAttributes)(-1))
{
return attributes;
}

// File was removed before we retrieved attributes.
// Return what we know.
attributes = default;

if (IsSymbolicLink)
attributes |= FileAttributes.ReparsePoint;

if (IsDirectory)
attributes |= FileAttributes.Directory;

if (FileStatus.IsNameHidden(FileName))
attributes |= FileAttributes.Hidden;

return attributes != default ? attributes : FileAttributes.Normal;
}
}
public long Length => _status.GetLength(FullPath, continueOnError: true);
public DateTimeOffset CreationTimeUtc => _status.GetCreationTime(FullPath, continueOnError: true);
public DateTimeOffset LastAccessTimeUtc => _status.GetLastAccessTime(FullPath, continueOnError: true);
public DateTimeOffset LastWriteTimeUtc => _status.GetLastWriteTime(FullPath, continueOnError: true);
public bool IsHidden => _status.IsHidden(FullPath, FileName, continueOnError: true);
internal bool IsReadOnly => _status.IsReadOnly(FullPath, continueOnError: true);

public bool IsDirectory => _status.InitiallyDirectory;
public bool IsHidden => _status.IsHidden(FullPath, FileName);
internal bool IsReadOnly => _status.IsReadOnly(FullPath);
internal bool IsSymbolicLink => _status.IsSymbolicLink(FullPath);
internal bool IsSymbolicLink => _directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK;

public FileSystemInfo ToFileSystemInfo()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ internal bool IsHidden(ReadOnlySpan<char> path, ReadOnlySpan<char> fileName, boo
return HasHiddenFlag;
}

internal bool IsNameHidden(ReadOnlySpan<char> fileName) => fileName.Length > 0 && fileName[0] == '.';
internal static bool IsNameHidden(ReadOnlySpan<char> fileName) => fileName.Length > 0 && fileName[0] == '.';

// Returns true if the path points to a directory, or if the path is a symbolic link
// that points to a directory
Expand All @@ -139,9 +139,9 @@ internal bool IsSymbolicLink(ReadOnlySpan<char> path, bool continueOnError = fal
return HasSymbolicLinkFlag;
}

internal FileAttributes GetAttributes(ReadOnlySpan<char> path, ReadOnlySpan<char> fileName)
internal FileAttributes GetAttributes(ReadOnlySpan<char> path, ReadOnlySpan<char> fileName, bool continueOnError = false)
{
EnsureCachesInitialized(path);
EnsureCachesInitialized(path, continueOnError);

if (!_exists)
return (FileAttributes)(-1);
Expand Down