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

Last change on this file since 2992 was 2992, checked in by bird, 9 years ago

fts-nt: prefix public names.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.9 KB
Line 
1/* $Id: fts-nt.c 2992 2016-11-01 22:06:08Z bird $ */
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 <bird-klibc-spam-xiv@anduin.net>
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
129nt_fts_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 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
264 len = strlen(++cp);
265 memmove(p->fts_name, cp, len + 1);
266 p->fts_namelen = len;
267 }
268 p->fts_accpath = p->fts_path = sp->fts_path;
269 sp->fts_dev = p->fts_dev;
270}
271
272int FTSCALL
273nt_fts_close(FTS *sp)
274{
275 FTSENT *freep, *p;
276 /*int saved_errno;*/
277
278 /*
279 * This still works if we haven't read anything -- the dummy structure
280 * points to the root list, so we step through to the end of the root
281 * list which has a valid parent pointer.
282 */
283 if (sp->fts_cur) {
284 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
285 freep = p;
286 p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
287 free(freep);
288 }
289 free(p);
290 }
291
292 /* Free up child linked list, sort array, path buffer. */
293 if (sp->fts_child)
294 fts_lfree(sp->fts_child);
295 if (sp->fts_array)
296 free(sp->fts_array);
297 free(sp->fts_path);
298
299 /* Free up the stream pointer. */
300 free(sp);
301 return (0);
302}
303
304/*
305 * Special case of "/" at the end of the path so that slashes aren't
306 * appended which would cause paths to be written as "....//foo".
307 */
308#define NAPPEND(p) \
309 (p->fts_path[p->fts_pathlen - 1] == '/' \
310 ? p->fts_pathlen - 1 : p->fts_pathlen)
311
312static void
313fts_free_entry(FTSENT *tmp)
314{
315 if (tmp != NULL) {
316 if (tmp->fts_dirfd != INVALID_HANDLE_VALUE) {
317 birdCloseFile(tmp->fts_dirfd);
318 tmp->fts_dirfd = INVALID_HANDLE_VALUE;
319 }
320 free(tmp);
321 }
322}
323
324FTSENT * FTSCALL
325nt_fts_read(FTS *sp)
326{
327 FTSENT *p, *tmp;
328 int instr;
329 char *t;
330
331 /* If finished or unrecoverable error, return NULL. */
332 if (sp->fts_cur == NULL || ISSET(FTS_STOP))
333 return (NULL);
334
335 /* Set current node pointer. */
336 p = sp->fts_cur;
337
338 /* Save and zero out user instructions. */
339 instr = p->fts_instr;
340 p->fts_instr = FTS_NOINSTR;
341
342 /* Any type of file may be re-visited; re-stat and re-turn. */
343 if (instr == FTS_AGAIN) {
344 p->fts_info = fts_stat(sp, p, 0, INVALID_HANDLE_VALUE);
345 return (p);
346 }
347
348 /*
349 * Following a symlink -- SLNONE test allows application to see
350 * SLNONE and recover. If indirecting through a symlink, have
351 * keep a pointer to current location. If unable to get that
352 * pointer, follow fails.
353 *
354 * NT: Since we don't change directory, we just set fts_symfd to a
355 * placeholder value handle value here in case a API client
356 * checks it. Ditto FTS_SYMFOLLOW.
357 */
358 if (instr == FTS_FOLLOW &&
359 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
360 p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
361 if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
362 p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
363 p->fts_flags |= FTS_SYMFOLLOW;
364 }
365 return (p);
366 }
367
368 /* Directory in pre-order. */
369 if (p->fts_info == FTS_D) {
370 /* If skipped or crossed mount point, do post-order visit. */
371 if (instr == FTS_SKIP ||
372 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
373 if (p->fts_flags & FTS_SYMFOLLOW) {
374 p->fts_symfd = INVALID_HANDLE_VALUE;
375 }
376 if (sp->fts_child) {
377 fts_lfree(sp->fts_child);
378 sp->fts_child = NULL;
379 }
380 p->fts_info = FTS_DP;
381 return (p);
382 }
383
384 /* Rebuild if only read the names and now traversing. */
385 if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
386 CLR(FTS_NAMEONLY);
387 fts_lfree(sp->fts_child);
388 sp->fts_child = NULL;
389 }
390
391 /*
392 * Cd to the subdirectory.
393 *
394 * If have already read and now fail to chdir, whack the list
395 * to make the names come out right, and set the parent errno
396 * so the application will eventually get an error condition.
397 * Set the FTS_DONTCHDIR flag so that when we logically change
398 * directories back to the parent we don't do a chdir.
399 *
400 * If haven't read do so. If the read fails, fts_build sets
401 * FTS_STOP or the fts_info field of the node.
402 */
403 if (sp->fts_child != NULL) {
404 /* nothing to do */
405 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
406 if (ISSET(FTS_STOP))
407 return (NULL);
408 return (p);
409 }
410 p = sp->fts_child;
411 sp->fts_child = NULL;
412 goto name;
413 }
414
415 /* Move to the next node on this level. */
416next: tmp = p;
417 if ((p = p->fts_link) != NULL) {
418 /*
419 * If reached the top, return to the original directory (or
420 * the root of the tree), and load the paths for the next root.
421 */
422 if (p->fts_level == FTS_ROOTLEVEL) {
423 fts_free_entry(tmp);
424 fts_load(sp, p);
425 return (sp->fts_cur = p);
426 }
427
428 /*
429 * User may have called fts_set on the node. If skipped,
430 * ignore. If followed, get a file descriptor so we can
431 * get back if necessary.
432 */
433 if (p->fts_instr == FTS_SKIP) {
434 fts_free_entry(tmp);
435 goto next;
436 }
437 if (p->fts_instr == FTS_FOLLOW) {
438 p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
439 /* NT: See above regarding fts_symfd. */
440 if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
441 p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
442 p->fts_flags |= FTS_SYMFOLLOW;
443 }
444 p->fts_instr = FTS_NOINSTR;
445 }
446
447 fts_free_entry(tmp);
448
449name: t = sp->fts_path + NAPPEND(p->fts_parent);
450 *t++ = '/';
451 memmove(t, p->fts_name, p->fts_namelen + 1);
452 return (sp->fts_cur = p);
453 }
454
455 /* Move up to the parent node. */
456 p = tmp->fts_parent;
457
458 if (p->fts_level == FTS_ROOTPARENTLEVEL) {
459 /*
460 * Done; free everything up and set errno to 0 so the user
461 * can distinguish between error and EOF.
462 */
463 fts_free_entry(tmp);
464 fts_free_entry(p);
465 errno = 0;
466 return (sp->fts_cur = NULL);
467 }
468
469 /* NUL terminate the pathname. */
470 sp->fts_path[p->fts_pathlen] = '\0';
471
472 /*
473 * Return to the parent directory. If at a root node or came through
474 * a symlink, go back through the file descriptor. Otherwise, cd up
475 * one directory.
476 *
477 * NT: We're doing no fchdir, but we need to close the directory handle
478 * and clear fts_symfd now.
479 */
480 if (p->fts_flags & FTS_SYMFOLLOW) {
481 p->fts_symfd = INVALID_HANDLE_VALUE;
482 }
483 if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
484 birdCloseFile(p->fts_dirfd);
485 p->fts_dirfd = INVALID_HANDLE_VALUE;
486 }
487 fts_free_entry(tmp);
488 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
489 return (sp->fts_cur = p);
490}
491
492/*
493 * Fts_set takes the stream as an argument although it's not used in this
494 * implementation; it would be necessary if anyone wanted to add global
495 * semantics to fts using fts_set. An error return is allowed for similar
496 * reasons.
497 */
498/* ARGSUSED */
499int FTSCALL
500nt_fts_set(FTS *sp, FTSENT *p, int instr)
501{
502 if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
503 instr != FTS_NOINSTR && instr != FTS_SKIP) {
504 errno = EINVAL;
505 return (1);
506 }
507 p->fts_instr = instr;
508 return (0);
509}
510
511FTSENT * FTSCALL
512nt_fts_children(FTS *sp, int instr)
513{
514 FTSENT *p;
515
516 if (instr != 0 && instr != FTS_NAMEONLY) {
517 errno = EINVAL;
518 return (NULL);
519 }
520
521 /* Set current node pointer. */
522 p = sp->fts_cur;
523
524 /*
525 * Errno set to 0 so user can distinguish empty directory from
526 * an error.
527 */
528 errno = 0;
529
530 /* Fatal errors stop here. */
531 if (ISSET(FTS_STOP))
532 return (NULL);
533
534 /* Return logical hierarchy of user's arguments. */
535 if (p->fts_info == FTS_INIT)
536 return (p->fts_link);
537
538 /*
539 * If not a directory being visited in pre-order, stop here. Could
540 * allow FTS_DNR, assuming the user has fixed the problem, but the
541 * same effect is available with FTS_AGAIN.
542 */
543 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
544 return (NULL);
545
546 /* Free up any previous child list. */
547 if (sp->fts_child != NULL) {
548 fts_lfree(sp->fts_child);
549 sp->fts_child = NULL; /* (bird - double free for _open(".") failure in original) */
550 }
551
552 /* NT: Some BSD utility sets FTS_NAMEONLY? We don't really need this
553 optimization, but since it only hurts that utility, it can stay. */
554 if (instr == FTS_NAMEONLY) {
555 assert(0); /* don't specify FTS_NAMEONLY on NT. */
556 SET(FTS_NAMEONLY);
557 instr = BNAMES;
558 } else
559 instr = BCHILD;
560
561 return (sp->fts_child = fts_build(sp, instr));
562}
563
564#ifndef fts_get_clientptr
565#error "fts_get_clientptr not defined"
566#endif
567
568void *
569(FTSCALL fts_get_clientptr)(FTS *sp)
570{
571
572 return (fts_get_clientptr(sp));
573}
574
575#ifndef fts_get_stream
576#error "fts_get_stream not defined"
577#endif
578
579FTS *
580(FTSCALL fts_get_stream)(FTSENT *p)
581{
582 return (fts_get_stream(p));
583}
584
585void FTSCALL
586nt_fts_set_clientptr(FTS *sp, void *clientptr)
587{
588
589 sp->fts_clientptr = clientptr;
590}
591
592/*
593 * This is the tricky part -- do not casually change *anything* in here. The
594 * idea is to build the linked list of entries that are used by fts_children
595 * and fts_read. There are lots of special cases.
596 *
597 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
598 * set and it's a physical walk (so that symbolic links can't be directories),
599 * we can do things quickly. First, if it's a 4.4BSD file system, the type
600 * of the file is in the directory entry. Otherwise, we assume that the number
601 * of subdirectories in a node is equal to the number of links to the parent.
602 * The former skips all stat calls. The latter skips stat calls in any leaf
603 * directories and for any files after the subdirectories in the directory have
604 * been found, cutting the stat calls by about 2/3.
605 *
606 * NT: We do not do any link counting or stat avoiding, which invalidates the
607 * above warnings. This function is very simple for us.
608 */
609static FTSENT *
610fts_build(FTS *sp, int type)
611{
612 BirdDirEntry_T *dp;
613 FTSENT *p, *head;
614 FTSENT *cur, *tail;
615 DIR *dirp;
616 void *oldaddr;
617 char *cp;
618 int saved_errno, doadjust;
619 long level;
620 size_t dnamlen, len, maxlen, nitems;
621 unsigned fDirOpenFlags;
622
623 /* Set current node pointer. */
624 cur = sp->fts_cur;
625
626 /*
627 * Open the directory for reading. If this fails, we're done.
628 * If being called from fts_read, set the fts_info field.
629 *
630 * NT: We do a two stage open so we can keep the directory handle around
631 * after we've enumerated the directory. The dir handle is used by
632 * us here and by the API users to more efficiently and safely open
633 * members of the directory.
634 */
635 fDirOpenFlags = BIRDDIR_F_EXTRA_INFO | BIRDDIR_F_KEEP_HANDLE;
636 if (cur->fts_dirfd == INVALID_HANDLE_VALUE) {
637 if (cur->fts_parent->fts_dirfd != INVALID_HANDLE_VALUE) {
638 /* (This works fine for symlinks too, since we follow them.) */
639 cur->fts_dirfd = birdOpenFileEx(cur->fts_parent->fts_dirfd,
640 cur->fts_name,
641 FILE_READ_DATA | SYNCHRONIZE,
642 FILE_ATTRIBUTE_NORMAL,
643 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
644 FILE_OPEN,
645 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
646 OBJ_CASE_INSENSITIVE);
647 } else {
648 cur->fts_dirfd = birdOpenFile(cur->fts_accpath,
649 FILE_READ_DATA | SYNCHRONIZE,
650 FILE_ATTRIBUTE_NORMAL,
651 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
652 FILE_OPEN,
653 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
654 OBJ_CASE_INSENSITIVE);
655 }
656 } else {
657 fDirOpenFlags |= BIRDDIR_F_RESTART_SCAN;
658 }
659 dirp = birdDirOpenFromHandle(cur->fts_dirfd, NULL, fDirOpenFlags);
660 if (dirp == NULL) {
661 if (type == BREAD) {
662 cur->fts_info = FTS_DNR;
663 cur->fts_errno = errno;
664 }
665 return (NULL);
666 }
667
668 /*
669 * Figure out the max file name length that can be stored in the
670 * current path -- the inner loop allocates more path as necessary.
671 * We really wouldn't have to do the maxlen calculations here, we
672 * could do them in fts_read before returning the path, but it's a
673 * lot easier here since the length is part of the dirent structure.
674 *
675 * If not changing directories set a pointer so that can just append
676 * each new name into the path.
677 */
678 len = NAPPEND(cur);
679 cp = sp->fts_path + len;
680 *cp++ = '/';
681 len++;
682 maxlen = sp->fts_pathlen - len;
683
684 level = cur->fts_level + 1;
685
686 /* Read the directory, attaching each entry to the `link' pointer. */
687 doadjust = 0;
688 for (head = tail = NULL, nitems = 0; dirp && (dp = birdDirRead(dirp));) {
689 dnamlen = dp->d_namlen;
690 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
691 continue;
692
693 if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
694 goto mem1;
695 if (dnamlen >= maxlen) { /* include space for NUL */
696 oldaddr = sp->fts_path;
697 if (fts_palloc(sp, dnamlen + len + 1)) {
698 /*
699 * No more memory for path or structures. Save
700 * errno, free up the current structure and the
701 * structures already allocated.
702 */
703mem1: saved_errno = errno;
704 if (p)
705 free(p);
706 fts_lfree(head);
707 birdDirClose(dirp);
708 birdCloseFile(cur->fts_dirfd);
709 cur->fts_dirfd = INVALID_HANDLE_VALUE;
710 cur->fts_info = FTS_ERR;
711 SET(FTS_STOP);
712 errno = saved_errno;
713 return (NULL);
714 }
715 /* Did realloc() change the pointer? */
716 if (oldaddr != sp->fts_path) {
717 doadjust = 1;
718 if (1 /*ISSET(FTS_NOCHDIR)*/)
719 cp = sp->fts_path + len;
720 }
721 maxlen = sp->fts_pathlen - len;
722 }
723
724 p->fts_level = level;
725 p->fts_parent = sp->fts_cur;
726 p->fts_pathlen = len + dnamlen;
727 p->fts_accpath = p->fts_path;
728 p->fts_stat = dp->d_stat;
729 p->fts_info = fts_process_stats(p, &dp->d_stat);
730
731 /* We walk in directory order so "ls -f" doesn't get upset. */
732 p->fts_link = NULL;
733 if (head == NULL)
734 head = tail = p;
735 else {
736 tail->fts_link = p;
737 tail = p;
738 }
739 ++nitems;
740 }
741
742 birdDirClose(dirp);
743
744 /*
745 * If realloc() changed the address of the path, adjust the
746 * addresses for the rest of the tree and the dir list.
747 */
748 if (doadjust)
749 fts_padjust(sp, head);
750
751 /*
752 * Reset the path back to original state.
753 */
754 sp->fts_path[cur->fts_pathlen] = '\0';
755
756 /* If didn't find anything, return NULL. */
757 if (!nitems) {
758 if (type == BREAD)
759 cur->fts_info = FTS_DP;
760 return (NULL);
761 }
762
763 /* Sort the entries. */
764 if (sp->fts_compar && nitems > 1)
765 head = fts_sort(sp, head, nitems);
766 return (head);
767}
768
769
770/**
771 * @note Only used on NT with input arguments, FTS_AGAIN, and links that needs
772 * following. On link information is generally retrieved during directory
773 * enumeration on NT, in line with it's DOS/OS2/FAT API heritage.
774 */
775static int
776fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
777{
778 int saved_errno;
779 const char *path;
780
781 if (dfd == INVALID_HANDLE_VALUE) {
782 path = p->fts_accpath;
783 } else {
784 path = p->fts_name;
785 }
786
787 /*
788 * If doing a logical walk, or application requested FTS_FOLLOW, do
789 * a stat(2). If that fails, check for a non-existent symlink. If
790 * fail, set the errno from the stat call.
791 */
792 if (ISSET(FTS_LOGICAL) || follow) {
793 if (fstatat(dfd, path, &p->fts_stat, 0)) {
794 saved_errno = errno;
795 if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
796 p->fts_errno = saved_errno;
797 goto err;
798 }
799 errno = 0;
800 if (S_ISLNK(p->fts_stat.st_mode))
801 return (FTS_SLNONE);
802 }
803 } else if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
804 p->fts_errno = errno;
805err: memset(&p->fts_stat, 0, sizeof(struct stat));
806 return (FTS_NS);
807 }
808 return fts_process_stats(p, &p->fts_stat);
809}
810
811/* Shared between fts_stat and fts_build. */
812static int
813fts_process_stats(FTSENT *p, BirdStat_T const *sbp)
814{
815 if (S_ISDIR(sbp->st_mode)) {
816 FTSENT *t;
817 fts_dev_t dev;
818 fts_ino_t ino;
819
820 /*
821 * Set the device/inode. Used to find cycles and check for
822 * crossing mount points. Also remember the link count, used
823 * in fts_build to limit the number of stat calls. It is
824 * understood that these fields are only referenced if fts_info
825 * is set to FTS_D.
826 */
827 dev = p->fts_dev = sbp->st_dev;
828 ino = p->fts_ino = sbp->st_ino;
829 p->fts_nlink = sbp->st_nlink;
830
831 if (ISDOT(p->fts_name))
832 return (FTS_DOT);
833
834 /*
835 * Cycle detection is done by brute force when the directory
836 * is first encountered. If the tree gets deep enough or the
837 * number of symbolic links to directories is high enough,
838 * something faster might be worthwhile.
839 */
840 for (t = p->fts_parent;
841 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
842 if (ino == t->fts_ino && dev == t->fts_dev) {
843 p->fts_cycle = t;
844 return (FTS_DC);
845 }
846 return (FTS_D);
847 }
848 if (S_ISLNK(sbp->st_mode))
849 return (FTS_SL);
850 if (S_ISREG(sbp->st_mode))
851 return (FTS_F);
852 return (FTS_DEFAULT);
853}
854
855/*
856 * The comparison function takes pointers to pointers to FTSENT structures.
857 * Qsort wants a comparison function that takes pointers to void.
858 * (Both with appropriate levels of const-poisoning, of course!)
859 * Use a trampoline function to deal with the difference.
860 */
861static int
862fts_compar(const void *a, const void *b)
863{
864 FTS *parent;
865
866 parent = (*(const FTSENT * const *)a)->fts_fts;
867 return (*parent->fts_compar)(a, b);
868}
869
870static FTSENT *
871fts_sort(FTS *sp, FTSENT *head, size_t nitems)
872{
873 FTSENT **ap, *p;
874
875 /*
876 * Construct an array of pointers to the structures and call qsort(3).
877 * Reassemble the array in the order returned by qsort. If unable to
878 * sort for memory reasons, return the directory entries in their
879 * current order. Allocate enough space for the current needs plus
880 * 40 so don't realloc one entry at a time.
881 */
882 if (nitems > sp->fts_nitems) {
883 void *ptr;
884 sp->fts_nitems = nitems + 40;
885 ptr = realloc(sp->fts_array, sp->fts_nitems * sizeof(FTSENT *));
886 if (ptr != NULL) {
887 sp->fts_array = ptr;
888 } else {
889 free(sp->fts_array);
890 sp->fts_array = NULL;
891 sp->fts_nitems = 0;
892 return (head);
893 }
894 }
895 for (ap = sp->fts_array, p = head; p; p = p->fts_link)
896 *ap++ = p;
897 qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar);
898 for (head = *(ap = sp->fts_array); --nitems; ++ap)
899 ap[0]->fts_link = ap[1];
900 ap[0]->fts_link = NULL;
901 return (head);
902}
903
904static FTSENT *
905fts_alloc(FTS *sp, char *name, size_t namelen)
906{
907 FTSENT *p;
908 size_t len;
909
910 struct ftsent_withstat {
911 FTSENT ent;
912 struct stat statbuf;
913 };
914
915 /*
916 * The file name is a variable length array. Allocate the FTSENT
917 * structure and the file name.
918 */
919 len = sizeof(FTSENT) + namelen + 1;
920 if ((p = malloc(len)) == NULL)
921 return (NULL);
922
923 p->fts_name = (char *)(p + 1);
924 p->fts_statp = &p->fts_stat;
925
926 /* Copy the name and guarantee NUL termination. */
927 memcpy(p->fts_name, name, namelen);
928 p->fts_name[namelen] = '\0';
929 p->fts_namelen = namelen;
930 p->fts_path = sp->fts_path;
931 p->fts_errno = 0;
932 p->fts_flags = 0;
933 p->fts_instr = FTS_NOINSTR;
934 p->fts_number = 0;
935 p->fts_pointer = NULL;
936 p->fts_fts = sp;
937 p->fts_symfd = INVALID_HANDLE_VALUE;
938 p->fts_dirfd = INVALID_HANDLE_VALUE;
939 return (p);
940}
941
942static void
943fts_lfree(FTSENT *head)
944{
945 FTSENT *p;
946
947 /* Free a linked list of structures. */
948 while ((p = head)) {
949 head = head->fts_link;
950 assert(p->fts_dirfd == INVALID_HANDLE_VALUE);
951 free(p);
952 }
953}
954
955/*
956 * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
957 * Most systems will allow creation of paths much longer than MAXPATHLEN, even
958 * though the kernel won't resolve them. Add the size (not just what's needed)
959 * plus 256 bytes so don't realloc the path 2 bytes at a time.
960 */
961static int
962fts_palloc(FTS *sp, size_t more)
963{
964 void *ptr;
965
966 sp->fts_pathlen += more + 256;
967 ptr = realloc(sp->fts_path, sp->fts_pathlen);
968 if (ptr) {
969 /*likely */
970 } else {
971 free(sp->fts_path);
972 }
973 sp->fts_path = ptr;
974 return (ptr == NULL);
975}
976
977/*
978 * When the path is realloc'd, have to fix all of the pointers in structures
979 * already returned.
980 */
981static void
982fts_padjust(FTS *sp, FTSENT *head)
983{
984 FTSENT *p;
985 char *addr = sp->fts_path;
986
987#define ADJUST(p) do { \
988 if ((p)->fts_accpath != (p)->fts_name) { \
989 (p)->fts_accpath = \
990 (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
991 } \
992 (p)->fts_path = addr; \
993} while (0)
994 /* Adjust the current set of children. */
995 for (p = sp->fts_child; p; p = p->fts_link)
996 ADJUST(p);
997
998 /* Adjust the rest of the tree, including the current level. */
999 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1000 ADJUST(p);
1001 p = p->fts_link ? p->fts_link : p->fts_parent;
1002 }
1003}
1004
1005static size_t
1006fts_maxarglen(char * const *argv)
1007{
1008 size_t len, max;
1009
1010 for (max = 0; *argv; ++argv)
1011 if ((len = strlen(*argv)) > max)
1012 max = len;
1013 return (max + 1);
1014}
1015
Note: See TracBrowser for help on using the repository browser.