VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/fts.c@ 3387

Last change on this file since 3387 was 3292, checked in by bird, 5 years ago

fts.c: Solaris 11 build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.6 KB
Line 
1/* $NetBSD: __fts13.c,v 1.44 2005/01/19 00:59:48 mycroft 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#ifdef __sun__
33# define _POSIX_C_SOURCE 199506L /* for dirfd() */
34# define __EXTENSIONS__ 1 /* for u_short and friends */
35#endif
36#if HAVE_NBTOOL_CONFIG_H
37#include "nbtool_config.h"
38#endif
39
40/*#include <sys/cdefs.h>*/
41#if defined(LIBC_SCCS) && !defined(lint)
42#if 0
43static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
44#else
45__RCSID("$NetBSD: __fts13.c,v 1.44 2005/01/19 00:59:48 mycroft Exp $");
46#endif
47#endif /* LIBC_SCCS and not lint */
48
49#include "config.h"
50/*#include "namespace.h"*/
51#ifndef _MSC_VER
52#include <sys/param.h>
53#endif
54#include <sys/stat.h>
55
56#include <assert.h>
57#include <dirent.h>
58#include <errno.h>
59#include <fcntl.h>
60#include "ftsfake.h"
61#include <stdlib.h>
62#include <string.h>
63#include <unistd.h>
64#ifdef HAVE_ALLOCA_H
65# include <alloca.h>
66#endif
67#include "kmkbuiltin.h" /* MAXPATHLEN for GNU/hurd */
68
69#ifdef __sun__
70# include "solfakes.h"
71# define dirfd(dir) ((dir)->d_fd)
72#endif
73#ifdef _MSC_VER
74# include "mscfakes.h"
75# define dirfd(dir) -1
76#endif
77
78#if ! HAVE_NBTOOL_CONFIG_H
79# if !defined(__sun__) && !defined(__gnu_linux__) && !defined(__HAIKU__)
80# define HAVE_STRUCT_DIRENT_D_NAMLEN 1
81# endif
82#endif
83
84#if 0
85#ifdef __weak_alias
86#ifdef __LIBC12_SOURCE__
87__weak_alias(fts_children,_fts_children)
88__weak_alias(fts_close,_fts_close)
89__weak_alias(fts_open,_fts_open)
90__weak_alias(fts_read,_fts_read)
91__weak_alias(fts_set,_fts_set)
92#endif /* __LIBC12_SOURCE__ */
93#endif /* __weak_alias */
94#endif
95
96#ifdef __LIBC12_SOURCE__
97#define STAT stat12
98#else
99#define STAT stat
100#endif
101
102#ifdef __LIBC12_SOURCE__
103__warn_references(fts_children,
104 "warning: reference to compatibility fts_children();"
105 " include <fts.h> for correct reference")
106__warn_references(fts_close,
107 "warning: reference to compatibility fts_close();"
108 " include <fts.h> for correct reference")
109__warn_references(fts_open,
110 "warning: reference to compatibility fts_open();"
111 " include <fts.h> for correct reference")
112__warn_references(fts_read,
113 "warning: reference to compatibility fts_read();"
114 " include <fts.h> for correct reference")
115__warn_references(fts_set,
116 "warning: reference to compatibility fts_set();"
117 " include <fts.h> for correct reference")
118#endif
119
120static FTSENT *fts_alloc(FTS *, const char *, size_t);
121static FTSENT *fts_build(FTS *, int);
122static void fts_lfree(FTSENT *);
123static void fts_load(FTS *, FTSENT *);
124static size_t fts_maxarglen(char * const *);
125static size_t fts_pow2(size_t);
126static int fts_palloc(FTS *, size_t);
127static void fts_padjust(FTS *, FTSENT *);
128static FTSENT *fts_sort(FTS *, FTSENT *, size_t);
129static u_short fts_stat(FTS *, FTSENT *, int);
130#ifdef _MSC_VER
131static u_short fts_stat_dirent(FTS *sp, FTSENT *p, int follow, struct dirent *pDirEnt);
132#endif
133static int fts_safe_changedir(const FTS *, const FTSENT *, int,
134 const char *);
135
136#ifdef _MSC_VER
137# undef HAVE_FCHDIR
138#endif
139
140#if defined(__EMX__) || defined(_MSC_VER)
141# define NEED_STRRSLASH
142# define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' )
143#else
144# define HAVE_FCHDIR
145# define IS_SLASH(ch) ( (ch) == '/' )
146#endif
147
148#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
149
150#define CLR(opt) (sp->fts_options &= ~(opt))
151#define ISSET(opt) (sp->fts_options & (opt))
152#define SET(opt) (sp->fts_options |= (opt))
153
154#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))
155#ifdef HAVE_FCHDIR
156#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
157#else
158#define FCHDIR(sp, rdir) CHDIR(sp, rdir)
159#endif
160
161
162/* fts_build flags */
163#define BCHILD 1 /* fts_children */
164#define BNAMES 2 /* fts_children, names only */
165#define BREAD 3 /* fts_read */
166
167#ifndef DTF_HIDEW
168#undef FTS_WHITEOUT
169#endif
170
171#ifndef _DIAGASSERT
172#define _DIAGASSERT assert
173#endif
174
175
176FTS *
177fts_open(argv, options, compar)
178 char * const *argv;
179 int options;
180 int (*compar)(const FTSENT **, const FTSENT **);
181{
182 FTS *sp;
183 FTSENT *p, *root;
184 size_t nitems;
185 FTSENT *parent, *tmp = NULL; /* pacify gcc */
186 size_t len;
187
188 _DIAGASSERT(argv != NULL);
189
190 /* Options check. */
191 if (options & ~FTS_OPTIONMASK) {
192 errno = EINVAL;
193 return (NULL);
194 }
195
196 /* Allocate/initialize the stream */
197 if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
198 return (NULL);
199 memset(sp, 0, sizeof(FTS));
200 sp->fts_compar = compar;
201 sp->fts_options = options;
202
203 /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
204 if (ISSET(FTS_LOGICAL))
205 SET(FTS_NOCHDIR);
206
207 /*
208 * Start out with 1K of path space, and enough, in any case,
209 * to hold the user's paths.
210 */
211 if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
212 goto mem1;
213
214 /* Allocate/initialize root's parent. */
215 if ((parent = fts_alloc(sp, "", 0)) == NULL)
216 goto mem2;
217 parent->fts_level = FTS_ROOTPARENTLEVEL;
218
219 /* Allocate/initialize root(s). */
220 for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
221 /* Don't allow zero-length paths. */
222 if ((len = strlen(*argv)) == 0) {
223 errno = ENOENT;
224 goto mem3;
225 }
226
227 if ((p = fts_alloc(sp, *argv, len)) == NULL)
228 goto mem3;
229 p->fts_level = FTS_ROOTLEVEL;
230 p->fts_parent = parent;
231 p->fts_accpath = p->fts_name;
232 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
233
234 /* Command-line "." and ".." are real directories. */
235 if (p->fts_info == FTS_DOT)
236 p->fts_info = FTS_D;
237
238 /*
239 * If comparison routine supplied, traverse in sorted
240 * order; otherwise traverse in the order specified.
241 */
242 if (compar) {
243 p->fts_link = root;
244 root = p;
245 } else {
246 p->fts_link = NULL;
247 if (root == NULL)
248 tmp = root = p;
249 else {
250 tmp->fts_link = p;
251 tmp = p;
252 }
253 }
254 }
255 if (compar && nitems > 1)
256 root = fts_sort(sp, root, nitems);
257
258 /*
259 * Allocate a dummy pointer and make fts_read think that we've just
260 * finished the node before the root(s); set p->fts_info to FTS_INIT
261 * so that everything about the "current" node is ignored.
262 */
263 if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
264 goto mem3;
265 sp->fts_cur->fts_link = root;
266 sp->fts_cur->fts_info = FTS_INIT;
267
268 /*
269 * If using chdir(2), grab a file descriptor pointing to dot to insure
270 * that we can get back here; this could be avoided for some paths,
271 * but almost certainly not worth the effort. Slashes, symbolic links,
272 * and ".." are all fairly nasty problems. Note, if we can't get the
273 * descriptor we run anyway, just more slowly.
274 */
275 if (!ISSET(FTS_NOCHDIR)) {
276#ifdef HAVE_FCHDIR
277 if ((sp->fts_rfd = open(".", O_RDONLY | KMK_OPEN_NO_INHERIT, 0)) == -1)
278 SET(FTS_NOCHDIR);
279 else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) {
280 close(sp->fts_rfd);
281 SET(FTS_NOCHDIR);
282 }
283#else
284 if ((sp->fts_rdir = getcwd(NULL, 0)) != NULL)
285 SET(FTS_NOCHDIR);
286#endif
287 }
288
289 return (sp);
290
291mem3: fts_lfree(root);
292 free(parent);
293mem2: free(sp->fts_path);
294mem1: free(sp);
295 return (NULL);
296}
297
298#ifdef NEED_STRRSLASH
299static char *strrslash(register char *psz)
300{
301 register char ch;
302 char *pszLast = NULL;
303 for (; (ch = *psz); psz++)
304 switch (ch)
305 {
306 case '/':
307 case '\\':
308 case ':':
309 pszLast = psz;
310 break;
311 }
312 return pszLast;
313}
314#endif
315
316static void
317fts_load(sp, p)
318 FTS *sp;
319 FTSENT *p;
320{
321 size_t len;
322 char *cp;
323
324 _DIAGASSERT(sp != NULL);
325 _DIAGASSERT(p != NULL);
326
327 /*
328 * Load the stream structure for the next traversal. Since we don't
329 * actually enter the directory until after the preorder visit, set
330 * the fts_accpath field specially so the chdir gets done to the right
331 * place and the user can access the first node. From fts_open it's
332 * known that the path will fit.
333 */
334 len = p->fts_pathlen = p->fts_namelen;
335 memmove(sp->fts_path, p->fts_name, len + 1);
336#ifdef NEED_STRRSLASH
337 if ((cp = strrslash(p->fts_name)) && (cp != p->fts_name || cp[1])) {
338#else
339 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
340#endif
341 len = strlen(++cp);
342 memmove(p->fts_name, cp, len + 1);
343 p->fts_namelen = len;
344 }
345 p->fts_accpath = p->fts_path = sp->fts_path;
346 sp->fts_dev = p->fts_dev;
347}
348
349int
350fts_close(sp)
351 FTS *sp;
352{
353 FTSENT *freep, *p;
354 int saved_errno = 0;
355
356 _DIAGASSERT(sp != NULL);
357
358 /*
359 * This still works if we haven't read anything -- the dummy structure
360 * points to the root list, so we step through to the end of the root
361 * list which has a valid parent pointer.
362 */
363 if (sp->fts_cur) {
364#ifndef _MSC_VER
365 if (ISSET(FTS_SYMFOLLOW))
366 (void)close(sp->fts_cur->fts_symfd);
367#endif
368 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
369 freep = p;
370 p = p->fts_link ? p->fts_link : p->fts_parent;
371 free(freep);
372 }
373 free(p);
374 }
375
376 /* Free up child linked list, sort array, path buffer. */
377 if (sp->fts_child)
378 fts_lfree(sp->fts_child);
379 if (sp->fts_array)
380 free(sp->fts_array);
381 free(sp->fts_path);
382
383 /* Return to original directory, save errno if necessary. */
384 if (!ISSET(FTS_NOCHDIR)) {
385#ifdef HAVE_FCHDIR
386 if (fchdir(sp->fts_rfd))
387 saved_errno = errno;
388 (void)close(sp->fts_rfd);
389#else
390 if (chdir(sp->fts_rdir))
391 saved_errno = errno;
392 free(sp->fts_rdir);
393 sp->fts_rdir = NULL;
394#endif
395 }
396
397 /* Free up the stream pointer. */
398 free(sp);
399 /* ISSET() is illegal after this, since the macro touches sp */
400
401 /* Set errno and return. */
402 if (saved_errno) {
403 errno = saved_errno;
404 return (-1);
405 }
406 return (0);
407}
408
409/*
410 * Special case a root of "/" so that slashes aren't appended which would
411 * cause paths to be written as "//foo".
412 */
413#define NAPPEND(p) \
414 (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
415 IS_SLASH(p->fts_path[0]) ? 0 : p->fts_pathlen)
416
417FTSENT *
418fts_read(sp)
419 FTS *sp;
420{
421 FTSENT *p, *tmp;
422 int instr;
423 char *t;
424 int saved_errno;
425
426 _DIAGASSERT(sp != NULL);
427
428 /* If finished or unrecoverable error, return NULL. */
429 if (sp->fts_cur == NULL || ISSET(FTS_STOP))
430 return (NULL);
431
432 /* Set current node pointer. */
433 p = sp->fts_cur;
434
435 /* Save and zero out user instructions. */
436 instr = p->fts_instr;
437 p->fts_instr = FTS_NOINSTR;
438
439 /* Any type of file may be re-visited; re-stat and re-turn. */
440 if (instr == FTS_AGAIN) {
441 p->fts_info = fts_stat(sp, p, 0);
442 return (p);
443 }
444
445 /*
446 * Following a symlink -- SLNONE test allows application to see
447 * SLNONE and recover. If indirecting through a symlink, have
448 * keep a pointer to current location. If unable to get that
449 * pointer, follow fails.
450 */
451 if (instr == FTS_FOLLOW &&
452 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
453 p->fts_info = fts_stat(sp, p, 1);
454 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
455#ifdef HAVE_FCHDIR
456 if ((p->fts_symfd = open(".", O_RDONLY | KMK_OPEN_NO_INHERIT, 0)) == -1) {
457 p->fts_errno = errno;
458 p->fts_info = FTS_ERR;
459 } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) {
460 p->fts_errno = errno;
461 p->fts_info = FTS_ERR;
462 close(p->fts_symfd);
463 } else
464 p->fts_flags |= FTS_SYMFOLLOW;
465#endif
466 }
467 return (p);
468 }
469
470 /* Directory in pre-order. */
471 if (p->fts_info == FTS_D) {
472 /* If skipped or crossed mount point, do post-order visit. */
473 if (instr == FTS_SKIP ||
474 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
475#ifdef HAVE_FCHDIR
476 if (p->fts_flags & FTS_SYMFOLLOW)
477 (void)close(p->fts_symfd);
478#endif
479 if (sp->fts_child) {
480 fts_lfree(sp->fts_child);
481 sp->fts_child = NULL;
482 }
483 p->fts_info = FTS_DP;
484 return (p);
485 }
486
487 /* Rebuild if only read the names and now traversing. */
488 if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
489 CLR(FTS_NAMEONLY);
490 fts_lfree(sp->fts_child);
491 sp->fts_child = NULL;
492 }
493
494 /*
495 * Cd to the subdirectory.
496 *
497 * If have already read and now fail to chdir, whack the list
498 * to make the names come out right, and set the parent errno
499 * so the application will eventually get an error condition.
500 * Set the FTS_DONTCHDIR flag so that when we logically change
501 * directories back to the parent we don't do a chdir.
502 *
503 * If haven't read do so. If the read fails, fts_build sets
504 * FTS_STOP or the fts_info field of the node.
505 */
506 if (sp->fts_child) {
507 if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
508 p->fts_errno = errno;
509 p->fts_flags |= FTS_DONTCHDIR;
510 for (p = sp->fts_child; p; p = p->fts_link)
511 p->fts_accpath =
512 p->fts_parent->fts_accpath;
513 }
514 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
515 if (ISSET(FTS_STOP))
516 return (NULL);
517 return (p);
518 }
519 p = sp->fts_child;
520 sp->fts_child = NULL;
521 goto name;
522 }
523
524 /* Move to the next node on this level. */
525next: tmp = p;
526 if ((p = p->fts_link) != NULL) {
527 free(tmp);
528
529 /*
530 * If reached the top, return to the original directory, and
531 * load the paths for the next root.
532 */
533 if (p->fts_level == FTS_ROOTLEVEL) {
534#ifdef HAVE_FCHDIR
535 if (FCHDIR(sp, sp->fts_rfd)) {
536#else
537 if (CHDIR(sp, sp->fts_rdir)) {
538#endif
539 SET(FTS_STOP);
540 return (NULL);
541 }
542 fts_load(sp, p);
543 return (sp->fts_cur = p);
544 }
545
546 /*
547 * User may have called fts_set on the node. If skipped,
548 * ignore. If followed, get a file descriptor so we can
549 * get back if necessary.
550 */
551 if (p->fts_instr == FTS_SKIP)
552 goto next;
553 if (p->fts_instr == FTS_FOLLOW) {
554 p->fts_info = fts_stat(sp, p, 1);
555 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
556#ifdef HAVE_FCHDIR
557 if ((p->fts_symfd =
558 open(".", O_RDONLY | KMK_OPEN_NO_INHERIT, 0)) == -1) {
559 p->fts_errno = errno;
560 p->fts_info = FTS_ERR;
561 } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) {
562 p->fts_errno = errno;
563 p->fts_info = FTS_ERR;
564 close(p->fts_symfd);
565 } else
566 p->fts_flags |= FTS_SYMFOLLOW;
567#endif
568 }
569 p->fts_instr = FTS_NOINSTR;
570 }
571
572name: t = sp->fts_path + NAPPEND(p->fts_parent);
573 *t++ = '/';
574 memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
575 return (sp->fts_cur = p);
576 }
577
578 /* Move up to the parent node. */
579 p = tmp->fts_parent;
580 free(tmp);
581
582 if (p->fts_level == FTS_ROOTPARENTLEVEL) {
583 /*
584 * Done; free everything up and set errno to 0 so the user
585 * can distinguish between error and EOF.
586 */
587 free(p);
588 errno = 0;
589 return (sp->fts_cur = NULL);
590 }
591
592 /* Nul terminate the pathname. */
593 sp->fts_path[p->fts_pathlen] = '\0';
594
595 /*
596 * Return to the parent directory. If at a root node or came through
597 * a symlink, go back through the file descriptor. Otherwise, cd up
598 * one directory.
599 */
600 if (p->fts_level == FTS_ROOTLEVEL) {
601#ifdef HAVE_FCHDIR
602 if (FCHDIR(sp, sp->fts_rfd)) {
603#else
604 if (CHDIR(sp, sp->fts_rdir)) {
605#endif
606 SET(FTS_STOP);
607 return (NULL);
608 }
609#ifdef HAVE_FCHDIR
610 } else if (p->fts_flags & FTS_SYMFOLLOW) {
611 if (FCHDIR(sp, p->fts_symfd)) {
612 saved_errno = errno;
613 (void)close(p->fts_symfd);
614 errno = saved_errno;
615 SET(FTS_STOP);
616 return (NULL);
617 }
618 (void)close(p->fts_symfd);
619#else
620 (void)saved_errno;
621#endif
622 } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
623 fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
624 SET(FTS_STOP);
625 return (NULL);
626 }
627 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
628 return (sp->fts_cur = p);
629}
630
631/*
632 * Fts_set takes the stream as an argument although it's not used in this
633 * implementation; it would be necessary if anyone wanted to add global
634 * semantics to fts using fts_set. An error return is allowed for similar
635 * reasons.
636 */
637/* ARGSUSED */
638int
639fts_set(sp, p, instr)
640 FTS *sp;
641 FTSENT *p;
642 int instr;
643{
644
645 _DIAGASSERT(sp != NULL);
646 _DIAGASSERT(p != NULL);
647
648 if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
649 instr != FTS_NOINSTR && instr != FTS_SKIP) {
650 errno = EINVAL;
651 return (1);
652 }
653 p->fts_instr = instr;
654 return (0);
655}
656
657FTSENT *
658fts_children(sp, instr)
659 FTS *sp;
660 int instr;
661{
662 FTSENT *p;
663#ifdef HAVE_FCHDIR
664 int fd;
665#else
666 char *pszRoot;
667 int rc;
668#endif
669
670 _DIAGASSERT(sp != NULL);
671
672 if (instr && instr != FTS_NAMEONLY) {
673 errno = EINVAL;
674 return (NULL);
675 }
676
677 /* Set current node pointer. */
678 p = sp->fts_cur;
679
680 /*
681 * Errno set to 0 so user can distinguish empty directory from
682 * an error.
683 */
684 errno = 0;
685
686 /* Fatal errors stop here. */
687 if (ISSET(FTS_STOP))
688 return (NULL);
689
690 /* Return logical hierarchy of user's arguments. */
691 if (p->fts_info == FTS_INIT)
692 return (p->fts_link);
693
694 /*
695 * If not a directory being visited in pre-order, stop here. Could
696 * allow FTS_DNR, assuming the user has fixed the problem, but the
697 * same effect is available with FTS_AGAIN.
698 */
699 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
700 return (NULL);
701
702 /* Free up any previous child list. */
703 if (sp->fts_child)
704 fts_lfree(sp->fts_child);
705
706 if (instr == FTS_NAMEONLY) {
707 SET(FTS_NAMEONLY);
708 instr = BNAMES;
709 } else
710 instr = BCHILD;
711
712 /*
713 * If using chdir on a relative path and called BEFORE fts_read does
714 * its chdir to the root of a traversal, we can lose -- we need to
715 * chdir into the subdirectory, and we don't know where the current
716 * directory is, so we can't get back so that the upcoming chdir by
717 * fts_read will work.
718 */
719 if (p->fts_level != FTS_ROOTLEVEL || IS_SLASH(p->fts_accpath[0]) ||
720 ISSET(FTS_NOCHDIR))
721 return (sp->fts_child = fts_build(sp, instr));
722
723#ifdef HAVE_FCHDIR
724 if ((fd = open(".", O_RDONLY | KMK_OPEN_NO_INHERIT, 0)) == -1)
725#else
726 if ((pszRoot = getcwd(NULL, 0)) == NULL)
727#endif
728 return (sp->fts_child = NULL);
729 sp->fts_child = fts_build(sp, instr);
730#ifdef HAVE_FCHDIR
731 if (fchdir(fd)) {
732 (void)close(fd);
733 return (NULL);
734 }
735 (void)close(fd);
736#else
737 rc = chdir(pszRoot);
738 free(pszRoot);
739 if (rc)
740 return (NULL);
741#endif
742
743 return (sp->fts_child);
744}
745
746/*
747 * This is the tricky part -- do not casually change *anything* in here. The
748 * idea is to build the linked list of entries that are used by fts_children
749 * and fts_read. There are lots of special cases.
750 *
751 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
752 * set and it's a physical walk (so that symbolic links can't be directories),
753 * we can do things quickly. First, if it's a 4.4BSD file system, the type
754 * of the file is in the directory entry. Otherwise, we assume that the number
755 * of subdirectories in a node is equal to the number of links to the parent.
756 * The former skips all stat calls. The latter skips stat calls in any leaf
757 * directories and for any files after the subdirectories in the directory have
758 * been found, cutting the stat calls by about 2/3.
759 */
760static FTSENT *
761fts_build(sp, type)
762 FTS *sp;
763 int type;
764{
765 struct dirent *dp;
766 FTSENT *p, *head;
767 size_t nitems;
768 FTSENT *cur, *tail;
769 DIR *dirp;
770 int adjust, cderrno, descend, len, level, nlinks, saved_errno, nostat;
771 size_t maxlen;
772#ifdef FTS_WHITEOUT
773 int oflag;
774#endif
775 char *cp = NULL; /* pacify gcc */
776
777 _DIAGASSERT(sp != NULL);
778
779 /* Set current node pointer. */
780 cur = sp->fts_cur;
781
782 /*
783 * Open the directory for reading. If this fails, we're done.
784 * If being called from fts_read, set the fts_info field.
785 */
786#if defined(FTS_WHITEOUT) && !defined(__OS2__)
787 if (ISSET(FTS_WHITEOUT))
788 oflag = DTF_NODUP|DTF_REWIND;
789 else
790 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
791#elif defined(_MSC_VER)
792# define __opendir2(path, flag) birdDirOpenExtraInfo(path)
793#else
794#define __opendir2(path, flag) opendir(path)
795#endif
796 if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
797 if (type == BREAD) {
798 cur->fts_info = FTS_DNR;
799 cur->fts_errno = errno;
800 }
801 return (NULL);
802 }
803
804 /*
805 * Nlinks is the number of possible entries of type directory in the
806 * directory if we're cheating on stat calls, 0 if we're not doing
807 * any stat calls at all, -1 if we're doing stats on everything.
808 */
809 if (type == BNAMES) {
810 nlinks = 0;
811 nostat = 1;
812 } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
813 nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
814 nostat = 1;
815 } else {
816 nlinks = -1;
817 nostat = 0;
818 }
819
820#ifdef notdef
821 (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
822 (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
823 ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
824#endif
825 /*
826 * If we're going to need to stat anything or we want to descend
827 * and stay in the directory, chdir. If this fails we keep going,
828 * but set a flag so we don't chdir after the post-order visit.
829 * We won't be able to stat anything, but we can still return the
830 * names themselves. Note, that since fts_read won't be able to
831 * chdir into the directory, it will have to return different path
832 * names than before, i.e. "a/b" instead of "b". Since the node
833 * has already been visited in pre-order, have to wait until the
834 * post-order visit to return the error. There is a special case
835 * here, if there was nothing to stat then it's not an error to
836 * not be able to stat. This is all fairly nasty. If a program
837 * needed sorted entries or stat information, they had better be
838 * checking FTS_NS on the returned nodes.
839 */
840 cderrno = 0;
841 if (nlinks || type == BREAD) {
842#ifdef HAVE_FCHDIR
843 if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
844#else
845 if (fts_safe_changedir(sp, cur, dirfd(dirp), cur->fts_accpath)) {
846#endif
847 if (nlinks && type == BREAD)
848 cur->fts_errno = errno;
849 cur->fts_flags |= FTS_DONTCHDIR;
850 descend = 0;
851 cderrno = errno;
852 } else
853 descend = 1;
854 } else
855 descend = 0;
856
857 /*
858 * Figure out the max file name length that can be stored in the
859 * current path -- the inner loop allocates more path as necessary.
860 * We really wouldn't have to do the maxlen calculations here, we
861 * could do them in fts_read before returning the path, but it's a
862 * lot easier here since the length is part of the dirent structure.
863 *
864 * If not changing directories set a pointer so that can just append
865 * each new name into the path.
866 */
867 len = NAPPEND(cur);
868 if (ISSET(FTS_NOCHDIR)) {
869 cp = sp->fts_path + len;
870 *cp++ = '/';
871 }
872 len++;
873 maxlen = sp->fts_pathlen - len;
874
875 level = cur->fts_level + 1;
876
877 /* Read the directory, attaching each entry to the `link' pointer. */
878 adjust = 0;
879 for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) {
880 size_t dlen;
881
882 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
883 continue;
884
885#if HAVE_STRUCT_DIRENT_D_NAMLEN
886 dlen = dp->d_namlen;
887#else
888 dlen = strlen(dp->d_name);
889#endif
890 if ((p = fts_alloc(sp, dp->d_name, dlen)) == NULL)
891 goto mem1;
892 if (dlen >= maxlen) { /* include space for NUL */
893 if (fts_palloc(sp, len + dlen + 1)) {
894 /*
895 * No more memory for path or structures. Save
896 * errno, free up the current structure and the
897 * structures already allocated.
898 */
899mem1: saved_errno = errno;
900 if (p)
901 free(p);
902 fts_lfree(head);
903 (void)closedir(dirp);
904 errno = saved_errno;
905 cur->fts_info = FTS_ERR;
906 SET(FTS_STOP);
907 return (NULL);
908 }
909 adjust = 1;
910 if (ISSET(FTS_NOCHDIR))
911 cp = sp->fts_path + len;
912 maxlen = sp->fts_pathlen - len;
913 }
914
915 p->fts_pathlen = len + dlen;
916 p->fts_parent = sp->fts_cur;
917 p->fts_level = level;
918
919#ifdef FTS_WHITEOUT
920 if (dp->d_type == DT_WHT)
921 p->fts_flags |= FTS_ISW;
922#endif
923
924 if (cderrno) {
925 if (nlinks) {
926 p->fts_info = FTS_NS;
927 p->fts_errno = cderrno;
928 } else
929 p->fts_info = FTS_NSOK;
930 p->fts_accpath = cur->fts_accpath;
931 } else if (nlinks == 0
932#ifdef DT_DIR
933 || (nostat &&
934 dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
935#endif
936 ) {
937 p->fts_accpath =
938 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
939 p->fts_info = FTS_NSOK;
940 } else {
941 /* Build a file name for fts_stat to stat. */
942 if (ISSET(FTS_NOCHDIR)) {
943 p->fts_accpath = p->fts_path;
944 memmove(cp, p->fts_name,
945 (size_t)(p->fts_namelen + 1));
946 } else
947 p->fts_accpath = p->fts_name;
948
949 /* Stat it. */
950#ifdef _MSC_VER
951 p->fts_info = fts_stat_dirent(sp, p, 0, dp);
952#else
953 p->fts_info = fts_stat(sp, p, 0);
954#endif
955
956 /* Decrement link count if applicable. */
957 if (nlinks > 0 && (p->fts_info == FTS_D ||
958 p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
959 --nlinks;
960 }
961
962 /* We walk in directory order so "ls -f" doesn't get upset. */
963 p->fts_link = NULL;
964 if (head == NULL)
965 head = tail = p;
966 else {
967 tail->fts_link = p;
968 tail = p;
969 }
970 ++nitems;
971 }
972 (void)closedir(dirp);
973
974 /*
975 * If had to realloc the path, adjust the addresses for the rest
976 * of the tree.
977 */
978 if (adjust)
979 fts_padjust(sp, head);
980
981 /*
982 * If not changing directories, reset the path back to original
983 * state.
984 */
985 if (ISSET(FTS_NOCHDIR)) {
986 if (cp - 1 > sp->fts_path)
987 --cp;
988 *cp = '\0';
989 }
990
991 /*
992 * If descended after called from fts_children or after called from
993 * fts_read and nothing found, get back. At the root level we use
994 * the saved fd; if one of fts_open()'s arguments is a relative path
995 * to an empty directory, we wind up here with no other way back. If
996 * can't get back, we're done.
997 */
998 if (descend && (type == BCHILD || !nitems) &&
999 (cur->fts_level == FTS_ROOTLEVEL ?
1000#ifdef HAVE_FCHDIR
1001 FCHDIR(sp, sp->fts_rfd) :
1002#else
1003 CHDIR(sp, sp->fts_rdir) :
1004#endif
1005 fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
1006 cur->fts_info = FTS_ERR;
1007 SET(FTS_STOP);
1008 return (NULL);
1009 }
1010
1011 /* If didn't find anything, return NULL. */
1012 if (!nitems) {
1013 if (type == BREAD)
1014 cur->fts_info = FTS_DP;
1015 return (NULL);
1016 }
1017
1018 /* Sort the entries. */
1019 if (sp->fts_compar && nitems > 1)
1020 head = fts_sort(sp, head, nitems);
1021 return (head);
1022}
1023
1024#ifdef _MSC_VER
1025/** Special version of fts_stat that takes the information from the directory
1026 * entry returned by readdir().
1027 *
1028 * Directory listing returns all the stat information on systems likes
1029 * Windows and OS/2. */
1030static u_short
1031fts_stat_dirent(FTS *sp, FTSENT *p, int follow, struct dirent *pDirEnt)
1032{
1033 FTSENT *t;
1034 dev_t dev;
1035 ino_t ino;
1036 struct STAT *sbp, sb;
1037 int saved_errno;
1038
1039 _DIAGASSERT(sp != NULL);
1040 _DIAGASSERT(p != NULL);
1041
1042 /* If user needs stat info, stat buffer already allocated. */
1043 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
1044
1045 /*
1046 * Copy over the stat info from the direntry.
1047 */
1048 *sbp = pDirEnt->d_stat;
1049
1050 /*
1051 * If doing a logical walk, or application requested FTS_FOLLOW, do
1052 * a stat(2) on symlinks. If that fails, assume non-existent
1053 * symlink and set the errno from the stat call.
1054 */
1055 if (S_ISLNK(sbp->st_mode) && (ISSET(FTS_LOGICAL) || follow)) {
1056 if (stat(p->fts_accpath, sbp)) {
1057 saved_errno = errno;
1058 errno = 0;
1059 return (FTS_SLNONE);
1060 }
1061 }
1062
1063 if (S_ISDIR(sbp->st_mode)) {
1064 /*
1065 * Set the device/inode. Used to find cycles and check for
1066 * crossing mount points. Also remember the link count, used
1067 * in fts_build to limit the number of stat calls. It is
1068 * understood that these fields are only referenced if fts_info
1069 * is set to FTS_D.
1070 */
1071 dev = p->fts_dev = sbp->st_dev;
1072 ino = p->fts_ino = sbp->st_ino;
1073 p->fts_nlink = sbp->st_nlink;
1074
1075 if (ISDOT(p->fts_name))
1076 return (FTS_DOT);
1077
1078 /*
1079 * Cycle detection is done by brute force when the directory
1080 * is first encountered. If the tree gets deep enough or the
1081 * number of symbolic links to directories is high enough,
1082 * something faster might be worthwhile.
1083 */
1084
1085#ifdef _MSC_VER
1086 if (ino && dev) /** @todo ino emulation on windows... */
1087#endif
1088 for (t = p->fts_parent;
1089 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
1090 if (ino == t->fts_ino && dev == t->fts_dev) {
1091 p->fts_cycle = t;
1092 return (FTS_DC);
1093 }
1094 return (FTS_D);
1095 }
1096 if (S_ISLNK(sbp->st_mode))
1097 return (FTS_SL);
1098 if (S_ISREG(sbp->st_mode))
1099 return (FTS_F);
1100 return (FTS_DEFAULT);
1101}
1102
1103#endif /* fts_stat_dirent */
1104
1105static u_short
1106fts_stat(sp, p, follow)
1107 FTS *sp;
1108 FTSENT *p;
1109 int follow;
1110{
1111 FTSENT *t;
1112 dev_t dev;
1113 ino_t ino;
1114 struct STAT *sbp, sb;
1115 int saved_errno;
1116
1117 _DIAGASSERT(sp != NULL);
1118 _DIAGASSERT(p != NULL);
1119
1120 /* If user needs stat info, stat buffer already allocated. */
1121 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
1122
1123#ifdef FTS_WHITEOUT
1124 /* check for whiteout */
1125 if (p->fts_flags & FTS_ISW) {
1126 if (sbp != &sb) {
1127 memset(sbp, '\0', sizeof (*sbp));
1128 sbp->st_mode = S_IFWHT;
1129 }
1130 return (FTS_W);
1131 }
1132#endif
1133
1134 /*
1135 * If doing a logical walk, or application requested FTS_FOLLOW, do
1136 * a stat(2). If that fails, check for a non-existent symlink. If
1137 * fail, set the errno from the stat call.
1138 */
1139 if (ISSET(FTS_LOGICAL) || follow) {
1140 if (stat(p->fts_accpath, sbp)) {
1141 saved_errno = errno;
1142 if (!lstat(p->fts_accpath, sbp)) {
1143 errno = 0;
1144 return (FTS_SLNONE);
1145 }
1146 p->fts_errno = saved_errno;
1147 goto err;
1148 }
1149 } else if (lstat(p->fts_accpath, sbp)) {
1150 p->fts_errno = errno;
1151err: memset(sbp, 0, sizeof(struct STAT));
1152 return (FTS_NS);
1153 }
1154
1155 if (S_ISDIR(sbp->st_mode)) {
1156 /*
1157 * Set the device/inode. Used to find cycles and check for
1158 * crossing mount points. Also remember the link count, used
1159 * in fts_build to limit the number of stat calls. It is
1160 * understood that these fields are only referenced if fts_info
1161 * is set to FTS_D.
1162 */
1163 dev = p->fts_dev = sbp->st_dev;
1164 ino = p->fts_ino = sbp->st_ino;
1165 p->fts_nlink = sbp->st_nlink;
1166
1167 if (ISDOT(p->fts_name))
1168 return (FTS_DOT);
1169
1170 /*
1171 * Cycle detection is done by brute force when the directory
1172 * is first encountered. If the tree gets deep enough or the
1173 * number of symbolic links to directories is high enough,
1174 * something faster might be worthwhile.
1175 */
1176
1177#ifdef _MSC_VER
1178 if (ino && dev) /** @todo ino emulation on windows... */
1179#endif
1180 for (t = p->fts_parent;
1181 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
1182 if (ino == t->fts_ino && dev == t->fts_dev) {
1183 p->fts_cycle = t;
1184 return (FTS_DC);
1185 }
1186 return (FTS_D);
1187 }
1188 if (S_ISLNK(sbp->st_mode))
1189 return (FTS_SL);
1190 if (S_ISREG(sbp->st_mode))
1191 return (FTS_F);
1192 return (FTS_DEFAULT);
1193}
1194
1195static FTSENT *
1196fts_sort(sp, head, nitems)
1197 FTS *sp;
1198 FTSENT *head;
1199 size_t nitems;
1200{
1201 FTSENT **ap, *p;
1202
1203 _DIAGASSERT(sp != NULL);
1204 _DIAGASSERT(head != NULL);
1205
1206 /*
1207 * Construct an array of pointers to the structures and call qsort(3).
1208 * Reassemble the array in the order returned by qsort. If unable to
1209 * sort for memory reasons, return the directory entries in their
1210 * current order. Allocate enough space for the current needs plus
1211 * 40 so don't realloc one entry at a time.
1212 */
1213 if (nitems > sp->fts_nitems) {
1214 FTSENT **new;
1215
1216 new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40));
1217 if (new == 0)
1218 return (head);
1219 sp->fts_array = new;
1220 sp->fts_nitems = nitems + 40;
1221 }
1222 for (ap = sp->fts_array, p = head; p; p = p->fts_link)
1223 *ap++ = p;
1224 qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *),
1225 (int (*)(const void *, const void *))sp->fts_compar);
1226 for (head = *(ap = sp->fts_array); --nitems; ++ap)
1227 ap[0]->fts_link = ap[1];
1228 ap[0]->fts_link = NULL;
1229 return (head);
1230}
1231
1232static FTSENT *
1233fts_alloc(sp, name, namelen)
1234 FTS *sp;
1235 const char *name;
1236 size_t namelen;
1237{
1238 FTSENT *p;
1239 size_t len;
1240
1241 _DIAGASSERT(sp != NULL);
1242 _DIAGASSERT(name != NULL);
1243
1244#if defined(ALIGNBYTES) && defined(ALIGN)
1245 /*
1246 * The file name is a variable length array and no stat structure is
1247 * necessary if the user has set the nostat bit. Allocate the FTSENT
1248 * structure, the file name and the stat structure in one chunk, but
1249 * be careful that the stat structure is reasonably aligned. Since the
1250 * fts_name field is declared to be of size 1, the fts_name pointer is
1251 * namelen + 2 before the first possible address of the stat structure.
1252 */
1253 len = sizeof(FTSENT) + namelen;
1254 if (!ISSET(FTS_NOSTAT))
1255 len += sizeof(struct STAT) + ALIGNBYTES;
1256 if ((p = malloc(len)) == NULL)
1257 return (NULL);
1258
1259 if (!ISSET(FTS_NOSTAT))
1260 p->fts_statp =
1261 (struct STAT *)ALIGN((u_long)(p->fts_name + namelen + 2));
1262#else
1263 (void)len;
1264 if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL)
1265 return (NULL);
1266
1267 if (!ISSET(FTS_NOSTAT))
1268 if ((p->fts_statp = malloc(sizeof(struct STAT))) == NULL) {
1269 free(p);
1270 return (NULL);
1271 }
1272#endif
1273
1274 /* Copy the name plus the trailing NULL. */
1275 memmove(p->fts_name, name, namelen + 1);
1276
1277 p->fts_namelen = namelen;
1278 p->fts_path = sp->fts_path;
1279 p->fts_errno = 0;
1280 p->fts_flags = 0;
1281 p->fts_instr = FTS_NOINSTR;
1282 p->fts_number = 0;
1283 p->fts_pointer = NULL;
1284 return (p);
1285}
1286
1287static void
1288fts_lfree(head)
1289 FTSENT *head;
1290{
1291 FTSENT *p;
1292
1293 /* XXX: head may be NULL ? */
1294
1295 /* Free a linked list of structures. */
1296 while ((p = head) != NULL) {
1297 head = head->fts_link;
1298
1299#if !defined(ALIGNBYTES) || !defined(ALIGN)
1300 if (p->fts_statp)
1301 free(p->fts_statp);
1302#endif
1303 free(p);
1304 }
1305}
1306
1307static size_t
1308fts_pow2(x)
1309 size_t x;
1310{
1311
1312 x--;
1313 x |= x>>1;
1314 x |= x>>2;
1315 x |= x>>4;
1316 x |= x>>8;
1317 x |= x>>16;
1318#if LONG_BIT > 32
1319 x |= x>>32;
1320#endif
1321#if LONG_BIT > 64
1322 x |= x>>64;
1323#endif
1324 x++;
1325 return (x);
1326}
1327
1328/*
1329 * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1330 * Most systems will allow creation of paths much longer than MAXPATHLEN, even
1331 * though the kernel won't resolve them. Round up the new size to a power of 2,
1332 * so we don't realloc the path 2 bytes at a time.
1333 */
1334static int
1335fts_palloc(sp, size)
1336 FTS *sp;
1337 size_t size;
1338{
1339 char *new;
1340
1341 _DIAGASSERT(sp != NULL);
1342
1343#if 1
1344 /* Protect against fts_pathlen overflow. */
1345 if (size > USHRT_MAX + 1) {
1346 errno = ENOMEM;
1347 return (1);
1348 }
1349#endif
1350 size = fts_pow2(size);
1351 new = realloc(sp->fts_path, size);
1352 if (new == 0)
1353 return (1);
1354 sp->fts_path = new;
1355 sp->fts_pathlen = size;
1356 return (0);
1357}
1358
1359/*
1360 * When the path is realloc'd, have to fix all of the pointers in structures
1361 * already returned.
1362 */
1363static void
1364fts_padjust(sp, head)
1365 FTS *sp;
1366 FTSENT *head;
1367{
1368 FTSENT *p;
1369 char *addr;
1370
1371 _DIAGASSERT(sp != NULL);
1372
1373#define ADJUST(p) do { \
1374 if ((p)->fts_accpath != (p)->fts_name) \
1375 (p)->fts_accpath = \
1376 addr + ((p)->fts_accpath - (p)->fts_path); \
1377 (p)->fts_path = addr; \
1378} while (/*CONSTCOND*/0)
1379
1380 addr = sp->fts_path;
1381
1382 /* Adjust the current set of children. */
1383 for (p = sp->fts_child; p; p = p->fts_link)
1384 ADJUST(p);
1385
1386 /* Adjust the rest of the tree, including the current level. */
1387 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1388 ADJUST(p);
1389 p = p->fts_link ? p->fts_link : p->fts_parent;
1390 }
1391}
1392
1393static size_t
1394fts_maxarglen(argv)
1395 char * const *argv;
1396{
1397 size_t len, max;
1398
1399 _DIAGASSERT(argv != NULL);
1400
1401 for (max = 0; *argv; ++argv)
1402 if ((len = strlen(*argv)) > max)
1403 max = len;
1404 return (max + 1);
1405}
1406
1407/*
1408 * Change to dir specified by fd or p->fts_accpath without getting
1409 * tricked by someone changing the world out from underneath us.
1410 * Assumes p->fts_dev and p->fts_ino are filled in.
1411 */
1412static int
1413fts_safe_changedir(sp, p, fd, path)
1414 const FTS *sp;
1415 const FTSENT *p;
1416 int fd;
1417 const char *path;
1418{
1419 int oldfd = fd, ret = -1;
1420 struct STAT sb;
1421
1422 if (ISSET(FTS_NOCHDIR))
1423 return 0;
1424
1425#ifdef HAVE_FCHDIR
1426 if (oldfd < 0) {
1427 if (!path) /* shuts up gcc nonull checks*/
1428 return -1;
1429 fd = open(path, O_RDONLY | KMK_OPEN_NO_INHERIT);
1430 if (fd == -1)
1431 return -1;
1432 }
1433
1434 if (fstat(fd, &sb) == -1)
1435 goto bail;
1436#else
1437 if (stat(path, &sb))
1438 goto bail;
1439#endif
1440
1441 if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) {
1442 errno = ENOENT;
1443 goto bail;
1444 }
1445
1446#ifdef HAVE_FCHDIR
1447 ret = fchdir(fd);
1448#else
1449 ret = chdir(path);
1450#endif
1451
1452bail:
1453#ifdef HAVE_FCHDIR
1454 if (oldfd < 0) {
1455 int save_errno = errno;
1456 (void)close(fd);
1457 errno = save_errno;
1458 }
1459#endif
1460 return ret;
1461}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use