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