1

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...

1 Answers1

6

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).

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 2
    ...or use `fstatat`; this is pretty much exactly what it's for. –  Apr 26 '16 at 14:35
  • @WumpusQ.Wumbley What a strange but useful call! I had never heard of that. Thanks. (Looks like a typo for `fstat`, though. :-) ) – Steve Summit Apr 26 '16 at 14:55
  • Never heard about it too! Is there a way to get a filedescriptor from a `DIR *`? Or should I also open the directory with standard `open` and call `fstatat`? – Nicolas Scotto Di Perto Apr 26 '16 at 15:42
  • @NicolasScottoDiPerto: You use `open(2)` with the `O_PATH` flag. I don't think there is a portable way to extract the file descriptor from a `DIR *`. – Nate Eldredge Apr 26 '16 at 16:44
  • Ok thanks I sure have learned something today! Do you know what is more efficient between using `fstatat` or `chdir`? – Nicolas Scotto Di Perto Apr 26 '16 at 19:01
  • @NicolasScottoDiPerto: `fstatat` is probably slightly more efficient since you save a system call and an extra path lookup (`namei` in traditional kernel jargon). But a modern kernel would have that cached anyway. – Nate Eldredge Apr 26 '16 at 20:16