~gl-az/percona-xtrabackup/2.1-io-block-size

« back to all changes in this revision

Viewing changes to src/libarchive/tar/tree.c

  • Committer: Alexey Kopytov
  • Date: 2012-02-10 20:05:56 UTC
  • mto: (391.1.5 staging)
  • mto: This revision was merged to the branch mainline in revision 390.
  • Revision ID: akopytov@gmail.com-20120210200556-6kx41z8wwrqfucro
Rebase of the parallel compression patch on new trunk + post-review
fixes.

Implementation of parallel compression and streaming for XtraBackup.

This revision implements the following changes:

* InnoDB files are now streamed by the xtrabackup binary rather than
innobackupex. As a result, integrity is now verified by xtrabackup and
thus tar4ibd is no longer needed, so it was removed.

* xtrabackup binary now accepts the new '--stream' option which has
exactly the same semantics as the '--stream' option in
innobackupex: it tells xtrabackup to stream all files to the standard
output in the specified format rather than storing them locally.

* The xtrabackup binary can now do parallel compression using the
quicklz library. Two new options were added to xtrabackup to support
this feature:

- '--compress' tells xtrabackup to compress all output data, including
the transaction log file and meta data files, using the specified
compression algorithm. The only currently supported algorithm is
'quicklz'. The resulting files have the qpress archive format,
i.e. every *.qp file produced by xtrabackup is essentially a one-file
qpress archive and can be extracted and uncompressed by the qpress
file archiver (http://www.quicklz.com/).

- '--compress-threads' specifies the number of worker threads used by
xtrabackup for parallel data compression. This option defaults to 1.

Parallel compression ('--compress-threads') can be used together with
parallel file copying ('--parallel'). For example, '--parallel=4
--compress --compress-threads=2' will create 4 IO threads that will
read the data and pipe it to 2 compression threads.

* To support simultaneous compression and streaming, a new custom
streaming format called 'xbstream' was introduced to XtraBackup in
addition to the 'tar' format. That was required to overcome some
limitations of traditional archive formats such as 'tar', 'cpio' and
others that do not allow streaming dynamically generated files, for
example dynamically compressed files.  Other advantages of xbstream over
traditional streaming/archive formats include ability to stream multiple
files concurrently (so it is possible to use streaming in the xbstream
format together with the --parallel option) and more compact data
storage.

* To allow streaming and extracting files to/from the xbstream format
produced by xtrabackup, a new utility aptly called 'xbstream' was
added to the XtraBackup distribution. This utility has a tar-like
interface:

- with the '-x' option it extracts files from the stream read from its
standard input to the current directory unless specified otherwise
with the '-C' option.

- with the '-c' option it streams files specified on the command line
to its standard output.

The utility also tries to minimize its impact on the OS page cache by
using the appropriate posix_fadvise() calls when available.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2003-2007 Tim Kientzle
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 * 1. Redistributions of source code must retain the above copyright
 
9
 *    notice, this list of conditions and the following disclaimer.
 
10
 * 2. Redistributions in binary form must reproduce the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer in the
 
12
 *    documentation and/or other materials provided with the distribution.
 
13
 *
 
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
15
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
16
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
17
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
18
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
19
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
20
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
21
 
 
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
25
 */
 
26
 
 
27
/*-
 
28
 * This is a new directory-walking system that addresses a number
 
29
 * of problems I've had with fts(3).  In particular, it has no
 
30
 * pathname-length limits (other than the size of 'int'), handles
 
31
 * deep logical traversals, uses considerably less memory, and has
 
32
 * an opaque interface (easier to modify in the future).
 
33
 *
 
34
 * Internally, it keeps a single list of "tree_entry" items that
 
35
 * represent filesystem objects that require further attention.
 
36
 * Non-directories are not kept in memory: they are pulled from
 
37
 * readdir(), returned to the client, then freed as soon as possible.
 
38
 * Any directory entry to be traversed gets pushed onto the stack.
 
39
 *
 
40
 * There is surprisingly little information that needs to be kept for
 
41
 * each item on the stack.  Just the name, depth (represented here as the
 
42
 * string length of the parent directory's pathname), and some markers
 
43
 * indicating how to get back to the parent (via chdir("..") for a
 
44
 * regular dir or via fchdir(2) for a symlink).
 
45
 */
 
46
#include "bsdtar_platform.h"
 
47
__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.9 2008/11/27 05:49:52 kientzle Exp $");
 
48
 
 
49
#ifdef HAVE_SYS_STAT_H
 
50
#include <sys/stat.h>
 
51
#endif
 
52
#ifdef HAVE_DIRECT_H
 
53
#include <direct.h>
 
54
#endif
 
55
#ifdef HAVE_DIRENT_H
 
56
#include <dirent.h>
 
57
#endif
 
58
#ifdef HAVE_ERRNO_H
 
59
#include <errno.h>
 
60
#endif
 
61
#ifdef HAVE_FCNTL_H
 
62
#include <fcntl.h>
 
63
#endif
 
64
#ifdef HAVE_STDLIB_H
 
65
#include <stdlib.h>
 
66
#endif
 
67
#ifdef HAVE_STRING_H
 
68
#include <string.h>
 
69
#endif
 
70
#ifdef HAVE_UNISTD_H
 
71
#include <unistd.h>
 
72
#endif
 
73
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
 
74
#include <windows.h>
 
75
#endif
 
76
 
 
77
#include "tree.h"
 
78
 
 
79
/*
 
80
 * TODO:
 
81
 *    1) Loop checking.
 
82
 *    3) Arbitrary logical traversals by closing/reopening intermediate fds.
 
83
 */
 
84
 
 
85
struct tree_entry {
 
86
        int depth;
 
87
        struct tree_entry *next;
 
88
        struct tree_entry *parent;
 
89
        char *name;
 
90
        size_t dirname_length;
 
91
        dev_t dev;
 
92
        ino_t ino;
 
93
        int flags;
 
94
        /* How to return back to the parent of a symlink. */
 
95
#ifdef HAVE_FCHDIR
 
96
        int symlink_parent_fd;
 
97
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
98
        char *symlink_parent_path;
 
99
#else
 
100
#error fchdir function required.
 
101
#endif
 
102
};
 
103
 
 
104
/* Definitions for tree_entry.flags bitmap. */
 
105
#define isDir 1 /* This entry is a regular directory. */
 
106
#define isDirLink 2 /* This entry is a symbolic link to a directory. */
 
107
#define needsFirstVisit 4 /* This is an initial entry. */
 
108
#define needsDescent 8 /* This entry needs to be previsited. */
 
109
#define needsOpen 16 /* This is a directory that needs to be opened. */
 
110
#define needsAscent 32 /* This entry needs to be postvisited. */
 
111
 
 
112
/*
 
113
 * On Windows, "first visit" is handled as a pattern to be handed to
 
114
 * _findfirst().  This is consistent with Windows conventions that
 
115
 * file patterns are handled within the application.  On Posix,
 
116
 * "first visit" is just returned to the client.
 
117
 */
 
118
 
 
119
/*
 
120
 * Local data for this package.
 
121
 */
 
122
struct tree {
 
123
        struct tree_entry       *stack;
 
124
        struct tree_entry       *current;
 
125
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
 
126
        HANDLE d;
 
127
        BY_HANDLE_FILE_INFORMATION fileInfo;
 
128
#define INVALID_DIR_HANDLE INVALID_HANDLE_VALUE
 
129
        WIN32_FIND_DATA _findData;
 
130
        WIN32_FIND_DATA *findData;
 
131
#else
 
132
        DIR     *d;
 
133
#define INVALID_DIR_HANDLE NULL
 
134
        struct dirent *de;
 
135
#endif
 
136
        int      flags;
 
137
        int      visit_type;
 
138
        int      tree_errno; /* Error code from last failed operation. */
 
139
 
 
140
        /* Dynamically-sized buffer for holding path */
 
141
        char    *buff;
 
142
        size_t   buff_length;
 
143
 
 
144
        const char *basename; /* Last path element */
 
145
        size_t   dirname_length; /* Leading dir length */
 
146
        size_t   path_length; /* Total path length */
 
147
 
 
148
        int      depth;
 
149
        int      openCount;
 
150
        int      maxOpenCount;
 
151
 
 
152
        struct stat     lst;
 
153
        struct stat     st;
 
154
};
 
155
 
 
156
/* Definitions for tree.flags bitmap. */
 
157
#define hasStat 16  /* The st entry is valid. */
 
158
#define hasLstat 32 /* The lst entry is valid. */
 
159
#define hasFileInfo 64 /* The Windows fileInfo entry is valid. */
 
160
 
 
161
#if defined(_WIN32) && !defined(__CYGWIN__)
 
162
static int
 
163
tree_dir_next_windows(struct tree *t, const char *pattern);
 
164
#else
 
165
static int
 
166
tree_dir_next_posix(struct tree *t);
 
167
#endif
 
168
 
 
169
#ifdef HAVE_DIRENT_D_NAMLEN
 
170
/* BSD extension; avoids need for a strlen() call. */
 
171
#define D_NAMELEN(dp)   (dp)->d_namlen
 
172
#else
 
173
#define D_NAMELEN(dp)   (strlen((dp)->d_name))
 
174
#endif
 
175
 
 
176
#include <stdio.h>
 
177
void
 
178
tree_dump(struct tree *t, FILE *out)
 
179
{
 
180
        char buff[300];
 
181
        struct tree_entry *te;
 
182
 
 
183
        fprintf(out, "\tdepth: %d\n", t->depth);
 
184
        fprintf(out, "\tbuff: %s\n", t->buff);
 
185
        fprintf(out, "\tpwd: %s\n", getcwd(buff, sizeof(buff)));
 
186
        fprintf(out, "\tbasename: %s\n", t->basename);
 
187
        fprintf(out, "\tstack:\n");
 
188
        for (te = t->stack; te != NULL; te = te->next) {
 
189
                fprintf(out, "\t\t%s%d:\"%s\" %s%s%s%s%s%s\n",
 
190
                    t->current == te ? "*" : " ",
 
191
                    te->depth,
 
192
                    te->name,
 
193
                    te->flags & needsFirstVisit ? "V" : "",
 
194
                    te->flags & needsDescent ? "D" : "",
 
195
                    te->flags & needsOpen ? "O" : "",
 
196
                    te->flags & needsAscent ? "A" : "",
 
197
                    te->flags & isDirLink ? "L" : "",
 
198
                    (t->current == te && t->d) ? "+" : ""
 
199
                );
 
200
        }
 
201
}
 
202
 
 
203
/*
 
204
 * Add a directory path to the current stack.
 
205
 */
 
206
static void
 
207
tree_push(struct tree *t, const char *path)
 
208
{
 
209
        struct tree_entry *te;
 
210
 
 
211
        te = malloc(sizeof(*te));
 
212
        memset(te, 0, sizeof(*te));
 
213
        te->next = t->stack;
 
214
        te->parent = t->current;
 
215
        if (te->parent)
 
216
                te->depth = te->parent->depth + 1;
 
217
        t->stack = te;
 
218
#ifdef HAVE_FCHDIR
 
219
        te->symlink_parent_fd = -1;
 
220
        te->name = strdup(path);
 
221
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
222
        te->symlink_parent_path = NULL;
 
223
        te->name = strdup(path);
 
224
#endif
 
225
        te->flags = needsDescent | needsOpen | needsAscent;
 
226
        te->dirname_length = t->dirname_length;
 
227
}
 
228
 
 
229
/*
 
230
 * Append a name to the current dir path.
 
231
 */
 
232
static void
 
233
tree_append(struct tree *t, const char *name, size_t name_length)
 
234
{
 
235
        char *p;
 
236
        size_t size_needed;
 
237
 
 
238
        if (t->buff != NULL)
 
239
                t->buff[t->dirname_length] = '\0';
 
240
        /* Strip trailing '/' from name, unless entire name is "/". */
 
241
        while (name_length > 1 && name[name_length - 1] == '/')
 
242
                name_length--;
 
243
 
 
244
        /* Resize pathname buffer as needed. */
 
245
        size_needed = name_length + 1 + t->dirname_length;
 
246
        if (t->buff_length < size_needed) {
 
247
                if (t->buff_length < 1024)
 
248
                        t->buff_length = 1024;
 
249
                while (t->buff_length < size_needed)
 
250
                        t->buff_length *= 2;
 
251
                t->buff = realloc(t->buff, t->buff_length);
 
252
        }
 
253
        if (t->buff == NULL)
 
254
                abort();
 
255
        p = t->buff + t->dirname_length;
 
256
        t->path_length = t->dirname_length + name_length;
 
257
        /* Add a separating '/' if it's needed. */
 
258
        if (t->dirname_length > 0 && p[-1] != '/') {
 
259
                *p++ = '/';
 
260
                t->path_length ++;
 
261
        }
 
262
#if HAVE_STRNCPY_S
 
263
        strncpy_s(p, t->buff_length - (p - t->buff), name, name_length);
 
264
#else
 
265
        strncpy(p, name, name_length);
 
266
#endif
 
267
        p[name_length] = '\0';
 
268
        t->basename = p;
 
269
}
 
270
 
 
271
/*
 
272
 * Open a directory tree for traversal.
 
273
 */
 
274
struct tree *
 
275
tree_open(const char *path)
 
276
{
 
277
#ifdef HAVE_FCHDIR
 
278
        struct tree *t;
 
279
 
 
280
        t = malloc(sizeof(*t));
 
281
        memset(t, 0, sizeof(*t));
 
282
        /* First item is set up a lot like a symlink traversal. */
 
283
        tree_push(t, path);
 
284
        t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
 
285
        t->stack->symlink_parent_fd = open(".", O_RDONLY);
 
286
        t->openCount++;
 
287
        t->d = INVALID_DIR_HANDLE;
 
288
        return (t);
 
289
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
290
        struct tree *t;
 
291
        char *cwd = _getcwd(NULL, 0);
 
292
        char *pathname = strdup(path), *p, *base;
 
293
 
 
294
        if (pathname == NULL)
 
295
                abort();
 
296
        for (p = pathname; *p != '\0'; ++p) {
 
297
                if (*p == '\\')
 
298
                        *p = '/';
 
299
        }
 
300
        base = pathname;
 
301
 
 
302
        t = malloc(sizeof(*t));
 
303
        memset(t, 0, sizeof(*t));
 
304
        /* First item is set up a lot like a symlink traversal. */
 
305
        /* printf("Looking for wildcard in %s\n", path); */
 
306
        /* TODO: wildcard detection here screws up on \\?\c:\ UNC names */
 
307
        if (strchr(base, '*') || strchr(base, '?')) {
 
308
                // It has a wildcard in it...
 
309
                // Separate the last element.
 
310
                p = strrchr(base, '/');
 
311
                if (p != NULL) {
 
312
                        *p = '\0';
 
313
                        chdir(base);
 
314
                        tree_append(t, base, p - base);
 
315
                        t->dirname_length = t->path_length;
 
316
                        base = p + 1;
 
317
                }
 
318
        }
 
319
        tree_push(t, base);
 
320
        free(pathname);
 
321
        t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
 
322
        t->stack->symlink_parent_path = cwd;
 
323
        t->d = INVALID_DIR_HANDLE;
 
324
        return (t);
 
325
#endif
 
326
}
 
327
 
 
328
/*
 
329
 * We've finished a directory; ascend back to the parent.
 
330
 */
 
331
static int
 
332
tree_ascend(struct tree *t)
 
333
{
 
334
        struct tree_entry *te;
 
335
        int r = 0;
 
336
 
 
337
        te = t->stack;
 
338
        t->depth--;
 
339
        if (te->flags & isDirLink) {
 
340
#ifdef HAVE_FCHDIR
 
341
                if (fchdir(te->symlink_parent_fd) != 0) {
 
342
                        t->tree_errno = errno;
 
343
                        r = TREE_ERROR_FATAL;
 
344
                }
 
345
                close(te->symlink_parent_fd);
 
346
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
347
                if (SetCurrentDirectory(te->symlink_parent_path) == 0) {
 
348
                        t->tree_errno = errno;
 
349
                        r = TREE_ERROR_FATAL;
 
350
                }
 
351
                free(te->symlink_parent_path);
 
352
                te->symlink_parent_path = NULL;
 
353
#endif
 
354
                t->openCount--;
 
355
        } else {
 
356
#if defined(_WIN32) && !defined(__CYGWIN__)
 
357
                if (SetCurrentDirectory("..") == 0) {
 
358
#else
 
359
                if (chdir("..") != 0) {
 
360
#endif
 
361
                        t->tree_errno = errno;
 
362
                        r = TREE_ERROR_FATAL;
 
363
                }
 
364
        }
 
365
        return (r);
 
366
}
 
367
 
 
368
/*
 
369
 * Pop the working stack.
 
370
 */
 
371
static void
 
372
tree_pop(struct tree *t)
 
373
{
 
374
        struct tree_entry *te;
 
375
 
 
376
        if (t->buff)
 
377
                t->buff[t->dirname_length] = '\0';
 
378
        if (t->stack == t->current && t->current != NULL)
 
379
                t->current = t->current->parent;
 
380
        te = t->stack;
 
381
        t->stack = te->next;
 
382
        t->dirname_length = te->dirname_length;
 
383
        if (t->buff) {
 
384
                t->basename = t->buff + t->dirname_length;
 
385
                while (t->basename[0] == '/')
 
386
                        t->basename++;
 
387
        }
 
388
        free(te->name);
 
389
        free(te);
 
390
}
 
391
 
 
392
/*
 
393
 * Get the next item in the tree traversal.
 
394
 */
 
395
int
 
396
tree_next(struct tree *t)
 
397
{
 
398
        int r;
 
399
 
 
400
        /* If we're called again after a fatal error, that's an API
 
401
         * violation.  Just crash now. */
 
402
        if (t->visit_type == TREE_ERROR_FATAL) {
 
403
                fprintf(stderr, "Unable to continue traversing"
 
404
                    " directory heirarchy after a fatal error.");
 
405
                abort();
 
406
        }
 
407
 
 
408
        while (t->stack != NULL) {
 
409
                /* If there's an open dir, get the next entry from there. */
 
410
                if (t->d != INVALID_DIR_HANDLE) {
 
411
#if defined(_WIN32) && !defined(__CYGWIN__)
 
412
                        r = tree_dir_next_windows(t, NULL);
 
413
#else
 
414
                        r = tree_dir_next_posix(t);
 
415
#endif
 
416
                        if (r == 0)
 
417
                                continue;
 
418
                        return (r);
 
419
                }
 
420
 
 
421
                if (t->stack->flags & needsFirstVisit) {
 
422
#if defined(_WIN32) && !defined(__CYGWIN__)
 
423
                        char *d = t->stack->name;
 
424
                        t->stack->flags &= ~needsFirstVisit;
 
425
                        if (strchr(d, '*') || strchr(d, '?')) {
 
426
                                r = tree_dir_next_windows(t, d);
 
427
                                if (r == 0)
 
428
                                        continue;
 
429
                                return (r);
 
430
                        }
 
431
                        // Not a pattern, handle it as-is...
 
432
#endif
 
433
                        /* Top stack item needs a regular visit. */
 
434
                        t->current = t->stack;
 
435
                        tree_append(t, t->stack->name, strlen(t->stack->name));
 
436
                        //t->dirname_length = t->path_length;
 
437
                        //tree_pop(t);
 
438
                        t->stack->flags &= ~needsFirstVisit;
 
439
                        return (t->visit_type = TREE_REGULAR);
 
440
                } else if (t->stack->flags & needsDescent) {
 
441
                        /* Top stack item is dir to descend into. */
 
442
                        t->current = t->stack;
 
443
                        tree_append(t, t->stack->name, strlen(t->stack->name));
 
444
                        t->stack->flags &= ~needsDescent;
 
445
                        /* If it is a link, set up fd for the ascent. */
 
446
                        if (t->stack->flags & isDirLink) {
 
447
#ifdef HAVE_FCHDIR
 
448
                                t->stack->symlink_parent_fd = open(".", O_RDONLY);
 
449
                                t->openCount++;
 
450
                                if (t->openCount > t->maxOpenCount)
 
451
                                        t->maxOpenCount = t->openCount;
 
452
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
453
                                t->stack->symlink_parent_path = _getcwd(NULL, 0);
 
454
#endif
 
455
                        }
 
456
                        t->dirname_length = t->path_length;
 
457
#if defined(_WIN32) && !defined(__CYGWIN__)
 
458
                        if (t->path_length == 259 || !SetCurrentDirectory(t->stack->name) != 0)
 
459
#else
 
460
                        if (chdir(t->stack->name) != 0)
 
461
#endif
 
462
                        {
 
463
                                /* chdir() failed; return error */
 
464
                                tree_pop(t);
 
465
                                t->tree_errno = errno;
 
466
                                return (t->visit_type = TREE_ERROR_DIR);
 
467
                        }
 
468
                        t->depth++;
 
469
                        return (t->visit_type = TREE_POSTDESCENT);
 
470
                } else if (t->stack->flags & needsOpen) {
 
471
                        t->stack->flags &= ~needsOpen;
 
472
#if defined(_WIN32) && !defined(__CYGWIN__)
 
473
                        r = tree_dir_next_windows(t, "*");
 
474
#else
 
475
                        r = tree_dir_next_posix(t);
 
476
#endif
 
477
                        if (r == 0)
 
478
                                continue;
 
479
                        return (r);
 
480
                } else if (t->stack->flags & needsAscent) {
 
481
                        /* Top stack item is dir and we're done with it. */
 
482
                        r = tree_ascend(t);
 
483
                        tree_pop(t);
 
484
                        t->visit_type = r != 0 ? r : TREE_POSTASCENT;
 
485
                        return (t->visit_type);
 
486
                } else {
 
487
                        /* Top item on stack is dead. */
 
488
                        tree_pop(t);
 
489
                        t->flags &= ~hasLstat;
 
490
                        t->flags &= ~hasStat;
 
491
                }
 
492
        }
 
493
        return (t->visit_type = 0);
 
494
}
 
495
 
 
496
#if defined(_WIN32) && !defined(__CYGWIN__)
 
497
static int
 
498
tree_dir_next_windows(struct tree *t, const char *pattern)
 
499
{
 
500
        const char *name;
 
501
        size_t namelen;
 
502
        int r;
 
503
 
 
504
        for (;;) {
 
505
                if (pattern != NULL) {
 
506
                        t->d = FindFirstFile(pattern, &t->_findData);
 
507
                        if (t->d == INVALID_DIR_HANDLE) {
 
508
                                r = tree_ascend(t); /* Undo "chdir" */
 
509
                                tree_pop(t);
 
510
                                t->tree_errno = errno;
 
511
                                t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
 
512
                                return (t->visit_type);
 
513
                        }
 
514
                        t->findData = &t->_findData;
 
515
                        pattern = NULL;
 
516
                } else if (!FindNextFile(t->d, &t->_findData)) {
 
517
                        FindClose(t->d);
 
518
                        t->d = INVALID_DIR_HANDLE;
 
519
                        t->findData = NULL;
 
520
                        return (0);
 
521
                }
 
522
                name = t->findData->cFileName;
 
523
                namelen = strlen(name);
 
524
                t->flags &= ~hasLstat;
 
525
                t->flags &= ~hasStat;
 
526
                if (name[0] == '.' && name[1] == '\0')
 
527
                        continue;
 
528
                if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
 
529
                        continue;
 
530
                tree_append(t, name, namelen);
 
531
                return (t->visit_type = TREE_REGULAR);
 
532
        }
 
533
}
 
534
#else
 
535
static int
 
536
tree_dir_next_posix(struct tree *t)
 
537
{
 
538
        int r;
 
539
        const char *name;
 
540
        size_t namelen;
 
541
 
 
542
        if (t->d == NULL) {
 
543
                if ((t->d = opendir(".")) == NULL) {
 
544
                        r = tree_ascend(t); /* Undo "chdir" */
 
545
                        tree_pop(t);
 
546
                        t->tree_errno = errno;
 
547
                        t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
 
548
                        return (t->visit_type);
 
549
                }
 
550
        }
 
551
        for (;;) {
 
552
                t->de = readdir(t->d);
 
553
                if (t->de == NULL) {
 
554
                        closedir(t->d);
 
555
                        t->d = INVALID_DIR_HANDLE;
 
556
                        return (0);
 
557
                }
 
558
                name = t->de->d_name;
 
559
                namelen = D_NAMELEN(t->de);
 
560
                t->flags &= ~hasLstat;
 
561
                t->flags &= ~hasStat;
 
562
                if (name[0] == '.' && name[1] == '\0')
 
563
                        continue;
 
564
                if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
 
565
                        continue;
 
566
                tree_append(t, name, namelen);
 
567
                return (t->visit_type = TREE_REGULAR);
 
568
        }
 
569
}
 
570
#endif
 
571
 
 
572
/*
 
573
 * Return error code.
 
574
 */
 
575
int
 
576
tree_errno(struct tree *t)
 
577
{
 
578
        return (t->tree_errno);
 
579
}
 
580
 
 
581
/*
 
582
 * Called by the client to mark the directory just returned from
 
583
 * tree_next() as needing to be visited.
 
584
 */
 
585
void
 
586
tree_descend(struct tree *t)
 
587
{
 
588
        if (t->visit_type != TREE_REGULAR)
 
589
                return;
 
590
 
 
591
        if (tree_current_is_physical_dir(t)) {
 
592
                tree_push(t, t->basename);
 
593
                t->stack->flags |= isDir;
 
594
        } else if (tree_current_is_dir(t)) {
 
595
                tree_push(t, t->basename);
 
596
                t->stack->flags |= isDirLink;
 
597
        }
 
598
}
 
599
 
 
600
/*
 
601
 * Get the stat() data for the entry just returned from tree_next().
 
602
 */
 
603
const struct stat *
 
604
tree_current_stat(struct tree *t)
 
605
{
 
606
        if (!(t->flags & hasStat)) {
 
607
                if (stat(tree_current_access_path(t), &t->st) != 0)
 
608
                        return NULL;
 
609
                t->flags |= hasStat;
 
610
        }
 
611
        return (&t->st);
 
612
}
 
613
 
 
614
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
 
615
const BY_HANDLE_FILE_INFORMATION *
 
616
tree_current_file_information(struct tree *t)
 
617
{
 
618
        if (!(t->flags & hasFileInfo)) {
 
619
                HANDLE h = CreateFile(tree_current_access_path(t),
 
620
                        0, 0, NULL,
 
621
                        OPEN_EXISTING,
 
622
                        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
 
623
                        NULL);
 
624
                if (h == INVALID_HANDLE_VALUE)
 
625
                        return NULL;
 
626
                if (!GetFileInformationByHandle(h, &t->fileInfo)) {
 
627
                        CloseHandle(h);
 
628
                        return NULL;
 
629
                }
 
630
                CloseHandle(h);
 
631
                t->flags |= hasFileInfo;
 
632
        }
 
633
        return (&t->fileInfo);
 
634
}
 
635
#endif
 
636
/*
 
637
 * Get the lstat() data for the entry just returned from tree_next().
 
638
 */
 
639
const struct stat *
 
640
tree_current_lstat(struct tree *t)
 
641
{
 
642
#if defined(_WIN32) && !defined(__CYGWIN__)
 
643
        return (tree_current_stat(t));
 
644
#else
 
645
        if (!(t->flags & hasLstat)) {
 
646
                if (lstat(tree_current_access_path(t), &t->lst) != 0)
 
647
                        return NULL;
 
648
                t->flags |= hasLstat;
 
649
        }
 
650
        return (&t->lst);
 
651
#endif
 
652
}
 
653
 
 
654
/*
 
655
 * Test whether current entry is a dir or link to a dir.
 
656
 */
 
657
int
 
658
tree_current_is_dir(struct tree *t)
 
659
{
 
660
#if defined(_WIN32) && !defined(__CYGWIN__)
 
661
        if (t->findData)
 
662
                return (t->findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
 
663
        if (tree_current_file_information(t))
 
664
                return (t->fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
 
665
        return (0);
 
666
#else
 
667
        const struct stat *st;
 
668
        /*
 
669
         * If we already have lstat() info, then try some
 
670
         * cheap tests to determine if this is a dir.
 
671
         */
 
672
        if (t->flags & hasLstat) {
 
673
                /* If lstat() says it's a dir, it must be a dir. */
 
674
                if (S_ISDIR(tree_current_lstat(t)->st_mode))
 
675
                        return 1;
 
676
                /* Not a dir; might be a link to a dir. */
 
677
                /* If it's not a link, then it's not a link to a dir. */
 
678
                if (!S_ISLNK(tree_current_lstat(t)->st_mode))
 
679
                        return 0;
 
680
                /*
 
681
                 * It's a link, but we don't know what it's a link to,
 
682
                 * so we'll have to use stat().
 
683
                 */
 
684
        }
 
685
 
 
686
        st = tree_current_stat(t);
 
687
        /* If we can't stat it, it's not a dir. */
 
688
        if (st == NULL)
 
689
                return 0;
 
690
        /* Use the definitive test.  Hopefully this is cached. */
 
691
        return (S_ISDIR(st->st_mode));
 
692
#endif
 
693
}
 
694
 
 
695
/*
 
696
 * Test whether current entry is a physical directory.  Usually, we
 
697
 * already have at least one of stat() or lstat() in memory, so we
 
698
 * use tricks to try to avoid an extra trip to the disk.
 
699
 */
 
700
int
 
701
tree_current_is_physical_dir(struct tree *t)
 
702
{
 
703
#if defined(_WIN32) && !defined(__CYGWIN__)
 
704
        if (tree_current_is_physical_link(t))
 
705
                return (0);
 
706
        return (tree_current_is_dir(t));
 
707
#else
 
708
        const struct stat *st;
 
709
 
 
710
        /*
 
711
         * If stat() says it isn't a dir, then it's not a dir.
 
712
         * If stat() data is cached, this check is free, so do it first.
 
713
         */
 
714
        if ((t->flags & hasStat)
 
715
            && (!S_ISDIR(tree_current_stat(t)->st_mode)))
 
716
                return 0;
 
717
 
 
718
        /*
 
719
         * Either stat() said it was a dir (in which case, we have
 
720
         * to determine whether it's really a link to a dir) or
 
721
         * stat() info wasn't available.  So we use lstat(), which
 
722
         * hopefully is already cached.
 
723
         */
 
724
 
 
725
        st = tree_current_lstat(t);
 
726
        /* If we can't stat it, it's not a dir. */
 
727
        if (st == NULL)
 
728
                return 0;
 
729
        /* Use the definitive test.  Hopefully this is cached. */
 
730
        return (S_ISDIR(st->st_mode));
 
731
#endif
 
732
}
 
733
 
 
734
/*
 
735
 * Test whether current entry is a symbolic link.
 
736
 */
 
737
int
 
738
tree_current_is_physical_link(struct tree *t)
 
739
{
 
740
#if defined(_WIN32) && !defined(__CYGWIN__)
 
741
#ifndef IO_REPARSE_TAG_SYMLINK
 
742
/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
 
743
#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
 
744
#endif
 
745
        if (t->findData)
 
746
                return ((t->findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
 
747
                                && (t->findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK));
 
748
        return (0);
 
749
#else
 
750
        const struct stat *st = tree_current_lstat(t);
 
751
        if (st == NULL)
 
752
                return 0;
 
753
        return (S_ISLNK(st->st_mode));
 
754
#endif
 
755
}
 
756
 
 
757
/*
 
758
 * Return the access path for the entry just returned from tree_next().
 
759
 */
 
760
const char *
 
761
tree_current_access_path(struct tree *t)
 
762
{
 
763
        return (t->basename);
 
764
}
 
765
 
 
766
/*
 
767
 * Return the full path for the entry just returned from tree_next().
 
768
 */
 
769
const char *
 
770
tree_current_path(struct tree *t)
 
771
{
 
772
        return (t->buff);
 
773
}
 
774
 
 
775
/*
 
776
 * Return the length of the path for the entry just returned from tree_next().
 
777
 */
 
778
size_t
 
779
tree_current_pathlen(struct tree *t)
 
780
{
 
781
        return (t->path_length);
 
782
}
 
783
 
 
784
/*
 
785
 * Return the nesting depth of the entry just returned from tree_next().
 
786
 */
 
787
int
 
788
tree_current_depth(struct tree *t)
 
789
{
 
790
        return (t->depth);
 
791
}
 
792
 
 
793
/*
 
794
 * Terminate the traversal and release any resources.
 
795
 */
 
796
void
 
797
tree_close(struct tree *t)
 
798
{
 
799
        /* Release anything remaining in the stack. */
 
800
        while (t->stack != NULL)
 
801
                tree_pop(t);
 
802
        free(t->buff);
 
803
        /* TODO: Ensure that premature close() resets cwd */
 
804
#if 0
 
805
#ifdef HAVE_FCHDIR
 
806
        if (t->initialDirFd >= 0) {
 
807
                int s = fchdir(t->initialDirFd);
 
808
                (void)s; /* UNUSED */
 
809
                close(t->initialDirFd);
 
810
                t->initialDirFd = -1;
 
811
        }
 
812
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
813
        if (t->initialDir != NULL) {
 
814
                SetCurrentDir(t->initialDir);
 
815
                free(t->initialDir);
 
816
                t->initialDir = NULL;
 
817
        }
 
818
#endif
 
819
#endif
 
820
        free(t);
 
821
}