/
FileSystemEntry.Unix.cs
172 lines (148 loc) · 6.71 KB
/
FileSystemEntry.Unix.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace System.IO.Enumeration
{
/// <summary>
/// Lower level view of FileSystemInfo used for processing and filtering find results.
/// </summary>
public unsafe ref partial struct FileSystemEntry
{
private Interop.Sys.DirectoryEntry _directoryEntry;
private bool _isDirectory;
private FileStatus _status;
private Span<char> _pathBuffer;
private ReadOnlySpan<char> _fullPath;
private ReadOnlySpan<char> _fileName;
private FileNameBuffer _fileNameBuffer;
// Wrap the fixed buffer to workaround visibility issues in api compat verification
private struct FileNameBuffer
{
internal fixed char _buffer[Interop.Sys.DirectoryEntry.NameBufferSize];
}
internal static FileAttributes Initialize(
ref FileSystemEntry entry,
Interop.Sys.DirectoryEntry directoryEntry,
ReadOnlySpan<char> directory,
ReadOnlySpan<char> rootDirectory,
ReadOnlySpan<char> originalRootDirectory,
Span<char> pathBuffer)
{
entry._directoryEntry = directoryEntry;
entry.Directory = directory;
entry.RootDirectory = rootDirectory;
entry.OriginalRootDirectory = originalRootDirectory;
entry._pathBuffer = pathBuffer;
entry._fullPath = ReadOnlySpan<char>.Empty;
entry._fileName = ReadOnlySpan<char>.Empty;
entry._isDirectory = false;
entry._status.InvalidateCaches();
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;
if (isDirectory)
{
entry._isDirectory = true;
}
else if (isSymlink)
{
entry._isDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
}
else if (isUnknown)
{
entry._isDirectory = entry._status.IsDirectory(entry.FullPath, continueOnError: true);
if (entry._status.IsSymbolicLink(entry.FullPath, continueOnError: true))
{
entry._directoryEntry.InodeType = Interop.Sys.NodeType.DT_LNK;
}
}
FileAttributes attributes = default;
if (entry.IsSymbolicLink)
attributes |= FileAttributes.ReparsePoint;
if (entry.IsDirectory)
attributes |= FileAttributes.Directory;
return attributes;
}
private ReadOnlySpan<char> FullPath
{
get
{
if (_fullPath.Length == 0)
{
Debug.Assert(Directory.Length + FileName.Length < _pathBuffer.Length,
$"directory ({Directory.Length} chars) & name ({Directory.Length} chars) too long for buffer ({_pathBuffer.Length} chars)");
Path.TryJoin(Directory, FileName, _pathBuffer, out int charsWritten);
Debug.Assert(charsWritten > 0, "didn't write any chars to buffer");
_fullPath = _pathBuffer.Slice(0, charsWritten);
}
return _fullPath;
}
}
public ReadOnlySpan<char> FileName
{
get
{
if (_directoryEntry.NameLength != 0 && _fileName.Length == 0)
{
Span<char> buffer = MemoryMarshal.CreateSpan(ref _fileNameBuffer._buffer[0], Interop.Sys.DirectoryEntry.NameBufferSize);
_fileName = _directoryEntry.GetName(buffer);
}
return _fileName;
}
}
/// <summary>
/// The full path of the directory this entry resides in.
/// </summary>
public ReadOnlySpan<char> Directory { get; private set; }
/// <summary>
/// The full path of the root directory used for the enumeration.
/// </summary>
public ReadOnlySpan<char> RootDirectory { get; private set; }
/// <summary>
/// The root directory for the enumeration as specified in the constructor.
/// </summary>
public ReadOnlySpan<char> OriginalRootDirectory { get; private set; }
// 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
{
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.IsFileSystemEntryHidden(FullPath, FileName);
internal bool IsReadOnly => _status.IsReadOnly(FullPath, continueOnError: true);
public bool IsDirectory => _isDirectory;
internal bool IsSymbolicLink => _directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK;
public FileSystemInfo ToFileSystemInfo()
{
string fullPath = ToFullPath();
return FileSystemInfo.Create(fullPath, new string(FileName), _isDirectory, ref _status);
}
/// <summary>
/// Returns the full path of the find result.
/// </summary>
public string ToFullPath() =>
new string(FullPath);
}
}