Search code examples
cunixstat

C strange stat st_mode


I am printing the result of S_ISDIR(info->st_mode) and S_ISREG(info->st_mode) over a directory that contains dynamic libraries with .so extension and the result is quite surprising, S_ISREG returns 0 while S_ISDIR returns 1.

I am a bit confused...

The code:

DIR *dir;
if ((dir = opendir (dirname)) != NULL) {
  struct dirent *ent;
  while ((ent = readdir (dir)) != NULL) {
    struct stat info;
    stat(ent->d_name, &info);
    printf("file: %s, S_ISREG: %d, S_ISDIR: %d", ent->d_name, S_ISREG(info.st_mode), S_ISDIR(info.st_mode));
  }
}
closedir(dir);

The output looks like:

file: ., S_ISREG: 0, S_ISDIR: 1
file: zyva.so, S_ISREG: 0, S_ISDIR: 1
file: .gitignore, S_ISREG: 1, S_ISDIR: 0
file: .., S_ISREG: 0, S_ISDIR: 1
file: plugin-app, S_ISREG: 0, S_ISDIR: 1
file: chat.so, S_ISREG: 0, S_ISDIR: 1

plugin-app is also an executable so it's also a regular file...


Solution

  • You didn't check the return value of stat(). I'll bet if you do so, you find that it failed. In that case, the struct stat is not filled in, so it just contains uninitialized garbage (or the result of a previous successful call).

    Why did it fail? I bet you find that errno == ENOENT. Note that ent->d_name only contains the name of the file, not the path, so when you try to stat it, it's interpreted as a path relative to the current working directory. Unless dirname is the directory you're already in, you're having stat look for these files in the wrong place, so it's no wonder they aren't found.

    Either chdir(dirname) before doing your stats, or else construct the full path in a separate buffer by prepending dirname/ to the filename (make sure to check the lengths to ensure that you do not overrun your buffer).