blob: a12912f1e364ff3d6301ddf9370332554b842804 [file] [log] [blame]
#include <dirent.h>
#include <errno.h>
#include <ftw.h>
#include <limits.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
struct history {
struct history* chain;
dev_t dev;
ino_t ino;
int level;
int base;
};
#undef dirfd
#define dirfd(d) (*(int*)d)
static int do_nftw(char* path, int (*fn)(const char*, const struct stat*, int, struct FTW*),
int fd_limit, int flags, struct history* h) {
size_t l = strlen(path), j = l && path[l - 1] == '/' ? l - 1 : l;
struct stat st;
struct history new;
int type;
int r;
struct FTW lev;
char* name;
if ((flags & FTW_PHYS) ? lstat(path, &st) : stat(path, &st) < 0) {
if (!(flags & FTW_PHYS) && errno == ENOENT && !lstat(path, &st))
type = FTW_SLN;
else if (errno != EACCES)
return -1;
else
type = FTW_NS;
} else if (S_ISDIR(st.st_mode)) {
if (access(path, R_OK) < 0)
type = FTW_DNR;
else if (flags & FTW_DEPTH)
type = FTW_DP;
else
type = FTW_D;
} else if (S_ISLNK(st.st_mode)) {
if (flags & FTW_PHYS)
type = FTW_SL;
else
type = FTW_SLN;
} else {
type = FTW_F;
}
if ((flags & FTW_MOUNT) && h && st.st_dev != h->dev)
return 0;
new.chain = h;
new.dev = st.st_dev;
new.ino = st.st_ino;
new.level = h ? h->level + 1 : 0;
new.base = l + 1;
lev.level = new.level;
lev.base = h ? h->base : (name = strrchr(path, '/')) ? name - path : 0;
if (!(flags & FTW_DEPTH) && (r = fn(path, &st, type, &lev)))
return r;
for (; h; h = h->chain)
if (h->dev == st.st_dev && h->ino == st.st_ino)
return 0;
if ((type == FTW_D || type == FTW_DP) && fd_limit) {
DIR* d = opendir(path);
if (d) {
struct dirent* de;
while ((de = readdir(d))) {
if (de->d_name[0] == '.' &&
(!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
continue;
if (strlen(de->d_name) >= PATH_MAX - l) {
errno = ENAMETOOLONG;
closedir(d);
return -1;
}
path[j] = '/';
strcpy(path + j + 1, de->d_name);
if ((r = do_nftw(path, fn, fd_limit - 1, flags, &new))) {
closedir(d);
return r;
}
}
closedir(d);
} else if (errno != EACCES) {
return -1;
}
}
path[l] = 0;
if ((flags & FTW_DEPTH) && (r = fn(path, &st, type, &lev)))
return r;
return 0;
}
int nftw(const char* path, int (*fn)(const char*, const struct stat*, int, struct FTW*),
int fd_limit, int flags) {
size_t l;
char pathbuf[PATH_MAX + 1];
if (fd_limit <= 0)
return 0;
l = strlen(path);
if (l > PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
memcpy(pathbuf, path, l + 1);
return do_nftw(pathbuf, fn, fd_limit, flags, NULL);
}