1#include <dirent.h> 2#include <errno.h> 3#include <ftw.h> 4#include <limits.h> 5#include <string.h> 6#include <sys/stat.h> 7#include <unistd.h> 8 9struct history { 10 struct history* chain; 11 dev_t dev; 12 ino_t ino; 13 int level; 14 int base; 15}; 16 17#undef dirfd 18#define dirfd(d) (*(int*)d) 19 20static int do_nftw(char* path, int (*fn)(const char*, const struct stat*, int, struct FTW*), 21 int fd_limit, int flags, struct history* h) { 22 size_t l = strlen(path), j = l && path[l - 1] == '/' ? l - 1 : l; 23 struct stat st; 24 struct history new; 25 int type; 26 int r; 27 struct FTW lev; 28 char* name; 29 30 if ((flags & FTW_PHYS) ? lstat(path, &st) : stat(path, &st) < 0) { 31 if (!(flags & FTW_PHYS) && errno == ENOENT && !lstat(path, &st)) 32 type = FTW_SLN; 33 else if (errno != EACCES) 34 return -1; 35 else 36 type = FTW_NS; 37 } else if (S_ISDIR(st.st_mode)) { 38 if (access(path, R_OK) < 0) 39 type = FTW_DNR; 40 else if (flags & FTW_DEPTH) 41 type = FTW_DP; 42 else 43 type = FTW_D; 44 } else if (S_ISLNK(st.st_mode)) { 45 if (flags & FTW_PHYS) 46 type = FTW_SL; 47 else 48 type = FTW_SLN; 49 } else { 50 type = FTW_F; 51 } 52 53 if ((flags & FTW_MOUNT) && h && st.st_dev != h->dev) 54 return 0; 55 56 new.chain = h; 57 new.dev = st.st_dev; 58 new.ino = st.st_ino; 59 new.level = h ? h->level + 1 : 0; 60 new.base = l + 1; 61 62 lev.level = new.level; 63 lev.base = h ? h->base : (name = strrchr(path, '/')) ? name - path : 0; 64 65 if (!(flags & FTW_DEPTH) && (r = fn(path, &st, type, &lev))) 66 return r; 67 68 for (; h; h = h->chain) 69 if (h->dev == st.st_dev && h->ino == st.st_ino) 70 return 0; 71 72 if ((type == FTW_D || type == FTW_DP) && fd_limit) { 73 DIR* d = opendir(path); 74 if (d) { 75 struct dirent* de; 76 while ((de = readdir(d))) { 77 if (de->d_name[0] == '.' && 78 (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))) 79 continue; 80 if (strlen(de->d_name) >= PATH_MAX - l) { 81 errno = ENAMETOOLONG; 82 closedir(d); 83 return -1; 84 } 85 path[j] = '/'; 86 strcpy(path + j + 1, de->d_name); 87 if ((r = do_nftw(path, fn, fd_limit - 1, flags, &new))) { 88 closedir(d); 89 return r; 90 } 91 } 92 closedir(d); 93 } else if (errno != EACCES) { 94 return -1; 95 } 96 } 97 98 path[l] = 0; 99 if ((flags & FTW_DEPTH) && (r = fn(path, &st, type, &lev))) 100 return r; 101 102 return 0; 103} 104 105int nftw(const char* path, int (*fn)(const char*, const struct stat*, int, struct FTW*), 106 int fd_limit, int flags) { 107 size_t l; 108 char pathbuf[PATH_MAX + 1]; 109 110 if (fd_limit <= 0) 111 return 0; 112 113 l = strlen(path); 114 if (l > PATH_MAX) { 115 errno = ENAMETOOLONG; 116 return -1; 117 } 118 memcpy(pathbuf, path, l + 1); 119 120 return do_nftw(pathbuf, fn, fd_limit, flags, NULL); 121} 122