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