VirtualBox

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

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

lib/nt: Got fts-nt halfways working, quite a few NT interface changes.

  • Property svn:eol-style set to native
File size: 31.7 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 <sys/cdefs.h>
81//__FBSDID("$FreeBSD$");
82
83//#include "namespace.h"
84//#include <sys/param.h>
85//#include <sys/mount.h>
86//#include <sys/stat.h>
87
88//#include <dirent.h>
89#include <errno.h>
90//#include <fcntl.h>
91#include "fts-nt.h"
92#include <stdlib.h>
93#include <string.h>
94//#include <unistd.h>
95//#include "un-namespace.h"
96//
97//#include "gen-private.h"
98#include <assert.h>
99#include "nthlp.h"
100#include "ntdir.h"
101
102static FTSENT *fts_alloc(FTS *, char *, size_t);
103static FTSENT *fts_build(FTS *, int);
104static void fts_lfree(FTSENT *);
105static void fts_load(FTS *, FTSENT *);
106static size_t fts_maxarglen(char * const *);
107static void fts_padjust(FTS *, FTSENT *);
108static int fts_palloc(FTS *, size_t);
109static FTSENT *fts_sort(FTS *, FTSENT *, size_t);
110static int fts_stat(FTS *, FTSENT *, int, HANDLE);
111static int fts_process_stats(FTSENT *, BirdStat_T const *);
112static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
113static int fts_ufslinks(FTS *, const FTSENT *);
114
115#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
116
117#define CLR(opt) (sp->fts_options &= ~(opt))
118#define ISSET(opt) (sp->fts_options & (opt))
119#define SET(opt) (sp->fts_options |= (opt))
120
121#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
122
123/* fts_build flags */
124#define BCHILD 1 /* fts_children */
125#define BNAMES 2 /* fts_children, names only */
126#define BREAD 3 /* fts_read */
127
128/* NT needs these: */
129#define MAXPATHLEN 260
130#define MAX(a, b) ( (a) >= (b) ? (a) : (b) )
131
132#define AT_SYMLINK_NOFOLLOW 1
133#define fstatat(hDir, pszPath, pStat, fFlags) birdStatAt((hDir), (pszPath), (pStat), (fFlags) != 0)
134#define FTS_NT_DUMMY_SYMFD_VALUE ((HANDLE)~(intptr_t)(2)) /* current process */
135#define fchdir(fd) todo_fchdir(fd)
136extern int todo_fchdir(fts_fd_t fd);
137
138/*
139 * Internal representation of an FTS, including extra implementation
140 * details. The FTS returned from fts_open points to this structure's
141 * ftsp_fts member (and can be cast to an _fts_private as required)
142 */
143struct _fts_private {
144 FTS ftsp_fts;
145#if 0 /* Not needed on NT, see comment on fts_ufslinks */
146 struct statfs ftsp_statfs;
147 dev_t ftsp_dev;
148 int ftsp_linksreliable;
149#endif
150};
151
152#if 0 /* Not needed on NT, see comment on fts_ufslinks */
153/*
154 * The "FTS_NOSTAT" option can avoid a lot of calls to stat(2) if it
155 * knows that a directory could not possibly have subdirectories. This
156 * is decided by looking at the link count: a subdirectory would
157 * increment its parent's link count by virtue of its own ".." entry.
158 * This assumption only holds for UFS-like filesystems that implement
159 * links and directories this way, so we must punt for others.
160 */
161
162static const char *ufslike_filesystems[] = {
163 "ufs",
164 "zfs",
165 "nfs",
166 "ext2fs",
167 0
168};
169#endif
170
171FTS * FTSCALL
172fts_open(char * const *argv, int options,
173 int (*compar)(const FTSENT * const *, const FTSENT * const *))
174{
175 struct _fts_private *priv;
176 FTS *sp;
177 FTSENT *p, *root;
178 FTSENT *parent, *tmp;
179 size_t len, nitems;
180
181 /* Options check. */
182 if (options & ~FTS_OPTIONMASK) {
183 errno = EINVAL;
184 return (NULL);
185 }
186
187 /* fts_open() requires at least one path */
188 if (*argv == NULL) {
189 errno = EINVAL;
190 return (NULL);
191 }
192
193 /* Allocate/initialize the stream. */
194 if ((priv = calloc(1, sizeof(*priv))) == NULL)
195 return (NULL);
196 sp = &priv->ftsp_fts;
197 sp->fts_compar = compar;
198 sp->fts_options = options;
199
200 /* Shush, GCC. */
201 tmp = NULL;
202
203 /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
204 if (ISSET(FTS_LOGICAL))
205 SET(FTS_NOCHDIR);
206
207 /*
208 * Start out with 1K of path space, and enough, in any case,
209 * to hold the user's paths.
210 */
211 if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
212 goto mem1;
213
214 /* Allocate/initialize root's parent. */
215 if ((parent = fts_alloc(sp, "", 0)) == NULL)
216 goto mem2;
217 parent->fts_level = FTS_ROOTPARENTLEVEL;
218
219 /* Allocate/initialize root(s). */
220 for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
221 /* NT: We need to do some small input transformations to make this and
222 the API user code happy. 1. Lone drive letters get a dot
223 appended so it won't matter if a slash is appended afterwards.
224 2. DOS slashes are converted to UNIX ones. */
225 char *slash;
226 len = strlen(*argv);
227 if (len == 2 && argv[0][1] == ':') {
228 char tmp[4];
229 tmp[0] = argv[0][0];
230 tmp[1] = ':';
231 tmp[2] = '.';
232 tmp[3] = '\0';
233 p = fts_alloc(sp, tmp, 3);
234 } else {
235 p = fts_alloc(sp, *argv, len);
236 }
237#if 1 /* bird */
238 if (p != NULL) { /* likely */ } else { goto mem3; }
239#endif
240 slash = strchr(p->fts_name, '\\');
241 while (slash != NULL) {
242 *slash++ = '/';
243 slash = strchr(p->fts_name, '\\');
244 }
245 p->fts_level = FTS_ROOTLEVEL;
246 p->fts_parent = parent;
247 p->fts_accpath = p->fts_name;
248 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), INVALID_HANDLE_VALUE);
249
250 /* Command-line "." and ".." are real directories. */
251 if (p->fts_info == FTS_DOT)
252 p->fts_info = FTS_D;
253
254 /*
255 * If comparison routine supplied, traverse in sorted
256 * order; otherwise traverse in the order specified.
257 */
258 if (compar) {
259 p->fts_link = root;
260 root = p;
261 } else {
262 p->fts_link = NULL;
263 if (root == NULL)
264 tmp = root = p;
265 else {
266 tmp->fts_link = p;
267 tmp = p;
268 }
269 }
270 }
271 if (compar && nitems > 1)
272 root = fts_sort(sp, root, nitems);
273
274 /*
275 * Allocate a dummy pointer and make fts_read think that we've just
276 * finished the node before the root(s); set p->fts_info to FTS_INIT
277 * so that everything about the "current" node is ignored.
278 */
279 if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
280 goto mem3;
281 sp->fts_cur->fts_link = root;
282 sp->fts_cur->fts_info = FTS_INIT;
283
284 return (sp);
285
286mem3: fts_lfree(root);
287 free(parent);
288mem2: free(sp->fts_path);
289mem1: free(sp);
290 return (NULL);
291}
292
293
294static void
295fts_load(FTS *sp, FTSENT *p)
296{
297 size_t len;
298 char *cp;
299
300 /*
301 * Load the stream structure for the next traversal. Since we don't
302 * actually enter the directory until after the preorder visit, set
303 * the fts_accpath field specially so the chdir gets done to the right
304 * place and the user can access the first node. From fts_open it's
305 * known that the path will fit.
306 */
307 len = p->fts_pathlen = p->fts_namelen;
308 memmove(sp->fts_path, p->fts_name, len + 1);
309/** @todo check for ':' and '\\'? */
310 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
311 len = strlen(++cp);
312 memmove(p->fts_name, cp, len + 1);
313 p->fts_namelen = len;
314 }
315 p->fts_accpath = p->fts_path = sp->fts_path;
316 sp->fts_dev = p->fts_dev;
317}
318
319int FTSCALL
320fts_close(FTS *sp)
321{
322 FTSENT *freep, *p;
323 /*int saved_errno;*/
324
325 /*
326 * This still works if we haven't read anything -- the dummy structure
327 * points to the root list, so we step through to the end of the root
328 * list which has a valid parent pointer.
329 */
330 if (sp->fts_cur) {
331 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
332 freep = p;
333 p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
334 free(freep);
335 }
336 free(p);
337 }
338
339 /* Free up child linked list, sort array, path buffer. */
340 if (sp->fts_child)
341 fts_lfree(sp->fts_child);
342 if (sp->fts_array)
343 free(sp->fts_array);
344 free(sp->fts_path);
345
346 /* Free up the stream pointer. */
347 free(sp);
348 return (0);
349}
350
351/*
352 * Special case of "/" at the end of the path so that slashes aren't
353 * appended which would cause paths to be written as "....//foo".
354 */
355#define NAPPEND(p) \
356 (p->fts_path[p->fts_pathlen - 1] == '/' \
357 ? p->fts_pathlen - 1 : p->fts_pathlen)
358
359static void
360fts_free_entry(FTSENT *tmp)
361{
362 if (tmp != NULL) {
363 if (tmp->fts_dirfd != INVALID_HANDLE_VALUE) {
364 birdCloseFile(tmp->fts_dirfd);
365 tmp->fts_dirfd = INVALID_HANDLE_VALUE;
366 }
367 free(tmp);
368 }
369}
370
371FTSENT * FTSCALL
372fts_read(FTS *sp)
373{
374 FTSENT *p, *tmp;
375 int instr;
376 char *t;
377
378 /* If finished or unrecoverable error, return NULL. */
379 if (sp->fts_cur == NULL || ISSET(FTS_STOP))
380 return (NULL);
381
382 /* Set current node pointer. */
383 p = sp->fts_cur;
384
385 /* Save and zero out user instructions. */
386 instr = p->fts_instr;
387 p->fts_instr = FTS_NOINSTR;
388
389 /* Any type of file may be re-visited; re-stat and re-turn. */
390 if (instr == FTS_AGAIN) {
391 p->fts_info = fts_stat(sp, p, 0, INVALID_HANDLE_VALUE);
392 return (p);
393 }
394
395 /*
396 * Following a symlink -- SLNONE test allows application to see
397 * SLNONE and recover. If indirecting through a symlink, have
398 * keep a pointer to current location. If unable to get that
399 * pointer, follow fails.
400 *
401 * NT: Since we don't change directory, we just set fts_symfd to a
402 * placeholder value handle value here in case a API client
403 * checks it. Ditto FTS_SYMFOLLOW.
404 */
405 if (instr == FTS_FOLLOW &&
406 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
407 p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
408 if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
409 p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
410 p->fts_flags |= FTS_SYMFOLLOW;
411 }
412 return (p);
413 }
414
415 /* Directory in pre-order. */
416 if (p->fts_info == FTS_D) {
417 /* If skipped or crossed mount point, do post-order visit. */
418 if (instr == FTS_SKIP ||
419 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
420 if (p->fts_flags & FTS_SYMFOLLOW) {
421 p->fts_symfd = INVALID_HANDLE_VALUE;
422 }
423 if (sp->fts_child) {
424 fts_lfree(sp->fts_child);
425 sp->fts_child = NULL;
426 }
427 p->fts_info = FTS_DP;
428 return (p);
429 }
430
431 /* Rebuild if only read the names and now traversing. */
432 if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
433 CLR(FTS_NAMEONLY);
434 fts_lfree(sp->fts_child);
435 sp->fts_child = NULL;
436 }
437
438 /*
439 * Cd to the subdirectory.
440 *
441 * If have already read and now fail to chdir, whack the list
442 * to make the names come out right, and set the parent errno
443 * so the application will eventually get an error condition.
444 * Set the FTS_DONTCHDIR flag so that when we logically change
445 * directories back to the parent we don't do a chdir.
446 *
447 * If haven't read do so. If the read fails, fts_build sets
448 * FTS_STOP or the fts_info field of the node.
449 */
450 if (sp->fts_child != NULL) {
451 if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
452 p->fts_errno = errno;
453 p->fts_flags |= FTS_DONTCHDIR;
454 for (p = sp->fts_child; p != NULL;
455 p = p->fts_link)
456 p->fts_accpath =
457 p->fts_parent->fts_accpath;
458 }
459 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
460 if (ISSET(FTS_STOP))
461 return (NULL);
462 return (p);
463 }
464 p = sp->fts_child;
465 sp->fts_child = NULL;
466 goto name;
467 }
468
469 /* Move to the next node on this level. */
470next: tmp = p;
471 if ((p = p->fts_link) != NULL) {
472 /*
473 * If reached the top, return to the original directory (or
474 * the root of the tree), and load the paths for the next root.
475 */
476 if (p->fts_level == FTS_ROOTLEVEL) {
477 /*NT: No fchdir: if (FCHDIR(sp, sp->fts_rfd)) {
478 SET(FTS_STOP);
479 return (NULL);
480 } */
481 fts_free_entry(tmp);
482 fts_load(sp, p);
483 return (sp->fts_cur = p);
484 }
485
486 /*
487 * User may have called fts_set on the node. If skipped,
488 * ignore. If followed, get a file descriptor so we can
489 * get back if necessary.
490 */
491 if (p->fts_instr == FTS_SKIP) {
492 fts_free_entry(tmp);
493 goto next;
494 }
495 if (p->fts_instr == FTS_FOLLOW) {
496 p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
497 /* NT: See above regarding fts_symfd. */
498 if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
499 p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
500 p->fts_flags |= FTS_SYMFOLLOW;
501 }
502 p->fts_instr = FTS_NOINSTR;
503 }
504
505 fts_free_entry(tmp);
506
507name: t = sp->fts_path + NAPPEND(p->fts_parent);
508 *t++ = '/';
509 memmove(t, p->fts_name, p->fts_namelen + 1);
510 return (sp->fts_cur = p);
511 }
512
513 /* Move up to the parent node. */
514 p = tmp->fts_parent;
515
516 if (p->fts_level == FTS_ROOTPARENTLEVEL) {
517 /*
518 * Done; free everything up and set errno to 0 so the user
519 * can distinguish between error and EOF.
520 */
521 fts_free_entry(tmp);
522 fts_free_entry(p);
523 errno = 0;
524 return (sp->fts_cur = NULL);
525 }
526
527 /* NUL terminate the pathname. */
528 sp->fts_path[p->fts_pathlen] = '\0';
529
530 /*
531 * Return to the parent directory. If at a root node or came through
532 * a symlink, go back through the file descriptor. Otherwise, cd up
533 * one directory.
534 *
535 * NT: We're doing no fchdir, but we need to close the directory handle
536 * and clear fts_symfd now.
537 */
538 if (p->fts_flags & FTS_SYMFOLLOW) {
539 p->fts_symfd = INVALID_HANDLE_VALUE;
540 }
541 if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
542 birdCloseFile(p->fts_dirfd);
543 p->fts_dirfd = INVALID_HANDLE_VALUE;
544 }
545 fts_free_entry(tmp);
546 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
547 return (sp->fts_cur = p);
548}
549
550/*
551 * Fts_set takes the stream as an argument although it's not used in this
552 * implementation; it would be necessary if anyone wanted to add global
553 * semantics to fts using fts_set. An error return is allowed for similar
554 * reasons.
555 */
556/* ARGSUSED */
557int FTSCALL
558fts_set(FTS *sp, FTSENT *p, int instr)
559{
560 if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
561 instr != FTS_NOINSTR && instr != FTS_SKIP) {
562 errno = EINVAL;
563 return (1);
564 }
565 p->fts_instr = instr;
566 return (0);
567}
568
569#if 0
570FTSENT * FTSCALL
571fts_children(FTS *sp, int instr)
572{
573 FTSENT *p;
574 int fd, rc, serrno;
575
576 if (instr != 0 && instr != FTS_NAMEONLY) {
577 errno = EINVAL;
578 return (NULL);
579 }
580
581 /* Set current node pointer. */
582 p = sp->fts_cur;
583
584 /*
585 * Errno set to 0 so user can distinguish empty directory from
586 * an error.
587 */
588 errno = 0;
589
590 /* Fatal errors stop here. */
591 if (ISSET(FTS_STOP))
592 return (NULL);
593
594 /* Return logical hierarchy of user's arguments. */
595 if (p->fts_info == FTS_INIT)
596 return (p->fts_link);
597
598 /*
599 * If not a directory being visited in pre-order, stop here. Could
600 * allow FTS_DNR, assuming the user has fixed the problem, but the
601 * same effect is available with FTS_AGAIN.
602 */
603 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
604 return (NULL);
605
606 /* Free up any previous child list. */
607 if (sp->fts_child != NULL)
608 fts_lfree(sp->fts_child);
609
610 if (instr == FTS_NAMEONLY) {
611 SET(FTS_NAMEONLY);
612 instr = BNAMES;
613 } else
614 instr = BCHILD;
615
616 /*
617 * If using chdir on a relative path and called BEFORE fts_read does
618 * its chdir to the root of a traversal, we can lose -- we need to
619 * chdir into the subdirectory, and we don't know where the current
620 * directory is, so we can't get back so that the upcoming chdir by
621 * fts_read will work.
622 */
623 if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
624 ISSET(FTS_NOCHDIR))
625 return (sp->fts_child = fts_build(sp, instr));
626
627 if ((fd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
628 return (NULL);
629 sp->fts_child = fts_build(sp, instr);
630 serrno = (sp->fts_child == NULL) ? errno : 0;
631 rc = fchdir(fd);
632 if (rc < 0 && serrno == 0)
633 serrno = errno;
634 (void)_close(fd);
635 errno = serrno;
636 if (rc < 0)
637 return (NULL);
638 return (sp->fts_child);
639}
640#endif /* PORTME */
641
642#ifndef fts_get_clientptr
643#error "fts_get_clientptr not defined"
644#endif
645
646void *
647(FTSCALL fts_get_clientptr)(FTS *sp)
648{
649
650 return (fts_get_clientptr(sp));
651}
652
653#ifndef fts_get_stream
654#error "fts_get_stream not defined"
655#endif
656
657FTS *
658(FTSCALL fts_get_stream)(FTSENT *p)
659{
660 return (fts_get_stream(p));
661}
662
663void FTSCALL
664fts_set_clientptr(FTS *sp, void *clientptr)
665{
666
667 sp->fts_clientptr = clientptr;
668}
669
670/*
671 * This is the tricky part -- do not casually change *anything* in here. The
672 * idea is to build the linked list of entries that are used by fts_children
673 * and fts_read. There are lots of special cases.
674 *
675 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
676 * set and it's a physical walk (so that symbolic links can't be directories),
677 * we can do things quickly. First, if it's a 4.4BSD file system, the type
678 * of the file is in the directory entry. Otherwise, we assume that the number
679 * of subdirectories in a node is equal to the number of links to the parent.
680 * The former skips all stat calls. The latter skips stat calls in any leaf
681 * directories and for any files after the subdirectories in the directory have
682 * been found, cutting the stat calls by about 2/3.
683 *
684 * NT: We do not do any link counting or stat avoiding, which invalidates the
685 * above warnings. This function is very simple for us.
686 */
687static FTSENT *
688fts_build(FTS *sp, int type)
689{
690 BirdDirEntry_T *dp;
691 FTSENT *p, *head;
692 FTSENT *cur, *tail;
693 DIR *dirp;
694 void *oldaddr;
695 char *cp;
696 int saved_errno, doadjust;
697 long level;
698 size_t dnamlen, len, maxlen, nitems;
699 unsigned fDirOpenFlags;
700
701 /* Set current node pointer. */
702 cur = sp->fts_cur;
703
704 /*
705 * Open the directory for reading. If this fails, we're done.
706 * If being called from fts_read, set the fts_info field.
707 *
708 * NT: We do a two stage open so we can keep the directory handle around
709 * after we've enumerated the directory. The dir handle is used by
710 * us here and by the API users to more efficiently and safely open
711 * members of the directory.
712 */
713 fDirOpenFlags = BIRDDIR_F_EXTRA_INFO | BIRDDIR_F_KEEP_HANDLE;
714 if (cur->fts_dirfd == INVALID_HANDLE_VALUE) {
715 if (cur->fts_parent->fts_dirfd != INVALID_HANDLE_VALUE) {
716 /* (This works fine for symlinks too, since we follow them.) */
717 cur->fts_dirfd = birdOpenFileEx(cur->fts_parent->fts_dirfd,
718 cur->fts_name,
719 FILE_READ_DATA | SYNCHRONIZE,
720 FILE_ATTRIBUTE_NORMAL,
721 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
722 FILE_OPEN,
723 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
724 OBJ_CASE_INSENSITIVE);
725 } else {
726 cur->fts_dirfd = birdOpenFile(cur->fts_accpath,
727 FILE_READ_DATA | SYNCHRONIZE,
728 FILE_ATTRIBUTE_NORMAL,
729 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
730 FILE_OPEN,
731 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
732 OBJ_CASE_INSENSITIVE);
733 }
734 } else {
735 fDirOpenFlags |= BIRDDIR_F_RESTART_SCAN;
736 }
737 dirp = birdDirOpenFromHandle(cur->fts_dirfd, NULL, fDirOpenFlags);
738 if (dirp == NULL) {
739 if (type == BREAD) {
740 cur->fts_info = FTS_DNR;
741 cur->fts_errno = errno;
742 }
743 return (NULL);
744 }
745
746 /*
747 * Figure out the max file name length that can be stored in the
748 * current path -- the inner loop allocates more path as necessary.
749 * We really wouldn't have to do the maxlen calculations here, we
750 * could do them in fts_read before returning the path, but it's a
751 * lot easier here since the length is part of the dirent structure.
752 *
753 * If not changing directories set a pointer so that can just append
754 * each new name into the path.
755 */
756 len = NAPPEND(cur);
757 cp = sp->fts_path + len;
758 *cp++ = '/';
759 len++;
760 maxlen = sp->fts_pathlen - len;
761
762 level = cur->fts_level + 1;
763
764 /* Read the directory, attaching each entry to the `link' pointer. */
765 doadjust = 0;
766 for (head = tail = NULL, nitems = 0; dirp && (dp = birdDirRead(dirp));) {
767 dnamlen = dp->d_namlen;
768 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
769 continue;
770
771 if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
772 goto mem1;
773 if (dnamlen >= maxlen) { /* include space for NUL */
774 oldaddr = sp->fts_path;
775 if (fts_palloc(sp, dnamlen + len + 1)) {
776 /*
777 * No more memory for path or structures. Save
778 * errno, free up the current structure and the
779 * structures already allocated.
780 */
781mem1: saved_errno = errno;
782 if (p)
783 free(p);
784 fts_lfree(head);
785 birdDirClose(dirp);
786 birdCloseFile(cur->fts_dirfd);
787 cur->fts_dirfd = INVALID_HANDLE_VALUE;
788 cur->fts_info = FTS_ERR;
789 SET(FTS_STOP);
790 errno = saved_errno;
791 return (NULL);
792 }
793 /* Did realloc() change the pointer? */
794 if (oldaddr != sp->fts_path) {
795 doadjust = 1;
796 if (ISSET(FTS_NOCHDIR))
797 cp = sp->fts_path + len;
798 }
799 maxlen = sp->fts_pathlen - len;
800 }
801
802 p->fts_level = level;
803 p->fts_parent = sp->fts_cur;
804 p->fts_pathlen = len + dnamlen;
805 p->fts_accpath = p->fts_path;
806 p->fts_stat = dp->d_stat;
807 p->fts_info = fts_process_stats(p, &dp->d_stat);
808
809 /* We walk in directory order so "ls -f" doesn't get upset. */
810 p->fts_link = NULL;
811 if (head == NULL)
812 head = tail = p;
813 else {
814 tail->fts_link = p;
815 tail = p;
816 }
817 ++nitems;
818 }
819
820 birdDirClose(dirp);
821
822 /*
823 * If realloc() changed the address of the path, adjust the
824 * addresses for the rest of the tree and the dir list.
825 */
826 if (doadjust)
827 fts_padjust(sp, head);
828
829 /*
830 * Reset the path back to original state.
831 */
832 sp->fts_path[cur->fts_pathlen] = '\0';
833
834 /* If didn't find anything, return NULL. */
835 if (!nitems) {
836 if (type == BREAD)
837 cur->fts_info = FTS_DP;
838 return (NULL);
839 }
840
841 /* Sort the entries. */
842 if (sp->fts_compar && nitems > 1)
843 head = fts_sort(sp, head, nitems);
844 return (head);
845}
846
847
848/**
849 * @note Only used on NT with input arguments, FTS_AGAIN, and links that needs
850 * following. On link information is generally retrieved during directory
851 * enumeration on NT, in line with it's DOS/OS2/FAT API heritage.
852 */
853static int
854fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
855{
856 int saved_errno;
857 const char *path;
858
859 if (dfd == INVALID_HANDLE_VALUE) {
860 path = p->fts_accpath;
861 } else {
862 path = p->fts_name;
863 }
864
865 /*
866 * If doing a logical walk, or application requested FTS_FOLLOW, do
867 * a stat(2). If that fails, check for a non-existent symlink. If
868 * fail, set the errno from the stat call.
869 */
870 if (ISSET(FTS_LOGICAL) || follow) {
871 if (fstatat(dfd, path, &p->fts_stat, 0)) {
872 saved_errno = errno;
873 if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
874 p->fts_errno = saved_errno;
875 goto err;
876 }
877 errno = 0;
878 if (S_ISLNK(p->fts_stat.st_mode))
879 return (FTS_SLNONE);
880 }
881 } else if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
882 p->fts_errno = errno;
883err: memset(&p->fts_stat, 0, sizeof(struct stat));
884 return (FTS_NS);
885 }
886 return fts_process_stats(p, &p->fts_stat);
887}
888
889/* Shared between fts_stat and fts_build. */
890static int
891fts_process_stats(FTSENT *p, BirdStat_T const *sbp)
892{
893 if (S_ISDIR(sbp->st_mode)) {
894 FTSENT *t;
895 fts_dev_t dev;
896 fts_ino_t ino;
897
898 /*
899 * Set the device/inode. Used to find cycles and check for
900 * crossing mount points. Also remember the link count, used
901 * in fts_build to limit the number of stat calls. It is
902 * understood that these fields are only referenced if fts_info
903 * is set to FTS_D.
904 */
905 dev = p->fts_dev = sbp->st_dev;
906 ino = p->fts_ino = sbp->st_ino;
907 p->fts_nlink = sbp->st_nlink;
908
909 if (ISDOT(p->fts_name))
910 return (FTS_DOT);
911
912 /*
913 * Cycle detection is done by brute force when the directory
914 * is first encountered. If the tree gets deep enough or the
915 * number of symbolic links to directories is high enough,
916 * something faster might be worthwhile.
917 */
918 for (t = p->fts_parent;
919 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
920 if (ino == t->fts_ino && dev == t->fts_dev) {
921 p->fts_cycle = t;
922 return (FTS_DC);
923 }
924 return (FTS_D);
925 }
926 if (S_ISLNK(sbp->st_mode))
927 return (FTS_SL);
928 if (S_ISREG(sbp->st_mode))
929 return (FTS_F);
930 return (FTS_DEFAULT);
931}
932
933/*
934 * The comparison function takes pointers to pointers to FTSENT structures.
935 * Qsort wants a comparison function that takes pointers to void.
936 * (Both with appropriate levels of const-poisoning, of course!)
937 * Use a trampoline function to deal with the difference.
938 */
939static int
940fts_compar(const void *a, const void *b)
941{
942 FTS *parent;
943
944 parent = (*(const FTSENT * const *)a)->fts_fts;
945 return (*parent->fts_compar)(a, b);
946}
947
948static FTSENT *
949fts_sort(FTS *sp, FTSENT *head, size_t nitems)
950{
951 FTSENT **ap, *p;
952
953 /*
954 * Construct an array of pointers to the structures and call qsort(3).
955 * Reassemble the array in the order returned by qsort. If unable to
956 * sort for memory reasons, return the directory entries in their
957 * current order. Allocate enough space for the current needs plus
958 * 40 so don't realloc one entry at a time.
959 */
960 if (nitems > sp->fts_nitems) {
961 void *ptr;
962 sp->fts_nitems = nitems + 40;
963 ptr = realloc(sp->fts_array, sp->fts_nitems * sizeof(FTSENT *));
964 if (ptr != NULL) {
965 sp->fts_array = ptr;
966 } else {
967 free(sp->fts_array);
968 sp->fts_array = NULL;
969 sp->fts_nitems = 0;
970 return (head);
971 }
972 }
973 for (ap = sp->fts_array, p = head; p; p = p->fts_link)
974 *ap++ = p;
975 qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar);
976 for (head = *(ap = sp->fts_array); --nitems; ++ap)
977 ap[0]->fts_link = ap[1];
978 ap[0]->fts_link = NULL;
979 return (head);
980}
981
982static FTSENT *
983fts_alloc(FTS *sp, char *name, size_t namelen)
984{
985 FTSENT *p;
986 size_t len;
987
988 struct ftsent_withstat {
989 FTSENT ent;
990 struct stat statbuf;
991 };
992
993 /*
994 * The file name is a variable length array. Allocate the FTSENT
995 * structure and the file name.
996 */
997 len = sizeof(FTSENT) + namelen + 1;
998 if ((p = malloc(len)) == NULL)
999 return (NULL);
1000
1001 p->fts_name = (char *)(p + 1);
1002 p->fts_statp = &p->fts_stat;
1003
1004 /* Copy the name and guarantee NUL termination. */
1005 memcpy(p->fts_name, name, namelen);
1006 p->fts_name[namelen] = '\0';
1007 p->fts_namelen = namelen;
1008 p->fts_path = sp->fts_path;
1009 p->fts_errno = 0;
1010 p->fts_flags = 0;
1011 p->fts_instr = FTS_NOINSTR;
1012 p->fts_number = 0;
1013 p->fts_pointer = NULL;
1014 p->fts_fts = sp;
1015 p->fts_symfd = INVALID_HANDLE_VALUE;
1016 p->fts_dirfd = INVALID_HANDLE_VALUE;
1017 return (p);
1018}
1019
1020static void
1021fts_lfree(FTSENT *head)
1022{
1023 FTSENT *p;
1024
1025 /* Free a linked list of structures. */
1026 while ((p = head)) {
1027 head = head->fts_link;
1028 assert(p->fts_dirfd == INVALID_HANDLE_VALUE);
1029 free(p);
1030 }
1031}
1032
1033/*
1034 * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1035 * Most systems will allow creation of paths much longer than MAXPATHLEN, even
1036 * though the kernel won't resolve them. Add the size (not just what's needed)
1037 * plus 256 bytes so don't realloc the path 2 bytes at a time.
1038 */
1039static int
1040fts_palloc(FTS *sp, size_t more)
1041{
1042 void *ptr;
1043
1044 sp->fts_pathlen += more + 256;
1045 ptr = realloc(sp->fts_path, sp->fts_pathlen);
1046 if (ptr) {
1047 /*likely */
1048 } else {
1049 free(sp->fts_path);
1050 }
1051 sp->fts_path = ptr;
1052 return (ptr == NULL);
1053}
1054
1055/*
1056 * When the path is realloc'd, have to fix all of the pointers in structures
1057 * already returned.
1058 */
1059static void
1060fts_padjust(FTS *sp, FTSENT *head)
1061{
1062 FTSENT *p;
1063 char *addr = sp->fts_path;
1064
1065#define ADJUST(p) do { \
1066 if ((p)->fts_accpath != (p)->fts_name) { \
1067 (p)->fts_accpath = \
1068 (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
1069 } \
1070 (p)->fts_path = addr; \
1071} while (0)
1072 /* Adjust the current set of children. */
1073 for (p = sp->fts_child; p; p = p->fts_link)
1074 ADJUST(p);
1075
1076 /* Adjust the rest of the tree, including the current level. */
1077 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1078 ADJUST(p);
1079 p = p->fts_link ? p->fts_link : p->fts_parent;
1080 }
1081}
1082
1083static size_t
1084fts_maxarglen(char * const *argv)
1085{
1086 size_t len, max;
1087
1088 for (max = 0; *argv; ++argv)
1089 if ((len = strlen(*argv)) > max)
1090 max = len;
1091 return (max + 1);
1092}
1093
1094/*
1095 * Change to dir specified by fd or p->fts_accpath without getting
1096 * tricked by someone changing the world out from underneath us.
1097 * Assumes p->fts_dev and p->fts_ino are filled in.
1098 */
1099static int
1100fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path)
1101{
1102#if 0
1103 int ret, oerrno, newfd;
1104 struct stat sb;
1105
1106 newfd = fd;
1107#endif
1108 if (ISSET(FTS_NOCHDIR))
1109 return (0);
1110 assert(0);
1111 return -1;
1112#if 0
1113 if (fd < 0 && (newfd = _open(path, O_RDONLY | O_DIRECTORY |
1114 O_CLOEXEC, 0)) < 0)
1115 return (-1);
1116 if (_fstat(newfd, &sb)) {
1117 ret = -1;
1118 goto bail;
1119 }
1120 if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
1121 errno = ENOENT; /* disinformation */
1122 ret = -1;
1123 goto bail;
1124 }
1125 ret = fchdir(newfd);
1126bail:
1127 oerrno = errno;
1128 if (fd < 0)
1129 (void)_close(newfd);
1130 errno = oerrno;
1131 return (ret);
1132#endif
1133}
1134
1135/*
1136 * Check if the filesystem for "ent" has UFS-style links.
1137 *
1138 * bird: NT does not, which is why they need this check.
1139 * See comment on r129052 (2004-05-08 15:09:02Z).
1140 */
1141static int
1142fts_ufslinks(FTS *sp, const FTSENT *ent)
1143{
1144#if 0
1145 struct _fts_private *priv;
1146 const char **cpp;
1147
1148 priv = (struct _fts_private *)sp;
1149 /*
1150 * If this node's device is different from the previous, grab
1151 * the filesystem information, and decide on the reliability
1152 * of the link information from this filesystem for stat(2)
1153 * avoidance.
1154 */
1155 if (priv->ftsp_dev != ent->fts_dev) {
1156 if (statfs(ent->fts_path, &priv->ftsp_statfs) != -1) {
1157 priv->ftsp_dev = ent->fts_dev;
1158 priv->ftsp_linksreliable = 0;
1159 for (cpp = ufslike_filesystems; *cpp; cpp++) {
1160 if (strcmp(priv->ftsp_statfs.f_fstypename,
1161 *cpp) == 0) {
1162 priv->ftsp_linksreliable = 1;
1163 break;
1164 }
1165 }
1166 } else {
1167 priv->ftsp_linksreliable = 0;
1168 }
1169 }
1170 return (priv->ftsp_linksreliable);
1171#else
1172 (void)sp; (void)ent;
1173 return 0;
1174#endif
1175}
1176
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