* Fix ConvertDirent for AIX by always returning PAL_DT_UNKNOWN
I commited the sin of untested code, as this function wasn't wired
up to Mono's BCL until a few days ago, and I had triggered the
situation where I needed this working by purging stale build
artifacts.
What happened here was me being too clever; I tried stat, but
we only have the file name, and from the unmanaged call stack this
would run in, we'd have no way to get the directory it's in
(without butchering the function signature) to properly use stat
for non-cwd directories. This meant that the check would get random
garbage on the stack; sometimes the data for "..", sometimes
another function would clobber it, and it'd read that. This caused
the Mono gensources tool to act erratically, preventing the BCL
from building.
Fix this by always returning DT_UNKNOWN. This isn't ideal, but the
BCL seems to handle this with grace and does a stat afterwards
to properly fill in data. It's enough to get the Mono build from
scratch working again. (The stat bit was verified by me running the
syscall tracing tool, `truss` over it, plus Mono's JIT trace.)
* Handle DT_UNKNOWN cases in FileSystemEntry.Initialize
On some OSes like AIX, we return DT_UNKNOWN as a sentinel value,
because they don't have a type field in dirent. If so, or the OS
does return a type and we got an unknown, also set symlink status
if so when making the managed equivalent structures.
TimeZoneInfo.Unix also calls ReadDir and thus ConvertDirent, but
it does this logic already.
* Move symlink check to its own if-elif block
Commit migrated from https://github.com/dotnet/corefx/commit/
c34dd2fb843c45ad135ef2b0348c06224e15c736
// location of the start of the string that exists in their own byte buffer.
outputEntry->Name = entry->d_name;
#if !defined(DT_UNKNOWN)
- /* AIX has no d_type, make a substitute */
- struct stat s;
- stat(entry->d_name, &s);
- if (S_ISDIR(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_DIR;
- }
- else if (S_ISFIFO(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_FIFO;
- }
- else if (S_ISCHR(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_CHR;
- }
- else if (S_ISBLK(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_BLK;
- }
- else if (S_ISREG(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_REG;
- }
- else if (S_ISLNK(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_LNK;
- }
- else if (S_ISSOCK(s.st_mode))
- {
- outputEntry->InodeType = PAL_DT_SOCK;
- }
- else
- {
- outputEntry->InodeType = PAL_DT_UNKNOWN;
- }
+ // AIX has no d_type, and since we can't get the directory that goes with
+ // the filename from ReadDir, we can't stat the file. Return unknown and
+ // hope that managed code can properly stat the file.
+ outputEntry->InodeType = PAL_DT_UNKNOWN;
#else
outputEntry->InodeType = (int32_t)entry->d_type;
#endif
// IMPORTANT: Attribute logic must match the logic in FileStatus
bool isDirectory = false;
+ bool isSymlink = false;
if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_DIR)
{
// We know it's a directory.
isDirectory = true;
}
+ // 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
+ // directory.
else if ((directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK
|| directoryEntry.InodeType == Interop.Sys.NodeType.DT_UNKNOWN)
&& Interop.Sys.Stat(entry.FullPath, out Interop.Sys.FileStatus targetStatus) >= 0)
// Symlink or unknown: Stat to it to see if we can resolve it to a directory.
isDirectory = (targetStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
}
+ // Same idea as the directory check, just repeated for (and tweaked due to the
+ // nature of) symlinks.
+ if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK)
+ {
+ isSymlink = true;
+ }
+ else if ((directoryEntry.InodeType == Interop.Sys.NodeType.DT_UNKNOWN)
+ && (Interop.Sys.LStat(entry.FullPath, out Interop.Sys.FileStatus linkTargetStatus) >= 0))
+ {
+ isSymlink = (linkTargetStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFLNK;
+ }
entry._status = default;
FileStatus.Initialize(ref entry._status, isDirectory);
FileAttributes attributes = default;
- if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK)
+ if (isSymlink)
attributes |= FileAttributes.ReparsePoint;
if (isDirectory)
attributes |= FileAttributes.Directory;