]>
Commit | Line | Data |
---|---|---|
59e0d9fe A |
1 | /* $OpenBSD: nftw.c,v 1.2 2003/07/21 21:15:32 millert Exp $ */ |
2 | ||
3 | /* | |
4 | * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> | |
5 | * | |
6 | * Permission to use, copy, modify, and distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | * | |
18 | * Sponsored in part by the Defense Advanced Research Projects | |
19 | * Agency (DARPA) and Air Force Research Laboratory, Air Force | |
20 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. | |
21 | */ | |
22 | ||
23 | #if defined(LIBC_SCCS) && !defined(lint) | |
24 | static const char rcsid[] = "$OpenBSD: nftw.c,v 1.2 2003/07/21 21:15:32 millert Exp $"; | |
25 | #endif /* LIBC_SCCS and not lint */ | |
26 | ||
3d9156a7 A |
27 | #include <stdio.h> |
28 | #include <sys/cdefs.h> | |
59e0d9fe A |
29 | #include <sys/types.h> |
30 | #include <sys/stat.h> | |
31 | #include <errno.h> | |
32 | #include <fts.h> | |
33 | #include <ftw.h> | |
34 | #include <limits.h> | |
3d9156a7 | 35 | #include <fcntl.h> |
34e8f829 | 36 | #include <stdlib.h> |
3d9156a7 A |
37 | #include <string.h> |
38 | #include <unistd.h> | |
59e0d9fe | 39 | |
3d9156a7 A |
40 | static int |
41 | both_ftw(const char *path, | |
42 | int (*ofn)(const char *, const struct stat *, int), | |
43 | int (*nfn)(const char *, const struct stat *, int, struct FTW *), | |
44 | int nfds, int ftwflags) | |
59e0d9fe A |
45 | { |
46 | const char *paths[2]; | |
47 | struct FTW ftw; | |
48 | FTSENT *cur; | |
1f2f436a | 49 | FTS *ftsp = NULL; |
59e0d9fe | 50 | int ftsflags, fnflag, error, postorder, sverrno; |
3d9156a7 A |
51 | int cwd_fd = -1; /* cwd_fd != -1 means call chdir a lot */ |
52 | ||
53 | #if __DARWIN_UNIX03 | |
54 | /* Macro to skip the mount point itself in UNiX03 mode, in legcy | |
55 | mode the mount point is returned, but we don't decend into it */ | |
56 | #define SKIP_MOUNT if ((ftwflags & FTW_MOUNT) \ | |
57 | && cur->fts_statp->st_dev != path_stat.st_dev) { \ | |
58 | continue; \ | |
59 | } | |
60 | #else | |
61 | #define SKIP_MOUNT | |
62 | #endif | |
59e0d9fe A |
63 | |
64 | /* XXX - nfds is currently unused */ | |
65 | if (nfds < 1 || nfds > OPEN_MAX) { | |
66 | errno = EINVAL; | |
1f2f436a A |
67 | error = -1; |
68 | goto done; | |
59e0d9fe A |
69 | } |
70 | ||
71 | ftsflags = FTS_COMFOLLOW; | |
72 | if (!(ftwflags & FTW_CHDIR)) | |
73 | ftsflags |= FTS_NOCHDIR; | |
74 | if (ftwflags & FTW_MOUNT) | |
75 | ftsflags |= FTS_XDEV; | |
3d9156a7 | 76 | if (ftwflags & FTW_PHYS) { |
59e0d9fe | 77 | ftsflags |= FTS_PHYSICAL; |
3d9156a7 A |
78 | } else { |
79 | ftsflags |= FTS_LOGICAL; | |
80 | } | |
59e0d9fe | 81 | postorder = (ftwflags & FTW_DEPTH) != 0; |
3d9156a7 A |
82 | |
83 | /* We have been requested to change directories, and fts doesn't | |
84 | always do it (never for FTS_LOGICAL, and sometimes not for | |
85 | FTS_PHYSICAL) */ | |
86 | if (ftwflags & FTW_CHDIR) { | |
87 | cwd_fd = open(".", O_RDONLY, 0); | |
88 | if (cwd_fd < 0) { | |
1f2f436a A |
89 | error = -1; |
90 | goto done; | |
3d9156a7 A |
91 | } |
92 | /* Prevent problems if fts ever starts using chdir when passed | |
93 | FTS_PHYSICAL */ | |
94 | ftsflags |= FTS_NOCHDIR; | |
95 | } | |
96 | ||
97 | #if __DARWIN_UNIX03 | |
98 | struct stat path_stat; | |
99 | ||
100 | /* UNIX03 requires us to return -1/errno=ELOOP if path | |
101 | is a looping symlink; fts_open is succesful and fts_read | |
102 | gives us FTS_NS which isn't very useful, in fact we get | |
103 | pretty much the same behaviour for ENAMETOOLONG, ENOENT, | |
104 | ENOTDIR, and EACCES */ | |
105 | { | |
106 | int rc = stat(path, &path_stat); | |
3d9156a7 A |
107 | if (rc < 0 |
108 | && (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT | |
109 | || errno == ENOTDIR || errno == EACCES)) { | |
1f2f436a A |
110 | error = -1; |
111 | goto done; | |
3d9156a7 A |
112 | } |
113 | if (rc >= 0 && nfn) { | |
114 | if (!S_ISDIR(path_stat.st_mode)) { | |
115 | errno = ENOTDIR; | |
1f2f436a A |
116 | error = -1; |
117 | goto done; | |
3d9156a7 A |
118 | } |
119 | } | |
120 | } | |
121 | #endif | |
59e0d9fe A |
122 | paths[0] = path; |
123 | paths[1] = NULL; | |
124 | ftsp = fts_open((char * const *)paths, ftsflags, NULL); | |
3d9156a7 | 125 | if (ftsp == NULL) { |
1f2f436a A |
126 | error = -1; |
127 | goto done; | |
3d9156a7 | 128 | } |
59e0d9fe A |
129 | error = 0; |
130 | while ((cur = fts_read(ftsp)) != NULL) { | |
131 | switch (cur->fts_info) { | |
132 | case FTS_D: | |
133 | if (postorder) | |
134 | continue; | |
3d9156a7 A |
135 | SKIP_MOUNT; |
136 | /* we will get FTS_DNR next (this is not an issue for | |
137 | FTS_DP, only FTS_D) */ | |
138 | if (access(cur->fts_path, R_OK) != 0) | |
139 | continue; | |
59e0d9fe A |
140 | fnflag = FTW_D; |
141 | break; | |
142 | case FTS_DNR: | |
143 | fnflag = FTW_DNR; | |
144 | break; | |
145 | case FTS_DP: | |
146 | if (!postorder) | |
147 | continue; | |
3d9156a7 | 148 | SKIP_MOUNT; |
59e0d9fe A |
149 | fnflag = FTW_DP; |
150 | break; | |
151 | case FTS_F: | |
152 | case FTS_DEFAULT: | |
153 | fnflag = FTW_F; | |
154 | break; | |
155 | case FTS_NS: | |
156 | case FTS_NSOK: | |
157 | fnflag = FTW_NS; | |
158 | break; | |
159 | case FTS_SL: | |
160 | fnflag = FTW_SL; | |
161 | break; | |
162 | case FTS_SLNONE: | |
3d9156a7 A |
163 | fnflag = nfn ? FTW_SLN : FTW_SL; |
164 | #if __DARWIN_UNIX03 | |
165 | { | |
166 | /* The legacy behaviour did not signal an error | |
167 | on symlink loops unless they ended up causing | |
168 | a directory cycle, but the Unix2003 standard | |
169 | requires ELOOP to end ftw and nftw walks with | |
170 | an error */ | |
171 | struct stat sb; | |
172 | int rc = stat(cur->fts_path, &sb); | |
173 | if (rc < 0 && errno == ELOOP) { | |
174 | error = -1; | |
175 | goto done; | |
176 | } | |
177 | } | |
178 | #endif | |
59e0d9fe A |
179 | break; |
180 | case FTS_DC: | |
3d9156a7 A |
181 | #if __DARWIN_UNIX03 |
182 | /* Unix03 says nftw should break cycles and not return | |
224c7076 | 183 | errors in non-physical mode (which is definitly what it |
3d9156a7 A |
184 | says ftw can't do) */ |
185 | if (nfn && !(ftwflags & FTW_PHYS)) { | |
224c7076 A |
186 | /* 4489297 - when FTW_DEPTH is set, skip |
187 | the link also */ | |
188 | if (postorder) | |
189 | continue; | |
3d9156a7 A |
190 | fnflag = FTW_D; |
191 | break; | |
192 | } | |
193 | #endif | |
59e0d9fe A |
194 | errno = ELOOP; |
195 | /* FALLTHROUGH */ | |
196 | default: | |
197 | error = -1; | |
198 | goto done; | |
199 | } | |
3d9156a7 A |
200 | |
201 | if (cwd_fd >= 0) { | |
202 | char *dir, *free_me = NULL; | |
203 | if (fnflag == FTW_D) { | |
204 | dir = cur->fts_path; | |
205 | } else { | |
206 | /* we could alloc just enough for the directory, | |
207 | and use memmove -- but that is a little more | |
208 | error prone, and not noticable in with all the | |
209 | extra work... */ | |
210 | dir = free_me = strdup(cur->fts_path); | |
211 | dir[cur->fts_pathlen - cur->fts_namelen] = '\0'; | |
212 | } | |
213 | int rc = chdir(dir); | |
214 | if (free_me) { | |
215 | free(free_me); | |
216 | } | |
217 | if (rc < 0) { | |
1f2f436a A |
218 | if(cur->fts_pathlen == cur->fts_namelen && |
219 | fnflag == FTW_DNR) { | |
220 | /* If cwd_fd is our last FD, fts_read will give us FTS_DNR | |
221 | * and fts_path == fts_name == "." | |
222 | * This check results in the correct errno being returned. | |
223 | */ | |
224 | errno = EMFILE; | |
225 | } | |
3d9156a7 A |
226 | error = -1; |
227 | goto done; | |
228 | } | |
229 | } | |
230 | if (nfn) { | |
231 | ftw.base = cur->fts_pathlen - cur->fts_namelen; | |
232 | ftw.level = cur->fts_level; | |
233 | error = nfn(cur->fts_path, cur->fts_statp, fnflag, &ftw); | |
234 | } else { | |
235 | error = ofn(cur->fts_path, cur->fts_statp, fnflag); | |
236 | } | |
237 | if (cwd_fd >= 0) { | |
238 | if (fchdir(cwd_fd) < 0) { | |
239 | error = -1; | |
240 | goto done; | |
241 | } | |
242 | } | |
243 | ||
59e0d9fe A |
244 | if (error != 0) |
245 | break; | |
246 | } | |
247 | done: | |
248 | sverrno = errno; | |
1f2f436a A |
249 | if(ftsp != NULL) |
250 | (void) fts_close(ftsp); | |
251 | if(cwd_fd >= 0) | |
252 | (void) close(cwd_fd); | |
59e0d9fe A |
253 | errno = sverrno; |
254 | return (error); | |
255 | } | |
3d9156a7 A |
256 | |
257 | int | |
258 | ftw(const char *path, int (*fn)(const char *, const struct stat *, int), | |
259 | int nfds) | |
260 | { | |
261 | /* The legacy implmentation didn't follow symlinks, but Unix03 | |
262 | does - this was likely a bug in the legacy implemtation; JKH | |
263 | thinks we ought change the legacy behaviour, and I agree; anyone | |
264 | who doesn't should replace FTW_PHYS with | |
265 | __DARWIN_UNIX03 ? 0 : FTW_PHYS */ | |
266 | return both_ftw(path, fn, NULL, nfds, FTW_PHYS); | |
267 | } | |
268 | ||
269 | int | |
270 | nftw(const char *path, int (*fn)(const char *, const struct stat *, int, | |
271 | struct FTW *), int nfds, int ftwflags) | |
272 | { | |
273 | return both_ftw(path, NULL, fn, nfds, ftwflags); | |
274 | } |