1 |
|
|
/* $OpenBSD: fts.c,v 1.58 2017/03/17 15:14:40 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1990, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
16 |
|
|
* may be used to endorse or promote products derived from this software |
17 |
|
|
* without specific prior written permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 |
|
|
* SUCH DAMAGE. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
#include <sys/param.h> /* ALIGN */ |
33 |
|
|
#include <sys/stat.h> |
34 |
|
|
|
35 |
|
|
#include <dirent.h> |
36 |
|
|
#include <errno.h> |
37 |
|
|
#include <fcntl.h> |
38 |
|
|
#include <fts.h> |
39 |
|
|
#include <limits.h> |
40 |
|
|
#include <stdlib.h> |
41 |
|
|
#include <string.h> |
42 |
|
|
#include <unistd.h> |
43 |
|
|
|
44 |
|
|
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
45 |
|
|
|
46 |
|
|
static FTSENT *fts_alloc(FTS *, char *, size_t); |
47 |
|
|
static FTSENT *fts_build(FTS *, int); |
48 |
|
|
static void fts_lfree(FTSENT *); |
49 |
|
|
static void fts_load(FTS *, FTSENT *); |
50 |
|
|
static size_t fts_maxarglen(char * const *); |
51 |
|
|
static void fts_padjust(FTS *, FTSENT *); |
52 |
|
|
static int fts_palloc(FTS *, size_t); |
53 |
|
|
static FTSENT *fts_sort(FTS *, FTSENT *, int); |
54 |
|
|
static u_short fts_stat(FTS *, FTSENT *, int, int); |
55 |
|
|
static int fts_safe_changedir(FTS *, FTSENT *, int, char *); |
56 |
|
|
|
57 |
|
|
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) |
58 |
|
|
|
59 |
|
|
#define CLR(opt) (sp->fts_options &= ~(opt)) |
60 |
|
|
#define ISSET(opt) (sp->fts_options & (opt)) |
61 |
|
|
#define SET(opt) (sp->fts_options |= (opt)) |
62 |
|
|
|
63 |
|
|
#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) |
64 |
|
|
|
65 |
|
|
/* fts_build flags */ |
66 |
|
|
#define BCHILD 1 /* fts_children */ |
67 |
|
|
#define BNAMES 2 /* fts_children, names only */ |
68 |
|
|
#define BREAD 3 /* fts_read */ |
69 |
|
|
|
70 |
|
|
FTS * |
71 |
|
|
fts_open(char * const *argv, int options, |
72 |
|
|
int (*compar)(const FTSENT **, const FTSENT **)) |
73 |
|
|
{ |
74 |
|
|
FTS *sp; |
75 |
|
|
FTSENT *p, *root; |
76 |
|
|
int nitems; |
77 |
|
|
FTSENT *parent, *prev; |
78 |
|
|
|
79 |
|
|
/* Options check. */ |
80 |
|
|
if (options & ~FTS_OPTIONMASK) { |
81 |
|
|
errno = EINVAL; |
82 |
|
|
return (NULL); |
83 |
|
|
} |
84 |
|
|
|
85 |
|
|
/* At least one path must be specified. */ |
86 |
|
|
if (*argv == NULL) { |
87 |
|
|
errno = EINVAL; |
88 |
|
|
return (NULL); |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
/* Allocate/initialize the stream */ |
92 |
|
|
if ((sp = calloc(1, sizeof(FTS))) == NULL) |
93 |
|
|
return (NULL); |
94 |
|
|
sp->fts_compar = compar; |
95 |
|
|
sp->fts_options = options; |
96 |
|
|
|
97 |
|
|
/* Logical walks turn on NOCHDIR; symbolic links are too hard. */ |
98 |
|
|
if (ISSET(FTS_LOGICAL)) |
99 |
|
|
SET(FTS_NOCHDIR); |
100 |
|
|
|
101 |
|
|
/* |
102 |
|
|
* Start out with 1K of path space, and enough, in any case, |
103 |
|
|
* to hold the user's paths. |
104 |
|
|
*/ |
105 |
|
|
if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX))) |
106 |
|
|
goto mem1; |
107 |
|
|
|
108 |
|
|
/* Allocate/initialize root's parent. */ |
109 |
|
|
if ((parent = fts_alloc(sp, "", 0)) == NULL) |
110 |
|
|
goto mem2; |
111 |
|
|
parent->fts_level = FTS_ROOTPARENTLEVEL; |
112 |
|
|
|
113 |
|
|
/* Allocate/initialize root(s). */ |
114 |
|
|
for (root = prev = NULL, nitems = 0; *argv; ++argv, ++nitems) { |
115 |
|
|
if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL) |
116 |
|
|
goto mem3; |
117 |
|
|
p->fts_level = FTS_ROOTLEVEL; |
118 |
|
|
p->fts_parent = parent; |
119 |
|
|
p->fts_accpath = p->fts_name; |
120 |
|
|
p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1); |
121 |
|
|
|
122 |
|
|
/* Command-line "." and ".." are real directories. */ |
123 |
|
|
if (p->fts_info == FTS_DOT) |
124 |
|
|
p->fts_info = FTS_D; |
125 |
|
|
|
126 |
|
|
/* |
127 |
|
|
* If comparison routine supplied, traverse in sorted |
128 |
|
|
* order; otherwise traverse in the order specified. |
129 |
|
|
*/ |
130 |
|
|
if (compar) { |
131 |
|
|
p->fts_link = root; |
132 |
|
|
root = p; |
133 |
|
|
} else { |
134 |
|
|
p->fts_link = NULL; |
135 |
|
|
if (root == NULL) |
136 |
|
|
root = p; |
137 |
|
|
else |
138 |
|
|
prev->fts_link = p; |
139 |
|
|
prev = p; |
140 |
|
|
} |
141 |
|
|
} |
142 |
|
|
if (compar && nitems > 1) |
143 |
|
|
root = fts_sort(sp, root, nitems); |
144 |
|
|
|
145 |
|
|
/* |
146 |
|
|
* Allocate a dummy pointer and make fts_read think that we've just |
147 |
|
|
* finished the node before the root(s); set p->fts_info to FTS_INIT |
148 |
|
|
* so that everything about the "current" node is ignored. |
149 |
|
|
*/ |
150 |
|
|
if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) |
151 |
|
|
goto mem3; |
152 |
|
|
sp->fts_cur->fts_link = root; |
153 |
|
|
sp->fts_cur->fts_info = FTS_INIT; |
154 |
|
|
|
155 |
|
|
/* |
156 |
|
|
* If using chdir(2), grab a file descriptor pointing to dot to ensure |
157 |
|
|
* that we can get back here; this could be avoided for some paths, |
158 |
|
|
* but almost certainly not worth the effort. Slashes, symbolic links, |
159 |
|
|
* and ".." are all fairly nasty problems. Note, if we can't get the |
160 |
|
|
* descriptor we run anyway, just more slowly. |
161 |
|
|
*/ |
162 |
|
|
if (!ISSET(FTS_NOCHDIR) && |
163 |
|
|
(sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC)) < 0) |
164 |
|
|
SET(FTS_NOCHDIR); |
165 |
|
|
|
166 |
|
|
if (nitems == 0) |
167 |
|
|
free(parent); |
168 |
|
|
|
169 |
|
|
return (sp); |
170 |
|
|
|
171 |
|
|
mem3: fts_lfree(root); |
172 |
|
|
free(parent); |
173 |
|
|
mem2: free(sp->fts_path); |
174 |
|
|
mem1: free(sp); |
175 |
|
|
return (NULL); |
176 |
|
|
} |
177 |
|
|
DEF_WEAK(fts_open); |
178 |
|
|
|
179 |
|
|
static void |
180 |
|
|
fts_load(FTS *sp, FTSENT *p) |
181 |
|
|
{ |
182 |
|
|
size_t len; |
183 |
|
|
char *cp; |
184 |
|
|
|
185 |
|
|
/* |
186 |
|
|
* Load the stream structure for the next traversal. Since we don't |
187 |
|
|
* actually enter the directory until after the preorder visit, set |
188 |
|
|
* the fts_accpath field specially so the chdir gets done to the right |
189 |
|
|
* place and the user can access the first node. From fts_open it's |
190 |
|
|
* known that the path will fit. |
191 |
|
|
*/ |
192 |
|
|
len = p->fts_pathlen = p->fts_namelen; |
193 |
|
|
memmove(sp->fts_path, p->fts_name, len + 1); |
194 |
|
|
if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { |
195 |
|
|
len = strlen(++cp); |
196 |
|
|
memmove(p->fts_name, cp, len + 1); |
197 |
|
|
p->fts_namelen = len; |
198 |
|
|
} |
199 |
|
|
p->fts_accpath = p->fts_path = sp->fts_path; |
200 |
|
|
sp->fts_dev = p->fts_dev; |
201 |
|
|
} |
202 |
|
|
|
203 |
|
|
int |
204 |
|
|
fts_close(FTS *sp) |
205 |
|
|
{ |
206 |
|
|
FTSENT *freep, *p; |
207 |
|
|
int rfd, error = 0; |
208 |
|
|
|
209 |
|
|
/* |
210 |
|
|
* This still works if we haven't read anything -- the dummy structure |
211 |
|
|
* points to the root list, so we step through to the end of the root |
212 |
|
|
* list which has a valid parent pointer. |
213 |
|
|
*/ |
214 |
|
|
if (sp->fts_cur) { |
215 |
|
|
for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { |
216 |
|
|
freep = p; |
217 |
|
|
p = p->fts_link ? p->fts_link : p->fts_parent; |
218 |
|
|
free(freep); |
219 |
|
|
} |
220 |
|
|
free(p); |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
/* Stash the original directory fd if needed. */ |
224 |
|
|
rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd; |
225 |
|
|
|
226 |
|
|
/* Free up child linked list, sort array, path buffer, stream ptr.*/ |
227 |
|
|
if (sp->fts_child) |
228 |
|
|
fts_lfree(sp->fts_child); |
229 |
|
|
free(sp->fts_array); |
230 |
|
|
free(sp->fts_path); |
231 |
|
|
free(sp); |
232 |
|
|
|
233 |
|
|
/* Return to original directory, checking for error. */ |
234 |
|
|
if (rfd != -1) { |
235 |
|
|
int saved_errno; |
236 |
|
|
error = fchdir(rfd); |
237 |
|
|
saved_errno = errno; |
238 |
|
|
(void)close(rfd); |
239 |
|
|
errno = saved_errno; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
return (error); |
243 |
|
|
} |
244 |
|
|
DEF_WEAK(fts_close); |
245 |
|
|
|
246 |
|
|
/* |
247 |
|
|
* Special case of "/" at the end of the path so that slashes aren't |
248 |
|
|
* appended which would cause paths to be written as "....//foo". |
249 |
|
|
*/ |
250 |
|
|
#define NAPPEND(p) \ |
251 |
|
|
(p->fts_path[p->fts_pathlen - 1] == '/' \ |
252 |
|
|
? p->fts_pathlen - 1 : p->fts_pathlen) |
253 |
|
|
|
254 |
|
|
FTSENT * |
255 |
|
|
fts_read(FTS *sp) |
256 |
|
|
{ |
257 |
|
|
FTSENT *p, *tmp; |
258 |
|
|
int instr; |
259 |
|
|
char *t; |
260 |
|
|
int saved_errno; |
261 |
|
|
|
262 |
|
|
/* If finished or unrecoverable error, return NULL. */ |
263 |
|
|
if (sp->fts_cur == NULL || ISSET(FTS_STOP)) |
264 |
|
|
return (NULL); |
265 |
|
|
|
266 |
|
|
/* Set current node pointer. */ |
267 |
|
|
p = sp->fts_cur; |
268 |
|
|
|
269 |
|
|
/* Save and zero out user instructions. */ |
270 |
|
|
instr = p->fts_instr; |
271 |
|
|
p->fts_instr = FTS_NOINSTR; |
272 |
|
|
|
273 |
|
|
/* Any type of file may be re-visited; re-stat and re-turn. */ |
274 |
|
|
if (instr == FTS_AGAIN) { |
275 |
|
|
p->fts_info = fts_stat(sp, p, 0, -1); |
276 |
|
|
return (p); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
/* |
280 |
|
|
* Following a symlink -- SLNONE test allows application to see |
281 |
|
|
* SLNONE and recover. If indirecting through a symlink, have |
282 |
|
|
* keep a pointer to current location. If unable to get that |
283 |
|
|
* pointer, follow fails. |
284 |
|
|
*/ |
285 |
|
|
if (instr == FTS_FOLLOW && |
286 |
|
|
(p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { |
287 |
|
|
p->fts_info = fts_stat(sp, p, 1, -1); |
288 |
|
|
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { |
289 |
|
|
if ((p->fts_symfd = |
290 |
|
|
open(".", O_RDONLY | O_CLOEXEC)) < 0) { |
291 |
|
|
p->fts_errno = errno; |
292 |
|
|
p->fts_info = FTS_ERR; |
293 |
|
|
} else |
294 |
|
|
p->fts_flags |= FTS_SYMFOLLOW; |
295 |
|
|
} |
296 |
|
|
return (p); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
/* Directory in pre-order. */ |
300 |
|
|
if (p->fts_info == FTS_D) { |
301 |
|
|
/* If skipped or crossed mount point, do post-order visit. */ |
302 |
|
|
if (instr == FTS_SKIP || |
303 |
|
|
(ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { |
304 |
|
|
if (p->fts_flags & FTS_SYMFOLLOW) |
305 |
|
|
(void)close(p->fts_symfd); |
306 |
|
|
if (sp->fts_child) { |
307 |
|
|
fts_lfree(sp->fts_child); |
308 |
|
|
sp->fts_child = NULL; |
309 |
|
|
} |
310 |
|
|
p->fts_info = FTS_DP; |
311 |
|
|
return (p); |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
/* Rebuild if only read the names and now traversing. */ |
315 |
|
|
if (sp->fts_child && ISSET(FTS_NAMEONLY)) { |
316 |
|
|
CLR(FTS_NAMEONLY); |
317 |
|
|
fts_lfree(sp->fts_child); |
318 |
|
|
sp->fts_child = NULL; |
319 |
|
|
} |
320 |
|
|
|
321 |
|
|
/* |
322 |
|
|
* Cd to the subdirectory. |
323 |
|
|
* |
324 |
|
|
* If have already read and now fail to chdir, whack the list |
325 |
|
|
* to make the names come out right, and set the parent errno |
326 |
|
|
* so the application will eventually get an error condition. |
327 |
|
|
* Set the FTS_DONTCHDIR flag so that when we logically change |
328 |
|
|
* directories back to the parent we don't do a chdir. |
329 |
|
|
* |
330 |
|
|
* If haven't read do so. If the read fails, fts_build sets |
331 |
|
|
* FTS_STOP or the fts_info field of the node. |
332 |
|
|
*/ |
333 |
|
|
if (sp->fts_child) { |
334 |
|
|
if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { |
335 |
|
|
p->fts_errno = errno; |
336 |
|
|
p->fts_flags |= FTS_DONTCHDIR; |
337 |
|
|
for (p = sp->fts_child; p; p = p->fts_link) |
338 |
|
|
p->fts_accpath = |
339 |
|
|
p->fts_parent->fts_accpath; |
340 |
|
|
} |
341 |
|
|
} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { |
342 |
|
|
if (ISSET(FTS_STOP)) |
343 |
|
|
return (NULL); |
344 |
|
|
return (p); |
345 |
|
|
} |
346 |
|
|
p = sp->fts_child; |
347 |
|
|
sp->fts_child = NULL; |
348 |
|
|
goto name; |
349 |
|
|
} |
350 |
|
|
|
351 |
|
|
/* Move to the next node on this level. */ |
352 |
|
|
next: tmp = p; |
353 |
|
|
if ((p = p->fts_link)) { |
354 |
|
|
free(tmp); |
355 |
|
|
|
356 |
|
|
/* |
357 |
|
|
* If reached the top, return to the original directory (or |
358 |
|
|
* the root of the tree), and load the paths for the next root. |
359 |
|
|
*/ |
360 |
|
|
if (p->fts_level == FTS_ROOTLEVEL) { |
361 |
|
|
if (FCHDIR(sp, sp->fts_rfd)) { |
362 |
|
|
SET(FTS_STOP); |
363 |
|
|
return (NULL); |
364 |
|
|
} |
365 |
|
|
fts_load(sp, p); |
366 |
|
|
return (sp->fts_cur = p); |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
/* |
370 |
|
|
* User may have called fts_set on the node. If skipped, |
371 |
|
|
* ignore. If followed, get a file descriptor so we can |
372 |
|
|
* get back if necessary. |
373 |
|
|
*/ |
374 |
|
|
if (p->fts_instr == FTS_SKIP) |
375 |
|
|
goto next; |
376 |
|
|
if (p->fts_instr == FTS_FOLLOW) { |
377 |
|
|
p->fts_info = fts_stat(sp, p, 1, -1); |
378 |
|
|
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { |
379 |
|
|
if ((p->fts_symfd = |
380 |
|
|
open(".", O_RDONLY | O_CLOEXEC)) < 0) { |
381 |
|
|
p->fts_errno = errno; |
382 |
|
|
p->fts_info = FTS_ERR; |
383 |
|
|
} else |
384 |
|
|
p->fts_flags |= FTS_SYMFOLLOW; |
385 |
|
|
} |
386 |
|
|
p->fts_instr = FTS_NOINSTR; |
387 |
|
|
} |
388 |
|
|
|
389 |
|
|
name: t = sp->fts_path + NAPPEND(p->fts_parent); |
390 |
|
|
*t++ = '/'; |
391 |
|
|
memmove(t, p->fts_name, p->fts_namelen + 1); |
392 |
|
|
return (sp->fts_cur = p); |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
/* Move up to the parent node. */ |
396 |
|
|
p = tmp->fts_parent; |
397 |
|
|
free(tmp); |
398 |
|
|
|
399 |
|
|
if (p->fts_level == FTS_ROOTPARENTLEVEL) { |
400 |
|
|
/* |
401 |
|
|
* Done; free everything up and set errno to 0 so the user |
402 |
|
|
* can distinguish between error and EOF. |
403 |
|
|
*/ |
404 |
|
|
free(p); |
405 |
|
|
errno = 0; |
406 |
|
|
return (sp->fts_cur = NULL); |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
/* NUL terminate the pathname. */ |
410 |
|
|
sp->fts_path[p->fts_pathlen] = '\0'; |
411 |
|
|
|
412 |
|
|
/* |
413 |
|
|
* Return to the parent directory. If at a root node or came through |
414 |
|
|
* a symlink, go back through the file descriptor. Otherwise, cd up |
415 |
|
|
* one directory. |
416 |
|
|
*/ |
417 |
|
|
if (p->fts_level == FTS_ROOTLEVEL) { |
418 |
|
|
if (FCHDIR(sp, sp->fts_rfd)) { |
419 |
|
|
SET(FTS_STOP); |
420 |
|
|
sp->fts_cur = p; |
421 |
|
|
return (NULL); |
422 |
|
|
} |
423 |
|
|
} else if (p->fts_flags & FTS_SYMFOLLOW) { |
424 |
|
|
if (FCHDIR(sp, p->fts_symfd)) { |
425 |
|
|
saved_errno = errno; |
426 |
|
|
(void)close(p->fts_symfd); |
427 |
|
|
errno = saved_errno; |
428 |
|
|
SET(FTS_STOP); |
429 |
|
|
sp->fts_cur = p; |
430 |
|
|
return (NULL); |
431 |
|
|
} |
432 |
|
|
(void)close(p->fts_symfd); |
433 |
|
|
} else if (!(p->fts_flags & FTS_DONTCHDIR) && |
434 |
|
|
fts_safe_changedir(sp, p->fts_parent, -1, "..")) { |
435 |
|
|
SET(FTS_STOP); |
436 |
|
|
sp->fts_cur = p; |
437 |
|
|
return (NULL); |
438 |
|
|
} |
439 |
|
|
p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; |
440 |
|
|
return (sp->fts_cur = p); |
441 |
|
|
} |
442 |
|
|
DEF_WEAK(fts_read); |
443 |
|
|
|
444 |
|
|
/* |
445 |
|
|
* Fts_set takes the stream as an argument although it's not used in this |
446 |
|
|
* implementation; it would be necessary if anyone wanted to add global |
447 |
|
|
* semantics to fts using fts_set. An error return is allowed for similar |
448 |
|
|
* reasons. |
449 |
|
|
*/ |
450 |
|
|
int |
451 |
|
|
fts_set(FTS *sp, FTSENT *p, int instr) |
452 |
|
|
{ |
453 |
|
|
if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && |
454 |
|
|
instr != FTS_NOINSTR && instr != FTS_SKIP) { |
455 |
|
|
errno = EINVAL; |
456 |
|
|
return (1); |
457 |
|
|
} |
458 |
|
|
p->fts_instr = instr; |
459 |
|
|
return (0); |
460 |
|
|
} |
461 |
|
|
DEF_WEAK(fts_set); |
462 |
|
|
|
463 |
|
|
FTSENT * |
464 |
|
|
fts_children(FTS *sp, int instr) |
465 |
|
|
{ |
466 |
|
|
FTSENT *p; |
467 |
|
|
int fd; |
468 |
|
|
|
469 |
|
|
if (instr && instr != FTS_NAMEONLY) { |
470 |
|
|
errno = EINVAL; |
471 |
|
|
return (NULL); |
472 |
|
|
} |
473 |
|
|
|
474 |
|
|
/* Set current node pointer. */ |
475 |
|
|
p = sp->fts_cur; |
476 |
|
|
|
477 |
|
|
/* |
478 |
|
|
* Errno set to 0 so user can distinguish empty directory from |
479 |
|
|
* an error. |
480 |
|
|
*/ |
481 |
|
|
errno = 0; |
482 |
|
|
|
483 |
|
|
/* Fatal errors stop here. */ |
484 |
|
|
if (ISSET(FTS_STOP)) |
485 |
|
|
return (NULL); |
486 |
|
|
|
487 |
|
|
/* Return logical hierarchy of user's arguments. */ |
488 |
|
|
if (p->fts_info == FTS_INIT) |
489 |
|
|
return (p->fts_link); |
490 |
|
|
|
491 |
|
|
/* |
492 |
|
|
* If not a directory being visited in pre-order, stop here. Could |
493 |
|
|
* allow FTS_DNR, assuming the user has fixed the problem, but the |
494 |
|
|
* same effect is available with FTS_AGAIN. |
495 |
|
|
*/ |
496 |
|
|
if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) |
497 |
|
|
return (NULL); |
498 |
|
|
|
499 |
|
|
/* Free up any previous child list. */ |
500 |
|
|
if (sp->fts_child) |
501 |
|
|
fts_lfree(sp->fts_child); |
502 |
|
|
|
503 |
|
|
if (instr == FTS_NAMEONLY) { |
504 |
|
|
SET(FTS_NAMEONLY); |
505 |
|
|
instr = BNAMES; |
506 |
|
|
} else |
507 |
|
|
instr = BCHILD; |
508 |
|
|
|
509 |
|
|
/* |
510 |
|
|
* If using chdir on a relative path and called BEFORE fts_read does |
511 |
|
|
* its chdir to the root of a traversal, we can lose -- we need to |
512 |
|
|
* chdir into the subdirectory, and we don't know where the current |
513 |
|
|
* directory is, so we can't get back so that the upcoming chdir by |
514 |
|
|
* fts_read will work. |
515 |
|
|
*/ |
516 |
|
|
if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || |
517 |
|
|
ISSET(FTS_NOCHDIR)) |
518 |
|
|
return (sp->fts_child = fts_build(sp, instr)); |
519 |
|
|
|
520 |
|
|
if ((fd = open(".", O_RDONLY | O_CLOEXEC)) < 0) |
521 |
|
|
return (NULL); |
522 |
|
|
sp->fts_child = fts_build(sp, instr); |
523 |
|
|
if (fchdir(fd)) { |
524 |
|
|
(void)close(fd); |
525 |
|
|
return (NULL); |
526 |
|
|
} |
527 |
|
|
(void)close(fd); |
528 |
|
|
return (sp->fts_child); |
529 |
|
|
} |
530 |
|
|
DEF_WEAK(fts_children); |
531 |
|
|
|
532 |
|
|
/* |
533 |
|
|
* This is the tricky part -- do not casually change *anything* in here. The |
534 |
|
|
* idea is to build the linked list of entries that are used by fts_children |
535 |
|
|
* and fts_read. There are lots of special cases. |
536 |
|
|
* |
537 |
|
|
* The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is |
538 |
|
|
* set and it's a physical walk (so that symbolic links can't be directories), |
539 |
|
|
* we can do things quickly. First, if it's a 4.4BSD file system, the type |
540 |
|
|
* of the file is in the directory entry. Otherwise, we assume that the number |
541 |
|
|
* of subdirectories in a node is equal to the number of links to the parent. |
542 |
|
|
* The former skips all stat calls. The latter skips stat calls in any leaf |
543 |
|
|
* directories and for any files after the subdirectories in the directory have |
544 |
|
|
* been found, cutting the stat calls by about 2/3. |
545 |
|
|
*/ |
546 |
|
|
static FTSENT * |
547 |
|
|
fts_build(FTS *sp, int type) |
548 |
|
|
{ |
549 |
|
|
struct dirent *dp; |
550 |
|
|
FTSENT *p, *head; |
551 |
|
|
FTSENT *cur, *tail; |
552 |
|
|
DIR *dirp; |
553 |
|
|
void *oldaddr; |
554 |
|
|
size_t len, maxlen; |
555 |
|
|
int nitems, cderrno, descend, level, nlinks, nostat, doadjust; |
556 |
|
|
int saved_errno; |
557 |
|
|
char *cp; |
558 |
|
|
|
559 |
|
|
/* Set current node pointer. */ |
560 |
|
|
cur = sp->fts_cur; |
561 |
|
|
|
562 |
|
|
/* |
563 |
|
|
* Open the directory for reading. If this fails, we're done. |
564 |
|
|
* If being called from fts_read, set the fts_info field. |
565 |
|
|
*/ |
566 |
|
|
if ((dirp = opendir(cur->fts_accpath)) == NULL) { |
567 |
|
|
if (type == BREAD) { |
568 |
|
|
cur->fts_info = FTS_DNR; |
569 |
|
|
cur->fts_errno = errno; |
570 |
|
|
} |
571 |
|
|
return (NULL); |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
/* |
575 |
|
|
* Nlinks is the number of possible entries of type directory in the |
576 |
|
|
* directory if we're cheating on stat calls, 0 if we're not doing |
577 |
|
|
* any stat calls at all, -1 if we're doing stats on everything. |
578 |
|
|
*/ |
579 |
|
|
if (type == BNAMES) |
580 |
|
|
nlinks = 0; |
581 |
|
|
else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { |
582 |
|
|
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); |
583 |
|
|
nostat = 1; |
584 |
|
|
} else { |
585 |
|
|
nlinks = -1; |
586 |
|
|
nostat = 0; |
587 |
|
|
} |
588 |
|
|
|
589 |
|
|
#ifdef notdef |
590 |
|
|
(void)printf("nlinks == %d (cur: %u)\n", nlinks, cur->fts_nlink); |
591 |
|
|
(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", |
592 |
|
|
ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); |
593 |
|
|
#endif |
594 |
|
|
/* |
595 |
|
|
* If we're going to need to stat anything or we want to descend |
596 |
|
|
* and stay in the directory, chdir. If this fails we keep going, |
597 |
|
|
* but set a flag so we don't chdir after the post-order visit. |
598 |
|
|
* We won't be able to stat anything, but we can still return the |
599 |
|
|
* names themselves. Note, that since fts_read won't be able to |
600 |
|
|
* chdir into the directory, it will have to return different path |
601 |
|
|
* names than before, i.e. "a/b" instead of "b". Since the node |
602 |
|
|
* has already been visited in pre-order, have to wait until the |
603 |
|
|
* post-order visit to return the error. There is a special case |
604 |
|
|
* here, if there was nothing to stat then it's not an error to |
605 |
|
|
* not be able to stat. This is all fairly nasty. If a program |
606 |
|
|
* needed sorted entries or stat information, they had better be |
607 |
|
|
* checking FTS_NS on the returned nodes. |
608 |
|
|
*/ |
609 |
|
|
cderrno = 0; |
610 |
|
|
if (nlinks || type == BREAD) { |
611 |
|
|
if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { |
612 |
|
|
if (nlinks && type == BREAD) |
613 |
|
|
cur->fts_errno = errno; |
614 |
|
|
cur->fts_flags |= FTS_DONTCHDIR; |
615 |
|
|
descend = 0; |
616 |
|
|
cderrno = errno; |
617 |
|
|
(void)closedir(dirp); |
618 |
|
|
dirp = NULL; |
619 |
|
|
} else |
620 |
|
|
descend = 1; |
621 |
|
|
} else |
622 |
|
|
descend = 0; |
623 |
|
|
|
624 |
|
|
/* |
625 |
|
|
* Figure out the max file name length that can be stored in the |
626 |
|
|
* current path -- the inner loop allocates more path as necessary. |
627 |
|
|
* We really wouldn't have to do the maxlen calculations here, we |
628 |
|
|
* could do them in fts_read before returning the path, but it's a |
629 |
|
|
* lot easier here since the length is part of the dirent structure. |
630 |
|
|
* |
631 |
|
|
* If not changing directories set a pointer so that can just append |
632 |
|
|
* each new name into the path. |
633 |
|
|
*/ |
634 |
|
|
len = NAPPEND(cur); |
635 |
|
|
if (ISSET(FTS_NOCHDIR)) { |
636 |
|
|
cp = sp->fts_path + len; |
637 |
|
|
*cp++ = '/'; |
638 |
|
|
} |
639 |
|
|
len++; |
640 |
|
|
maxlen = sp->fts_pathlen - len; |
641 |
|
|
|
642 |
|
|
/* |
643 |
|
|
* fts_level is signed so we must prevent it from wrapping |
644 |
|
|
* around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL. |
645 |
|
|
*/ |
646 |
|
|
level = cur->fts_level; |
647 |
|
|
if (level < FTS_MAXLEVEL) |
648 |
|
|
level++; |
649 |
|
|
|
650 |
|
|
/* Read the directory, attaching each entry to the `link' pointer. */ |
651 |
|
|
doadjust = 0; |
652 |
|
|
for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { |
653 |
|
|
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) |
654 |
|
|
continue; |
655 |
|
|
|
656 |
|
|
if (!(p = fts_alloc(sp, dp->d_name, dp->d_namlen))) |
657 |
|
|
goto mem1; |
658 |
|
|
if (dp->d_namlen >= maxlen) { /* include space for NUL */ |
659 |
|
|
oldaddr = sp->fts_path; |
660 |
|
|
if (fts_palloc(sp, dp->d_namlen +len + 1)) { |
661 |
|
|
/* |
662 |
|
|
* No more memory for path or structures. Save |
663 |
|
|
* errno, free up the current structure and the |
664 |
|
|
* structures already allocated. |
665 |
|
|
*/ |
666 |
|
|
mem1: saved_errno = errno; |
667 |
|
|
free(p); |
668 |
|
|
fts_lfree(head); |
669 |
|
|
(void)closedir(dirp); |
670 |
|
|
cur->fts_info = FTS_ERR; |
671 |
|
|
SET(FTS_STOP); |
672 |
|
|
errno = saved_errno; |
673 |
|
|
return (NULL); |
674 |
|
|
} |
675 |
|
|
/* Did realloc() change the pointer? */ |
676 |
|
|
if (oldaddr != sp->fts_path) { |
677 |
|
|
doadjust = 1; |
678 |
|
|
if (ISSET(FTS_NOCHDIR)) |
679 |
|
|
cp = sp->fts_path + len; |
680 |
|
|
} |
681 |
|
|
maxlen = sp->fts_pathlen - len; |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
p->fts_level = level; |
685 |
|
|
p->fts_parent = sp->fts_cur; |
686 |
|
|
p->fts_pathlen = len + dp->d_namlen; |
687 |
|
|
if (p->fts_pathlen < len) { |
688 |
|
|
/* |
689 |
|
|
* If we wrap, free up the current structure and |
690 |
|
|
* the structures already allocated, then error |
691 |
|
|
* out with ENAMETOOLONG. |
692 |
|
|
*/ |
693 |
|
|
free(p); |
694 |
|
|
fts_lfree(head); |
695 |
|
|
(void)closedir(dirp); |
696 |
|
|
cur->fts_info = FTS_ERR; |
697 |
|
|
SET(FTS_STOP); |
698 |
|
|
errno = ENAMETOOLONG; |
699 |
|
|
return (NULL); |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
if (cderrno) { |
703 |
|
|
if (nlinks) { |
704 |
|
|
p->fts_info = FTS_NS; |
705 |
|
|
p->fts_errno = cderrno; |
706 |
|
|
} else |
707 |
|
|
p->fts_info = FTS_NSOK; |
708 |
|
|
p->fts_accpath = cur->fts_accpath; |
709 |
|
|
} else if (nlinks == 0 |
710 |
|
|
#ifdef DT_DIR |
711 |
|
|
|| (nostat && |
712 |
|
|
dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) |
713 |
|
|
#endif |
714 |
|
|
) { |
715 |
|
|
p->fts_accpath = |
716 |
|
|
ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; |
717 |
|
|
p->fts_info = FTS_NSOK; |
718 |
|
|
} else { |
719 |
|
|
/* Build a file name for fts_stat to stat. */ |
720 |
|
|
if (ISSET(FTS_NOCHDIR)) { |
721 |
|
|
p->fts_accpath = p->fts_path; |
722 |
|
|
memmove(cp, p->fts_name, p->fts_namelen + 1); |
723 |
|
|
p->fts_info = fts_stat(sp, p, 0, dirfd(dirp)); |
724 |
|
|
} else { |
725 |
|
|
p->fts_accpath = p->fts_name; |
726 |
|
|
p->fts_info = fts_stat(sp, p, 0, -1); |
727 |
|
|
} |
728 |
|
|
|
729 |
|
|
/* Decrement link count if applicable. */ |
730 |
|
|
if (nlinks > 0 && (p->fts_info == FTS_D || |
731 |
|
|
p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) |
732 |
|
|
--nlinks; |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
/* We walk in directory order so "ls -f" doesn't get upset. */ |
736 |
|
|
p->fts_link = NULL; |
737 |
|
|
if (head == NULL) |
738 |
|
|
head = tail = p; |
739 |
|
|
else { |
740 |
|
|
tail->fts_link = p; |
741 |
|
|
tail = p; |
742 |
|
|
} |
743 |
|
|
++nitems; |
744 |
|
|
} |
745 |
|
|
if (dirp) |
746 |
|
|
(void)closedir(dirp); |
747 |
|
|
|
748 |
|
|
/* |
749 |
|
|
* If realloc() changed the address of the path, adjust the |
750 |
|
|
* addresses for the rest of the tree and the dir list. |
751 |
|
|
*/ |
752 |
|
|
if (doadjust) |
753 |
|
|
fts_padjust(sp, head); |
754 |
|
|
|
755 |
|
|
/* |
756 |
|
|
* If not changing directories, reset the path back to original |
757 |
|
|
* state. |
758 |
|
|
*/ |
759 |
|
|
if (ISSET(FTS_NOCHDIR)) { |
760 |
|
|
if (len == sp->fts_pathlen || nitems == 0) |
761 |
|
|
--cp; |
762 |
|
|
*cp = '\0'; |
763 |
|
|
} |
764 |
|
|
|
765 |
|
|
/* |
766 |
|
|
* If descended after called from fts_children or after called from |
767 |
|
|
* fts_read and nothing found, get back. At the root level we use |
768 |
|
|
* the saved fd; if one of fts_open()'s arguments is a relative path |
769 |
|
|
* to an empty directory, we wind up here with no other way back. If |
770 |
|
|
* can't get back, we're done. |
771 |
|
|
*/ |
772 |
|
|
if (descend && (type == BCHILD || !nitems) && |
773 |
|
|
(cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) : |
774 |
|
|
fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { |
775 |
|
|
cur->fts_info = FTS_ERR; |
776 |
|
|
SET(FTS_STOP); |
777 |
|
|
return (NULL); |
778 |
|
|
} |
779 |
|
|
|
780 |
|
|
/* If didn't find anything, return NULL. */ |
781 |
|
|
if (!nitems) { |
782 |
|
|
if (type == BREAD) |
783 |
|
|
cur->fts_info = FTS_DP; |
784 |
|
|
return (NULL); |
785 |
|
|
} |
786 |
|
|
|
787 |
|
|
/* Sort the entries. */ |
788 |
|
|
if (sp->fts_compar && nitems > 1) |
789 |
|
|
head = fts_sort(sp, head, nitems); |
790 |
|
|
return (head); |
791 |
|
|
} |
792 |
|
|
|
793 |
|
|
static u_short |
794 |
|
|
fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) |
795 |
|
|
{ |
796 |
|
|
FTSENT *t; |
797 |
|
|
dev_t dev; |
798 |
|
|
ino_t ino; |
799 |
|
|
struct stat *sbp, sb; |
800 |
|
|
int saved_errno; |
801 |
|
|
const char *path; |
802 |
|
|
|
803 |
|
|
if (dfd == -1) { |
804 |
|
|
path = p->fts_accpath; |
805 |
|
|
dfd = AT_FDCWD; |
806 |
|
|
} else |
807 |
|
|
path = p->fts_name; |
808 |
|
|
|
809 |
|
|
/* If user needs stat info, stat buffer already allocated. */ |
810 |
|
|
sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; |
811 |
|
|
|
812 |
|
|
/* |
813 |
|
|
* If doing a logical walk, or application requested FTS_FOLLOW, do |
814 |
|
|
* a stat(2). If that fails, check for a non-existent symlink. If |
815 |
|
|
* fail, set the errno from the stat call. |
816 |
|
|
*/ |
817 |
|
|
if (ISSET(FTS_LOGICAL) || follow) { |
818 |
|
|
if (fstatat(dfd, path, sbp, 0)) { |
819 |
|
|
saved_errno = errno; |
820 |
|
|
if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { |
821 |
|
|
errno = 0; |
822 |
|
|
return (FTS_SLNONE); |
823 |
|
|
} |
824 |
|
|
p->fts_errno = saved_errno; |
825 |
|
|
goto err; |
826 |
|
|
} |
827 |
|
|
} else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { |
828 |
|
|
p->fts_errno = errno; |
829 |
|
|
err: memset(sbp, 0, sizeof(struct stat)); |
830 |
|
|
return (FTS_NS); |
831 |
|
|
} |
832 |
|
|
|
833 |
|
|
if (S_ISDIR(sbp->st_mode)) { |
834 |
|
|
/* |
835 |
|
|
* Set the device/inode. Used to find cycles and check for |
836 |
|
|
* crossing mount points. Also remember the link count, used |
837 |
|
|
* in fts_build to limit the number of stat calls. It is |
838 |
|
|
* understood that these fields are only referenced if fts_info |
839 |
|
|
* is set to FTS_D. |
840 |
|
|
*/ |
841 |
|
|
dev = p->fts_dev = sbp->st_dev; |
842 |
|
|
ino = p->fts_ino = sbp->st_ino; |
843 |
|
|
p->fts_nlink = sbp->st_nlink; |
844 |
|
|
|
845 |
|
|
if (ISDOT(p->fts_name)) |
846 |
|
|
return (FTS_DOT); |
847 |
|
|
|
848 |
|
|
/* |
849 |
|
|
* Cycle detection is done by brute force when the directory |
850 |
|
|
* is first encountered. If the tree gets deep enough or the |
851 |
|
|
* number of symbolic links to directories is high enough, |
852 |
|
|
* something faster might be worthwhile. |
853 |
|
|
*/ |
854 |
|
|
for (t = p->fts_parent; |
855 |
|
|
t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) |
856 |
|
|
if (ino == t->fts_ino && dev == t->fts_dev) { |
857 |
|
|
p->fts_cycle = t; |
858 |
|
|
return (FTS_DC); |
859 |
|
|
} |
860 |
|
|
return (FTS_D); |
861 |
|
|
} |
862 |
|
|
if (S_ISLNK(sbp->st_mode)) |
863 |
|
|
return (FTS_SL); |
864 |
|
|
if (S_ISREG(sbp->st_mode)) |
865 |
|
|
return (FTS_F); |
866 |
|
|
return (FTS_DEFAULT); |
867 |
|
|
} |
868 |
|
|
|
869 |
|
|
static FTSENT * |
870 |
|
|
fts_sort(FTS *sp, FTSENT *head, int nitems) |
871 |
|
|
{ |
872 |
|
|
FTSENT **ap, *p; |
873 |
|
|
|
874 |
|
|
/* |
875 |
|
|
* Construct an array of pointers to the structures and call qsort(3). |
876 |
|
|
* Reassemble the array in the order returned by qsort. If unable to |
877 |
|
|
* sort for memory reasons, return the directory entries in their |
878 |
|
|
* current order. Allocate enough space for the current needs plus |
879 |
|
|
* 40 so don't realloc one entry at a time. |
880 |
|
|
*/ |
881 |
|
|
if (nitems > sp->fts_nitems) { |
882 |
|
|
struct _ftsent **a; |
883 |
|
|
|
884 |
|
|
if ((a = reallocarray(sp->fts_array, |
885 |
|
|
nitems + 40, sizeof(FTSENT *))) == NULL) { |
886 |
|
|
free(sp->fts_array); |
887 |
|
|
sp->fts_array = NULL; |
888 |
|
|
sp->fts_nitems = 0; |
889 |
|
|
return (head); |
890 |
|
|
} |
891 |
|
|
sp->fts_nitems = nitems + 40; |
892 |
|
|
sp->fts_array = a; |
893 |
|
|
} |
894 |
|
|
for (ap = sp->fts_array, p = head; p; p = p->fts_link) |
895 |
|
|
*ap++ = p; |
896 |
|
|
qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); |
897 |
|
|
for (head = *(ap = sp->fts_array); --nitems; ++ap) |
898 |
|
|
ap[0]->fts_link = ap[1]; |
899 |
|
|
ap[0]->fts_link = NULL; |
900 |
|
|
return (head); |
901 |
|
|
} |
902 |
|
|
|
903 |
|
|
static FTSENT * |
904 |
|
|
fts_alloc(FTS *sp, char *name, size_t namelen) |
905 |
|
|
{ |
906 |
|
|
FTSENT *p; |
907 |
|
|
size_t len; |
908 |
|
|
|
909 |
|
|
/* |
910 |
|
|
* The file name is a variable length array and no stat structure is |
911 |
|
|
* necessary if the user has set the nostat bit. Allocate the FTSENT |
912 |
|
|
* structure, the file name and the stat structure in one chunk, but |
913 |
|
|
* be careful that the stat structure is reasonably aligned. Since the |
914 |
|
|
* fts_name field is declared to be of size 1, the fts_name pointer is |
915 |
|
|
* namelen + 2 before the first possible address of the stat structure. |
916 |
|
|
*/ |
917 |
|
|
len = sizeof(FTSENT) + namelen; |
918 |
|
|
if (!ISSET(FTS_NOSTAT)) |
919 |
|
|
len += sizeof(struct stat) + ALIGNBYTES; |
920 |
|
|
if ((p = calloc(1, len)) == NULL) |
921 |
|
|
return (NULL); |
922 |
|
|
|
923 |
|
|
p->fts_path = sp->fts_path; |
924 |
|
|
p->fts_namelen = namelen; |
925 |
|
|
p->fts_instr = FTS_NOINSTR; |
926 |
|
|
if (!ISSET(FTS_NOSTAT)) |
927 |
|
|
p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); |
928 |
|
|
memcpy(p->fts_name, name, namelen); |
929 |
|
|
|
930 |
|
|
return (p); |
931 |
|
|
} |
932 |
|
|
|
933 |
|
|
static void |
934 |
|
|
fts_lfree(FTSENT *head) |
935 |
|
|
{ |
936 |
|
|
FTSENT *p; |
937 |
|
|
|
938 |
|
|
/* Free a linked list of structures. */ |
939 |
|
|
while ((p = head)) { |
940 |
|
|
head = head->fts_link; |
941 |
|
|
free(p); |
942 |
|
|
} |
943 |
|
|
} |
944 |
|
|
|
945 |
|
|
/* |
946 |
|
|
* Allow essentially unlimited paths; find, rm, ls should all work on any tree. |
947 |
|
|
* Most systems will allow creation of paths much longer than PATH_MAX, even |
948 |
|
|
* though the kernel won't resolve them. Add the size (not just what's needed) |
949 |
|
|
* plus 256 bytes so don't realloc the path 2 bytes at a time. |
950 |
|
|
*/ |
951 |
|
|
static int |
952 |
|
|
fts_palloc(FTS *sp, size_t more) |
953 |
|
|
{ |
954 |
|
|
char *p; |
955 |
|
|
|
956 |
|
|
/* |
957 |
|
|
* Check for possible wraparound. |
958 |
|
|
*/ |
959 |
|
|
more += 256; |
960 |
|
|
if (sp->fts_pathlen + more < sp->fts_pathlen) { |
961 |
|
|
free(sp->fts_path); |
962 |
|
|
sp->fts_path = NULL; |
963 |
|
|
errno = ENAMETOOLONG; |
964 |
|
|
return (1); |
965 |
|
|
} |
966 |
|
|
p = recallocarray(sp->fts_path, sp->fts_pathlen, |
967 |
|
|
sp->fts_pathlen + more, 1); |
968 |
|
|
if (p == NULL) { |
969 |
|
|
free(sp->fts_path); |
970 |
|
|
sp->fts_path = NULL; |
971 |
|
|
return (1); |
972 |
|
|
} |
973 |
|
|
sp->fts_pathlen += more; |
974 |
|
|
sp->fts_path = p; |
975 |
|
|
return (0); |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
/* |
979 |
|
|
* When the path is realloc'd, have to fix all of the pointers in structures |
980 |
|
|
* already returned. |
981 |
|
|
*/ |
982 |
|
|
static void |
983 |
|
|
fts_padjust(FTS *sp, FTSENT *head) |
984 |
|
|
{ |
985 |
|
|
FTSENT *p; |
986 |
|
|
char *addr = sp->fts_path; |
987 |
|
|
|
988 |
|
|
#define ADJUST(p) { \ |
989 |
|
|
if ((p)->fts_accpath != (p)->fts_name) { \ |
990 |
|
|
(p)->fts_accpath = \ |
991 |
|
|
(char *)addr + ((p)->fts_accpath - (p)->fts_path); \ |
992 |
|
|
} \ |
993 |
|
|
(p)->fts_path = addr; \ |
994 |
|
|
} |
995 |
|
|
/* Adjust the current set of children. */ |
996 |
|
|
for (p = sp->fts_child; p; p = p->fts_link) |
997 |
|
|
ADJUST(p); |
998 |
|
|
|
999 |
|
|
/* Adjust the rest of the tree, including the current level. */ |
1000 |
|
|
for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { |
1001 |
|
|
ADJUST(p); |
1002 |
|
|
p = p->fts_link ? p->fts_link : p->fts_parent; |
1003 |
|
|
} |
1004 |
|
|
} |
1005 |
|
|
|
1006 |
|
|
static size_t |
1007 |
|
|
fts_maxarglen(char * const *argv) |
1008 |
|
|
{ |
1009 |
|
|
size_t len, max; |
1010 |
|
|
|
1011 |
|
|
for (max = 0; *argv; ++argv) |
1012 |
|
|
if ((len = strlen(*argv)) > max) |
1013 |
|
|
max = len; |
1014 |
|
|
return (max + 1); |
1015 |
|
|
} |
1016 |
|
|
|
1017 |
|
|
/* |
1018 |
|
|
* Change to dir specified by fd or p->fts_accpath without getting |
1019 |
|
|
* tricked by someone changing the world out from underneath us. |
1020 |
|
|
* Assumes p->fts_dev and p->fts_ino are filled in. |
1021 |
|
|
*/ |
1022 |
|
|
static int |
1023 |
|
|
fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path) |
1024 |
|
|
{ |
1025 |
|
|
int ret, oerrno, newfd; |
1026 |
|
|
struct stat sb; |
1027 |
|
|
|
1028 |
|
|
newfd = fd; |
1029 |
|
|
if (ISSET(FTS_NOCHDIR)) |
1030 |
|
|
return (0); |
1031 |
|
|
if (fd < 0 && (newfd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC)) < 0) |
1032 |
|
|
return (-1); |
1033 |
|
|
if (fstat(newfd, &sb)) { |
1034 |
|
|
ret = -1; |
1035 |
|
|
goto bail; |
1036 |
|
|
} |
1037 |
|
|
if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { |
1038 |
|
|
errno = ENOENT; /* disinformation */ |
1039 |
|
|
ret = -1; |
1040 |
|
|
goto bail; |
1041 |
|
|
} |
1042 |
|
|
ret = fchdir(newfd); |
1043 |
|
|
bail: |
1044 |
|
|
oerrno = errno; |
1045 |
|
|
if (fd < 0) |
1046 |
|
|
(void)close(newfd); |
1047 |
|
|
errno = oerrno; |
1048 |
|
|
return (ret); |
1049 |
|
|
} |