I have done some further research and I believe I have found what causes the issue. The issue appears to be in the OSXFUSE kext and specifically fuse_internal_readdir_processdata().
To better understand the described issue I wrote a small program that performs a readdir and then stats all reported directory entries. For the simple filesystem containing the file "file" and directory "dir" it returned a single entry:
st_ino=0000000400000008, d_ino=0000000000000008, d_type=00, d_name=file
As before the directory entry for "dir" is missing. The directory entry for "file" is there. But notice that st_ino and d_ino defer. This means that stat(2) reports an inode of 0x400000008 and readdir(3) reports an inode of 0x8!
This clearly points to a truncation of the inode number somewhere. After double-checking that the culprit is not my file system I went through the OSXFUSE source code. As mentioned I believe that the problem is in the OSXFUSE kext, function fuse_internal_readdir_processdata(), lines 893-897:
#ifdef _DARWIN_FEATURE_64_BIT_INODE
de->d_ino = fudge->ino;
#else
de->d_ino = (ino_t)fudge->ino; /* XXX: truncation */
#endif /* _DARWIN_FEATURE_64_BIT_INODE */
It appears that the symbol _DARWIN_FEATURE_64_BIT_INODE is *not* defined, which results in the line
de->d_ino = (ino_t)fudge->ino; /* XXX: truncation */
I have confirmed this finding by disassembling the installed OSXFUSE kext on my system. The resulting disassembly only copies the bottom 32 bits effectively throwing away the top 32 ones.
I believe the fix is to simply replace lines 893-897 with:
de->d_ino = fudge->ino;
This will work whether de->d_ino is 32 or 64 bits.
Thank you for your time. I would very much appreciate any feedback.
Bill