Checking if a dir. entry returned by readdir is a directory, link or file. dent->d_type isn't showing the type

36,753

Solution 1

d_type is a speed optimization to save on lstat(2) calls, when it's supported.

As the readdir(3) man page points out, not all filesystems return real info in the d_type field (typically because it would take an extra disk seek to read the inode, as is the case for XFS if you didn't use mkfs.xfs -n ftype=1 (implied by -m crc=1 which is not yet the default). Filesystems that always set DT_UNKNOWN are common in real life, and not something that you can ignore. XFS is not the only example.

You always need code that will fall back to using lstat(2) if d_type==DT_UNKNOWN, if the filename alone isn't enough to decide it's uninteresting. (This is the case for some callers, like find -name or expanding globs like *.c, which is why readdir doesn't incur the overhead of filling it in if it would take an extra disk read.)

The Linux getdents(2) man page has an example program that does what you're trying to do, including a chained-ternary-operator block to decode the d_type field into text strings. (As the other answers point out, your mistake is printing it out as an character, rather than comparing it against DT_REG, DT_DIR, etc.)

Anyway, the other answers mostly covered things, but missed the critical detail that you NEED a fallback for the case when d_type == DT_UNKNOWN (0 on Linux. d_type is stored in what used to be a padding byte, until Linux 2.6.4).

To be portable, your code needs to check that struct dirent even HAS a d_type field, if you use it, or your code won't even compile outside of GNU and BSD systems. (see readdir(3))


I wrote an example for finding directories with readdir, using d_type with a fallback to stat when d_type isn't available at compile time, when it's DT_UNKNOWN, and for symlinks.

Solution 2

The d_type in the return struct gives a number for the type. You can't print that directly because the used values are not printable when interpreted as ASCII (for example they are 4 for dirs and 8 for files.).

You can either print them as numbers like this:

printf("%d ", dent->d_type)

Or compare them to the constants like DT_DIR and construct some meaningful output from that, like a char type:

if(dent->type == DT_DIR) type = 'd'

Solution 3

Print d_type as an integer like so:

printf("%d ", dent->d_type);

and you'll see meaningful values.

Solution 4

I was able to use d_type on ubuntu:

    switch (readDir->d_type)
    {
    case DT_DIR:
        printf("Dir: %s\n", readDir->d_name);
        break;
    case DT_REG:
        printf("File: %s\n", readDir->d_name);
        break;
    default:
        printf("Other: %s\n", readDir->d_name);
    }

The list of entry types can be found in dirent.h, (this could be different for os other than ubuntu):

dirent.h

#define DT_UNKNOWN       0
#define DT_FIFO          1
#define DT_CHR           2
#define DT_DIR           4
#define DT_BLK           6
#define DT_REG           8
#define DT_LNK          10
#define DT_SOCK         12
#define DT_WHT          14
Share:
36,753
Barney Chambers
Author by

Barney Chambers

Wedding photographer on weekends, blockchain programmer during the week, crypto-coin enthusiast the rest of the time!

Updated on August 06, 2020

Comments

  • Barney Chambers
    Barney Chambers almost 4 years

    I am making a program which is run in a Linux shell, and accepts an argument (a directory), and displays all the files in the directory, along with their type.

    Output should be like this:

     << ./Program testDirectory
    
     Dir directory1
     lnk linkprogram.c
     reg file.txt
    

    If no argument is made, it uses the current directory. Here is my code:

    #include <stdio.h>
    #include <dirent.h>
    #include <sys/stat.h>
    
    int main(int argc, char *argv[])
    {
      struct stat info;
      DIR *dirp;
      struct dirent* dent;
    
      //If no args
      if (argc == 1)
      {
    
        argv[1] = ".";
        dirp = opendir(argv[1]); // specify directory here: "." is the "current directory"
        do
        {
          dent = readdir(dirp);
          if (dent)
          {
            printf("%c ", dent->d_type);
            printf("%s \n", dent->d_name);
    
            /* if (!stat(dent->d_name, &info))
             {
             //printf("%u bytes\n", (unsigned int)info.st_size);
    
             }*/
          }
        } while (dent);
        closedir(dirp);
    
      }
    
      //If specified directory 
      if (argc > 1)
      {
        dirp = opendir(argv[1]); // specify directory here: "." is the "current directory"
        do
        {
          dent = readdir(dirp);
          if (dent)
          {
            printf("%c ", dent->d_type);
            printf("%s \n", dent->d_name);
            /*  if (!stat(dent->d_name, &info))
             {
             printf("%u bytes\n", (unsigned int)info.st_size);
             }*/
          }
        } while (dent);
        closedir(dirp);
    
      }
      return 0;
    }
    

    For some reason dent->d_type is not displaying the type of file. I'm not really sure what to do, any suggestions?

  • Barney Chambers
    Barney Chambers about 10 years
    thank you! This is somewhat helpful, I am now receiving 0's for all of my things in the directory regardless of type. Hmm, this is confusing!
  • Barney Chambers
    Barney Chambers about 10 years
    Thanks for your input, I tried this and received no output. I also tried == DT_LNK and == DT_REG for regular files and link files, no luck though.
  • fweik
    fweik about 10 years
    In my case that worked. You have to be aware that the type is not provided by all file systems, and is not mentioned in posix. What file system are you on ?
  • Barney Chambers
    Barney Chambers about 10 years
    I'm running Red Hat Linux and using the default shell
  • wangzhengyi
    wangzhengyi about 9 years
    I am also find all my directorys in my android phone , d_type is always 0. can you give me some help to fix it ?