Normally, if you have
d = opendir(dirname);
while((dir = readdir(d)) != NULL) {
some_operation(dir->d_name);
}
some_operation will fail for every d_name, because the path you should have passed to some_operation is ${dirname}/${dir->d_name}, not just dir->d_name.
Your program, though, is hardwired to pass the special directory . to opendir; when you do that, it is safe to pass just dir->d_name to some_operation, since . is always the current working directory. Your problem is instead that readlink does not fail when applied to a broken symlink, but does fail when applied to a directory entry that isn't a symlink. It would have been easier to figure this out for yourself if you had included dir->d_name and strerror(errno) in your error message, like this:
d = opendir(".");
while ((dir = readdir(d)) != 0) {
char buff[256];
if (readlink(dir->d_name, buff, sizeof buff) {
printf("%s: readlink failed: %s\n", dir->d_name, strerror(errno));
} else {
printf("%s -> %s\n", dir->d_name, buff);
}
}
If you had done that, you would have gotten output like this:
.gnome2: readlink failed: Invalid argument
.python_history: readlink failed: Invalid argument
test.s: readlink failed: Invalid argument
bin -> .local/bin
[etc]
and then it would probably have occurred to you to look at the readlink manpage and discover that it returns EINVAL when applied to something that isn't a symlink.
The proper way to detect a broken symlink is by observing that lstat succeeds but stat fails with ENOENT:
struct stat lst, st;
if (lstat(dir->d_name, &lst)) {
/* some other kind of problem */
} else if (!S_ISLNK(lst.st_mode)) {
/* not a symlink at all */
} else if (stat(dir->d_name, &st)) {
if (errno == ENOENT) {
/* broken symlink */
} else {
/* some other kind of problem */
}
} else {
/* valid symlink, `lst` tells you about the link,
`st` tells you about what it points to */
}
If you don't need any of the other information from lst, and your filesystem supports d_type, you can skip the lstat call:
if (dir->d_type == DT_LNK) {
struct stat st;
if (stat(dir->d_name, &st)) {
if (errno == ENOENT) {
/* broken symlink */
}
}
}
But don't neglect to do the entire dance with both lstat and stat in the DT_UNKNOWN case, or you'll be sad when you try to run your program on a filesystem that doesn't report d_type information.