Created attachment 1982003 [details] How I reproduce this behaviour Description of problem: Apparently find does not find soft links when these are on an NTFS device Version-Release number of selected component (if applicable): findutils-4.9.0-3.fc38.x86_64 How reproducible: Always Steps to Reproduce: 1. Connect an NTFS-formatted device to your Fedora Workstation 2. In the directory where the OS mounts the NTFS device, create a file and a soft-link to that file 3. Give a command such as "find <that_directory> -type l" Actual results: The find command does not find the soft-link. Expected results: The find command should find the soft-link. Additional info: The find command does find soft-links under other formats such as EXT4. The find command does find the soft-link in the NTFS device if it looks for regular files.
Hello! I was able to reproduce this issue on my machine. Unfortunately, this seems to be a bug in the kernel. find internally uses the fts* functions from glibc to traverse the filesystem and it tries to minimise the number of (l)stat(2) calls by reusing as much information it already has acquired before. For fts to work, glibc internally calls readdir(3) which is implemented using the getdents64(2) syscall. The structure forwarded by readdir(3) from getdents64(2) for every directory entry also contains file type information which seems to be incorrect for symlinks on NTFS volumes as witnessed by the following experiment: $mount ... /dev/sda1 on /mnt/ntfs type ntfs3 (rw,relatime,uid=0,gid=0,iocharset=utf8) $ cat ~/tmp/readdir_test.c #include <dirent.h> #include <stdio.h> #include <sys/types.h> int main(int argc, char* argv[]) { DIR* dp = opendir(argv[1]); if (dp == NULL) { perror("opendir"); return 1; } struct dirent* dent; while ((dent = readdir(dp)) != NULL) { const char* type; switch (dent->d_type) { case DT_BLK: type = "block dev"; break; case DT_CHR: type = "char dev"; break; case DT_DIR: type = "dir"; break; case DT_FIFO: type = "pipe"; break; case DT_LNK: type = "symlink"; break; case DT_REG: type = "regular file"; break; case DT_SOCK: type = "socket"; break; case DT_UNKNOWN: type = "unknown"; break; } printf("name: %s, type: %s\n", dent->d_name, type); } closedir(dp); } $ cc ~/tmp/readdir_test.c $ ./a.out /mnt/ntfs name: ., type: dir name: .., type: dir name: a, type: regular file name: b, type: regular file <-- WRONG! I've checked ls and while it also uses readdir(3), it always issues an explicit (l)stat(2) so that's the reason why the final displayed information are correct.
I forgot to mention that I've used the enclosed reproducer for the experiment above.