Skip to content
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

FileSystemEntry.Unix: ensure properties are available when file is deleted. #60214

Merged
merged 12 commits into from
Nov 18, 2021
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.
carlossanlop marked this conversation as resolved.
Show resolved Hide resolved
if (isUnknown)
if (isDirectory)
{
isSymlink = entry.IsSymbolicLink;
// Need to fail silently in case we are enumerating
isDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
carlossanlop marked this conversation as resolved.
Show resolved Hide resolved
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;
}
carlossanlop marked this conversation as resolved.
Show resolved Hide resolved
}

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
tmds marked this conversation as resolved.
Show resolved Hide resolved
{
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.
tmds marked this conversation as resolved.
Show resolved Hide resolved
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