2

I am iterating directories recursively with readdir.

struct dirent is documented here, and notes that d_type can have different values including DT_LNK and DT_DIR and DT_REG. However, when d_type is DT_LNK, there is no hint about whether the link is to a file or directory.

Is there a a way to check whether the target of a symlink is a file or directory?

merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • 3
    The directory information for a symbolic link cannot indicate whether the target is a file or directory because the target can change at any moment. E.g., if the link `foo` points to the file `/mnt/directory/bar`, another process could remove the file `/mnt/directory/bar` and create a new directory `/mnt/directory/bar`, and nothing in the symbolic link, its metadata, or its directory would change. To know what a symbolic link points to, you must examine its target. – Eric Postpischil Jul 15 '22 at 22:52
  • I think my question might be better framed as "how might I examine the target in a way that tells me whether it's a file or directory?" Or maybe, how do I map from a `dirent` to a data structure that tells me whether it's a file or directory? – merlin2011 Jul 15 '22 at 22:53
  • 1
    I'd guess you have to follow the path – Axeltherabbit Jul 15 '22 at 22:54
  • @Axeltherabbit Can you be more concrete? What system call should I be using? – merlin2011 Jul 15 '22 at 22:55
  • Thanks for the pointer to `readlink()`. However, I don't see anything in the `man 2 stat` page that says whether it's a file or directory. – merlin2011 Jul 15 '22 at 23:07
  • You don't have to call `readlink` at all -- just call `stat` on the path of the symlink; it will be followed. – Jonathon Reinhart Jul 15 '22 at 23:08
  • Ah I found it, thanks! https://stackoverflow.com/a/4553076/391161 – merlin2011 Jul 15 '22 at 23:08
  • @merlin2011: remember that you must construct the path from the name of the directory and the entry name to pass to `stat`. – chqrlie Jul 15 '22 at 23:34
  • Yup, I know. Thanks for reminder though! – merlin2011 Jul 16 '22 at 01:06

1 Answers1

5

The dirent structure might tell you if the entry is a directory or a symbolic link, but some file systems do not provide this information in d_type and set it to DT_UNKNOWN.

In any case, the simplest way to tell if a symbolic link points to a directory or not, is via a stat system call that will resolve the symbolic link and if successful, will populate the stat structure, allowing for S_ISDIR to give you the information:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int is_directory(const char *path) {
    struct stat st;
    return !stat(path, &st) && S_ISDIR(st.st_mode);
}

Note these caveats:

  • you must construct the path from the directory name and the entry name to pass to is_directory().
  • recursing on directories linked to by symbolic links may cause infinite recursion as the symbolic link may point to a parent directory or the directory itself where it is located.
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 2
    You can use `dirfd` on the `DIR *` to get the fd for the directory and use that with the `d_*` path from the dirent and pass to `fstatat` – Craig Estey Jul 16 '22 at 00:32