]>
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 A |
35 | #include <fcntl.h> |
36 | #include <string.h> | |
37 | #include <unistd.h> | |
59e0d9fe | 38 | |
3d9156a7 A |
39 | static int |
40 | both_ftw(const char *path, | |
41 | int (*ofn)(const char *, const struct stat *, int), | |
42 | int (*nfn)(const char *, const struct stat *, int, struct FTW *), | |
43 | int nfds, int ftwflags) | |
59e0d9fe A |
44 | { |
45 | const char *paths[2]; | |
46 | struct FTW ftw; | |
47 | FTSENT *cur; | |
48 | FTS *ftsp; | |
49 | int ftsflags, fnflag, error, postorder, sverrno; | |
3d9156a7 A |
50 | int cwd_fd = -1; /* cwd_fd != -1 means call chdir a lot */ |
51 | ||
52 | #if __DARWIN_UNIX03 | |
53 | /* Macro to skip the mount point itself in UNiX03 mode, in legcy | |
54 | mode the mount point is returned, but we don't decend into it */ | |
55 | #define SKIP_MOUNT if ((ftwflags & FTW_MOUNT) \ | |
56 | && cur->fts_statp->st_dev != path_stat.st_dev) { \ | |
57 | continue; \ | |
58 | } | |
59 | #else | |
60 | #define SKIP_MOUNT | |
61 | #endif | |
59e0d9fe A |
62 | |
63 | /* XXX - nfds is currently unused */ | |
64 | if (nfds < 1 || nfds > OPEN_MAX) { | |
65 | errno = EINVAL; | |
66 | return (-1); | |
67 | } | |
68 | ||
69 | ftsflags = FTS_COMFOLLOW; | |
70 | if (!(ftwflags & FTW_CHDIR)) | |
71 | ftsflags |= FTS_NOCHDIR; | |
72 | if (ftwflags & FTW_MOUNT) | |
73 | ftsflags |= FTS_XDEV; | |
3d9156a7 | 74 | if (ftwflags & FTW_PHYS) { |
59e0d9fe | 75 | ftsflags |= FTS_PHYSICAL; |
3d9156a7 A |
76 | } else { |
77 | ftsflags |= FTS_LOGICAL; | |
78 | } | |
59e0d9fe | 79 | postorder = (ftwflags & FTW_DEPTH) != 0; |
3d9156a7 A |
80 | |
81 | /* We have been requested to change directories, and fts doesn't | |
82 | always do it (never for FTS_LOGICAL, and sometimes not for | |
83 | FTS_PHYSICAL) */ | |
84 | if (ftwflags & FTW_CHDIR) { | |
85 | cwd_fd = open(".", O_RDONLY, 0); | |
86 | if (cwd_fd < 0) { | |
87 | return -1; | |
88 | } | |
89 | /* Prevent problems if fts ever starts using chdir when passed | |
90 | FTS_PHYSICAL */ | |
91 | ftsflags |= FTS_NOCHDIR; | |
92 | } | |
93 | ||
94 | #if __DARWIN_UNIX03 | |
95 | struct stat path_stat; | |
96 | ||
97 | /* UNIX03 requires us to return -1/errno=ELOOP if path | |
98 | is a looping symlink; fts_open is succesful and fts_read | |
99 | gives us FTS_NS which isn't very useful, in fact we get | |
100 | pretty much the same behaviour for ENAMETOOLONG, ENOENT, | |
101 | ENOTDIR, and EACCES */ | |
102 | { | |
103 | int rc = stat(path, &path_stat); | |
104 | int e = errno; | |
105 | if (rc < 0 | |
106 | && (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT | |
107 | || errno == ENOTDIR || errno == EACCES)) { | |
108 | return -1; | |
109 | } | |
110 | if (rc >= 0 && nfn) { | |
111 | if (!S_ISDIR(path_stat.st_mode)) { | |
112 | errno = ENOTDIR; | |
113 | return -1; | |
114 | } | |
115 | } | |
116 | } | |
117 | #endif | |
59e0d9fe A |
118 | paths[0] = path; |
119 | paths[1] = NULL; | |
120 | ftsp = fts_open((char * const *)paths, ftsflags, NULL); | |
3d9156a7 A |
121 | if (ftsp == NULL) { |
122 | return (-1); | |
123 | } | |
59e0d9fe A |
124 | error = 0; |
125 | while ((cur = fts_read(ftsp)) != NULL) { | |
126 | switch (cur->fts_info) { | |
127 | case FTS_D: | |
128 | if (postorder) | |
129 | continue; | |
3d9156a7 A |
130 | SKIP_MOUNT; |
131 | /* we will get FTS_DNR next (this is not an issue for | |
132 | FTS_DP, only FTS_D) */ | |
133 | if (access(cur->fts_path, R_OK) != 0) | |
134 | continue; | |
59e0d9fe A |
135 | fnflag = FTW_D; |
136 | break; | |
137 | case FTS_DNR: | |
138 | fnflag = FTW_DNR; | |
139 | break; | |
140 | case FTS_DP: | |
141 | if (!postorder) | |
142 | continue; | |
3d9156a7 | 143 | SKIP_MOUNT; |
59e0d9fe A |
144 | fnflag = FTW_DP; |
145 | break; | |
146 | case FTS_F: | |
147 | case FTS_DEFAULT: | |
148 | fnflag = FTW_F; | |
149 | break; | |
150 | case FTS_NS: | |
151 | case FTS_NSOK: | |
152 | fnflag = FTW_NS; | |
153 | break; | |
154 | case FTS_SL: | |
155 | fnflag = FTW_SL; | |
156 | break; | |
157 | case FTS_SLNONE: | |
3d9156a7 A |
158 | fnflag = nfn ? FTW_SLN : FTW_SL; |
159 | #if __DARWIN_UNIX03 | |
160 | { | |
161 | /* The legacy behaviour did not signal an error | |
162 | on symlink loops unless they ended up causing | |
163 | a directory cycle, but the Unix2003 standard | |
164 | requires ELOOP to end ftw and nftw walks with | |
165 | an error */ | |
166 | struct stat sb; | |
167 | int rc = stat(cur->fts_path, &sb); | |
168 | if (rc < 0 && errno == ELOOP) { | |
169 | error = -1; | |
170 | goto done; | |
171 | } | |
172 | } | |
173 | #endif | |
59e0d9fe A |
174 | break; |
175 | case FTS_DC: | |
3d9156a7 A |
176 | #if __DARWIN_UNIX03 |
177 | /* Unix03 says nftw should break cycles and not return | |
224c7076 | 178 | errors in non-physical mode (which is definitly what it |
3d9156a7 A |
179 | says ftw can't do) */ |
180 | if (nfn && !(ftwflags & FTW_PHYS)) { | |
224c7076 A |
181 | /* 4489297 - when FTW_DEPTH is set, skip |
182 | the link also */ | |
183 | if (postorder) | |
184 | continue; | |
3d9156a7 A |
185 | fnflag = FTW_D; |
186 | break; | |
187 | } | |
188 | #endif | |
59e0d9fe A |
189 | errno = ELOOP; |
190 | /* FALLTHROUGH */ | |
191 | default: | |
192 | error = -1; | |
193 | goto done; | |
194 | } | |
3d9156a7 A |
195 | |
196 | if (cwd_fd >= 0) { | |
197 | char *dir, *free_me = NULL; | |
198 | if (fnflag == FTW_D) { | |
199 | dir = cur->fts_path; | |
200 | } else { | |
201 | /* we could alloc just enough for the directory, | |
202 | and use memmove -- but that is a little more | |
203 | error prone, and not noticable in with all the | |
204 | extra work... */ | |
205 | dir = free_me = strdup(cur->fts_path); | |
206 | dir[cur->fts_pathlen - cur->fts_namelen] = '\0'; | |
207 | } | |
208 | int rc = chdir(dir); | |
209 | if (free_me) { | |
210 | free(free_me); | |
211 | } | |
212 | if (rc < 0) { | |
213 | error = -1; | |
214 | goto done; | |
215 | } | |
216 | } | |
217 | if (nfn) { | |
218 | ftw.base = cur->fts_pathlen - cur->fts_namelen; | |
219 | ftw.level = cur->fts_level; | |
220 | error = nfn(cur->fts_path, cur->fts_statp, fnflag, &ftw); | |
221 | } else { | |
222 | error = ofn(cur->fts_path, cur->fts_statp, fnflag); | |
223 | } | |
224 | if (cwd_fd >= 0) { | |
225 | if (fchdir(cwd_fd) < 0) { | |
226 | error = -1; | |
227 | goto done; | |
228 | } | |
229 | } | |
230 | ||
59e0d9fe A |
231 | if (error != 0) |
232 | break; | |
233 | } | |
234 | done: | |
235 | sverrno = errno; | |
236 | (void) fts_close(ftsp); | |
237 | errno = sverrno; | |
238 | return (error); | |
239 | } | |
3d9156a7 A |
240 | |
241 | int | |
242 | ftw(const char *path, int (*fn)(const char *, const struct stat *, int), | |
243 | int nfds) | |
244 | { | |
245 | /* The legacy implmentation didn't follow symlinks, but Unix03 | |
246 | does - this was likely a bug in the legacy implemtation; JKH | |
247 | thinks we ought change the legacy behaviour, and I agree; anyone | |
248 | who doesn't should replace FTW_PHYS with | |
249 | __DARWIN_UNIX03 ? 0 : FTW_PHYS */ | |
250 | return both_ftw(path, fn, NULL, nfds, FTW_PHYS); | |
251 | } | |
252 | ||
253 | int | |
254 | nftw(const char *path, int (*fn)(const char *, const struct stat *, int, | |
255 | struct FTW *), int nfds, int ftwflags) | |
256 | { | |
257 | return both_ftw(path, NULL, fn, nfds, ftwflags); | |
258 | } |