]>
Commit | Line | Data |
---|---|---|
320054e8 DG |
1 | #include <ftw.h> |
2 | #include <dirent.h> | |
58795582 | 3 | #include <fcntl.h> |
320054e8 DG |
4 | #include <sys/stat.h> |
5 | #include <errno.h> | |
6 | #include <unistd.h> | |
7 | #include <string.h> | |
8 | #include <limits.h> | |
9f0d8e85 | 9 | #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) |
320054e8 | 10 | #include <pthread.h> |
9f0d8e85 | 11 | #endif |
320054e8 DG |
12 | |
13 | struct history | |
14 | { | |
15 | struct history *chain; | |
16 | dev_t dev; | |
17 | ino_t ino; | |
18 | int level; | |
19 | int base; | |
20 | }; | |
21 | ||
22 | #undef dirfd | |
23 | #define dirfd(d) (*(int *)d) | |
24 | ||
25 | static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags, struct history *h) | |
26 | { | |
27 | size_t l = strlen(path), j = l && path[l-1]=='/' ? l-1 : l; | |
28 | struct stat st; | |
29 | struct history new; | |
30 | int type; | |
31 | int r; | |
58795582 DG |
32 | int dfd; |
33 | int err; | |
320054e8 DG |
34 | struct FTW lev; |
35 | ||
36 | if ((flags & FTW_PHYS) ? lstat(path, &st) : stat(path, &st) < 0) { | |
37 | if (!(flags & FTW_PHYS) && errno==ENOENT && !lstat(path, &st)) | |
38 | type = FTW_SLN; | |
39 | else if (errno != EACCES) return -1; | |
40 | else type = FTW_NS; | |
41 | } else if (S_ISDIR(st.st_mode)) { | |
58795582 | 42 | if (flags & FTW_DEPTH) type = FTW_DP; |
320054e8 DG |
43 | else type = FTW_D; |
44 | } else if (S_ISLNK(st.st_mode)) { | |
45 | if (flags & FTW_PHYS) type = FTW_SL; | |
46 | else type = FTW_SLN; | |
47 | } else { | |
48 | type = FTW_F; | |
49 | } | |
50 | ||
51 | if ((flags & FTW_MOUNT) && h && st.st_dev != h->dev) | |
52 | return 0; | |
53 | ||
54 | new.chain = h; | |
55 | new.dev = st.st_dev; | |
56 | new.ino = st.st_ino; | |
57 | new.level = h ? h->level+1 : 0; | |
58 | new.base = j+1; | |
59 | ||
60 | lev.level = new.level; | |
61 | if (h) { | |
62 | lev.base = h->base; | |
63 | } else { | |
64 | size_t k; | |
65 | for (k=j; k && path[k]=='/'; k--); | |
66 | for (; k && path[k-1]!='/'; k--); | |
67 | lev.base = k; | |
68 | } | |
69 | ||
58795582 DG |
70 | if (type == FTW_D || type == FTW_DP) { |
71 | dfd = open(path, O_RDONLY); | |
72 | err = errno; | |
73 | if (dfd < 0 && err == EACCES) type = FTW_DNR; | |
74 | if (!fd_limit) close(dfd); | |
75 | } | |
76 | ||
320054e8 DG |
77 | if (!(flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev))) |
78 | return r; | |
79 | ||
80 | for (; h; h = h->chain) | |
81 | if (h->dev == st.st_dev && h->ino == st.st_ino) | |
82 | return 0; | |
83 | ||
84 | if ((type == FTW_D || type == FTW_DP) && fd_limit) { | |
58795582 DG |
85 | if (dfd < 0) { |
86 | errno = err; | |
87 | return -1; | |
88 | } | |
89 | DIR *d = fdopendir(dfd); | |
320054e8 DG |
90 | if (d) { |
91 | struct dirent *de; | |
92 | while ((de = readdir(d))) { | |
93 | if (de->d_name[0] == '.' | |
94 | && (!de->d_name[1] | |
95 | || (de->d_name[1]=='.' | |
96 | && !de->d_name[2]))) continue; | |
97 | if (strlen(de->d_name) >= PATH_MAX-l) { | |
98 | errno = ENAMETOOLONG; | |
99 | closedir(d); | |
100 | return -1; | |
101 | } | |
102 | path[j]='/'; | |
103 | strcpy(path+j+1, de->d_name); | |
104 | if ((r=do_nftw(path, fn, fd_limit-1, flags, &new))) { | |
105 | closedir(d); | |
106 | return r; | |
107 | } | |
108 | } | |
109 | closedir(d); | |
58795582 DG |
110 | } else { |
111 | close(dfd); | |
320054e8 DG |
112 | return -1; |
113 | } | |
114 | } | |
115 | ||
116 | path[l] = 0; | |
117 | if ((flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev))) | |
118 | return r; | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
123 | int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags) | |
124 | { | |
125 | int r, cs; | |
126 | size_t l; | |
127 | char pathbuf[PATH_MAX+1]; | |
128 | ||
129 | if (fd_limit <= 0) return 0; | |
130 | ||
131 | l = strlen(path); | |
132 | if (l > PATH_MAX) { | |
133 | errno = ENAMETOOLONG; | |
134 | return -1; | |
135 | } | |
136 | memcpy(pathbuf, path, l+1); | |
137 | ||
138 | #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) | |
139 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); | |
140 | #endif | |
141 | r = do_nftw(pathbuf, fn, fd_limit, flags, NULL); | |
142 | #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) | |
143 | pthread_setcancelstate(cs, 0); | |
144 | #endif | |
145 | return r; | |
146 | } | |
147 | ||
148 | weak_alias(nftw, nftw64); |