VirtualBox

source: kBuild/trunk/src/lib/nt/fts-nt.c@ 2988

Last change on this file since 2988 was 2988, checked in by bird, 8 years ago

fts-nt.c: cleanups.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette