#include #include #include #include #include #include #include #include #include "libc.h" struct context { char *path; int (*fn)(const char *, const struct stat *, int, struct FTW *); int fd_limit; int flags; size_t l; struct stat st; struct FTW lev; }; 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(struct context *c, struct history *h) { struct history new; int type; int r; if ((c->flags & FTW_PHYS) ? lstat(c->path, &c->st) : stat(c->path, &c->st) < 0) { if (!(c->flags & FTW_PHYS) && errno==ENOENT && !lstat(c->path, &c->st)) type = FTW_SLN; else if (errno != EACCES) return -1; else type = FTW_NS; } else if (S_ISDIR(c->st.st_mode)) { if (access(c->path, R_OK) < 0) type = FTW_DNR; else if (c->flags & FTW_DEPTH) type = FTW_DP; else type = FTW_D; } else if (S_ISLNK(c->st.st_mode)) { if (c->flags & FTW_PHYS) type = FTW_SL; else type = FTW_SLN; } else { type = FTW_F; } if ((c->flags & FTW_MOUNT) && c->st.st_dev != h->dev) return 0; new.chain = h; new.dev = c->st.st_dev; new.ino = c->st.st_ino; new.level = h->level+1; new.base = c->l+1; if (!(c->flags & FTW_DEPTH)) { c->lev.level = new.level; c->lev.base = h->base; if ((r=c->fn(c->path, &c->st, type, &c->lev))) return r; } for (; h; h = h->chain) if (h->dev == c->st.st_dev && h->ino == c->st.st_ino) return 0; if ((type == FTW_D || type == FTW_DP) && c->fd_limit) { DIR *d = opendir(c->path); if (d) { struct dirent *de; while ((de = readdir(d))) { size_t dl; if (de->d_name[0] == '.' && (!de->d_name[1] || (de->d_name[1]=='.' && !de->d_name[2]))) continue; if ((dl=strlen(de->d_name)) > PATH_MAX-new.base) { errno = ENAMETOOLONG; closedir(d); return -1; } c->l = new.base+dl; c->path[new.base-1]='/'; memcpy(c->path+new.base, de->d_name, dl+1); --c->fd_limit; r=do_nftw(c, &new); ++c->fd_limit; if (r) { closedir(d); return r; } } closedir(d); } else if (errno != EACCES) { return -1; } } c->path[new.base-1]=0; if (c->flags & FTW_DEPTH) { c->lev.level = new.level; c->lev.base = h->base; if ((r=c->fn(c->path, &c->st, type, &c->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) { struct context c; struct history h; int r, cs; size_t l; char pathbuf[PATH_MAX+1]; if (fd_limit <= 0) return 0; l = strlen(path); while (l && path[l-1]=='/') --l; if (l > PATH_MAX) { errno = ENAMETOOLONG; return -1; } memcpy(pathbuf, path, l); pathbuf[l] = 0; h.chain = NULL; h.dev = (dev_t)-1; h.ino = (ino_t)-1; h.level = -1; h.base = l; while (h.base && pathbuf[h.base-1]!='/') --h.base; c.path = pathbuf; c.fn = fn; c.fd_limit = fd_limit; c.flags = flags; c.l = l; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); r = do_nftw(&c, &h); pthread_setcancelstate(cs, 0); return r; } LFS64(nftw);