Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

readdir returning dirent with d_type == DT_UNKNOWN for directories . and

I have the following code that mimicks ls:

#include <dirent.h>
#include <stdio.h>

char* dirent_type_to_str(unsigned char dirent_type) {
  switch (dirent_type) {
  case DT_DIR:
    return "Dir ";
  case DT_REG:
    return "File";
  }

  printf("DEBUG: Unknown type %x\n", dirent_type);
  return "Unk ";
}

int main(int argc, char** argv) {
  char* dir_path = argc > 1 ? argv[1] : ".";
  DIR* dir_stream = opendir(dir_path);

  struct dirent* dirent_ptr;
  while (dirent_ptr = readdir(dir_stream)) {
    printf("%s %s\n", dirent_type_to_str(dirent_ptr->d_type), dirent_ptr->d_name);
  }

  closedir(dir_stream);

  return 0;
}

For some reason, when I run the program, it is saying that the d_type associated with . and .. is unknown:

james.ko@cslab1-20:~/Desktop/systems-11-02$ ./main
DEBUG: Unknown type 0
Unk  .
DEBUG: Unknown type 0
Unk  ..
File .gitignore
File main.o
File Makefile~
File Makefile
File main.c
Dir  .git
File main
File main.c~

From my research, it appears that 0 is equal to DT_UNKNOWN according to this answer. Why is this happening (instead of it yielding DT_DIR)?

like image 371
James Ko Avatar asked Dec 23 '22 12:12

James Ko


1 Answers

The manpage for readdir() clearly states, that a filesystem is free to return DT_UNKNOWN in the struct dirent:

Currently, only some filesystems (among them: Btrfs, ext2, ext3, and ext4) have full support for returning the file type in d_type. All applications must properly handle a return of DT_UNKNOWN.

This is mainly done for performance reasons, because with some filesystems the file type is not stored in the directory itself. In this case, for large directories, many reads scattered over the whole block device would slow down readdir() significantly.

So, if you really need the d_type on a filesystem, which does not fill it properly, you will have to call (l)stat() explicitly.

like image 64
Ctx Avatar answered Jan 05 '23 18:01

Ctx