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