
readdir returning invalid results for junction points
Hi,
While using Baobab (a Linux tool to check disk usage) I stumbled across a bug which is probably caused by ntfs-3g.
GLib bugreport:
https://bugzilla.gnome.org/show_bug.cgi?id=672219The issue is that the readdir syscall returns an invalid result for junction points/symbolic links on NTFS volumes.
The readdir syscall returns a 'struct dirent' containing information about all files and directories in a folder.
This structure contains a field called d_type which is used to indicate the type of the file in question.
Some possible values are DT_DIR for directories, DT_REG for regular files and DT_LNK for symbolic links.
When trying to perform a readdir on a NTFS folder which contains a junction point (for example the root on a NTFS volume with Windows 7 installed which contains a junction point called 'Documents and Settings' which points to the 'Users' folder) then the d_type field contains the value DT_DIR for any detected junction points. I consider this an invalid value as a junction point isn't really a directory, but is a symbolic link (in UNIX terminology). Therefore I expect the d_type field to have the value DT_LNK.
Here's a simple testcase to illustrate this behaviour:
Code:
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
const char *get_d_type_str(unsigned char d_type)
{
switch (d_type) {
case DT_BLK: return "Block device";
case DT_CHR: return "Character device";
case DT_DIR: return "Directory";
case DT_FIFO: return "Named pipe (FIFO)";
case DT_LNK: return "Symbolic link";
case DT_REG: return "Regular file";
case DT_SOCK: return "UNIX domain socket";
case DT_UNKNOWN:
default:
return "Unknown";
}
}
const char *get_st_mode(mode_t st_mode)
{
if (S_ISBLK(st_mode)) return "Block device";
if (S_ISCHR(st_mode)) return "Character device";
if (S_ISDIR(st_mode)) return "Directory";
if (S_ISFIFO(st_mode)) return "Named pipe (FIFO)";
if (S_ISLNK(st_mode)) return "Symbolic link";
if (S_ISREG(st_mode)) return "Regular file";
if (S_ISSOCK(st_mode)) return "UNIX domain socket";
return "Unknown";
}
int main(int argc, char **argv)
{
DIR *dir;
struct dirent *di;
if (argc != 2) {
printf("Usage: %s folder\n", argv[0]);
return 1;
}
dir = opendir(argv[1]);
if (!dir) {
printf("Unable to opendir %s: %s\n", argv[1], strerror(errno));
return 1;
}
while (di = readdir(dir)) {
char path[256];
struct stat st;
memset(path, 0, sizeof(path));
snprintf(path, sizeof(path), "%s/%s", argv[1], di->d_name);
if (lstat(path, &st) != 0) {
printf("Unable to stat %s: %s\n", path, strerror(errno));
continue;
}
printf("di.d_name = %s - di.d_type = %i (%s) - st.st_mode = %i (%s)\n", di->d_name, di->d_type, get_d_type_str(di->d_type), st.st_mode, get_st_mode(st.st_mode));
}
closedir(dir);
return 0;
}
When this command is run on a Linux (ext4) folder which contains a symbolic link (pointing to a directory) then it returns these results:
<snip>
di.d_name = my_symlink - di.d_type = 10 (Symbolic link) - st.st_mode = 41471 (Symbolic link)
</snip>
However, when I run this on a NTFS volume containing Windows 7 then I get these results:
<snip>
di.d_name = Documents and Settings - di.d_type = 4 (Directory) - st.st_mode = 41471 (Symbolic link)
</snip>
As you can see both the readdir and lstat syscalls correctly detect the symbolic link on the ext4 volume, but on the NTFS volume only the lstat syscall indicates that the file in question is a symbolic link while the readdir syscall indicates that the file is a directory.
Please fix this behaviour so that the readdir syscall sets the d_type to 10 (Symbolic link) whenever a junction point is detected