2
* Copyright (c) 2003-2004 Tim Kientzle
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer
10
* in this position and unchanged.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
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.
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).
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.
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).
46
#include "bsdtar_platform.h"
47
__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.4 2006/03/21 17:03:51 kientzle Exp $");
62
* 3) Arbitrary logical traversals by closing/reopening intermediate fds.
66
struct tree_entry *next;
67
struct tree_entry *parent;
69
size_t dirname_length;
76
/* Definitions for tree_entry.flags bitmap. */
77
#define isDir 1 /* This entry is a regular directory. */
78
#define isDirLink 2 /* This entry is a symbolic link to a directory. */
79
#define needsPreVisit 4 /* This entry needs to be previsited. */
80
#define needsPostVisit 8 /* This entry needs to be postvisited. */
83
* Local data for this package.
86
struct tree_entry *stack;
87
struct tree_entry *current;
92
int tree_errno; /* Error code from last failed operation. */
98
size_t dirname_length;
108
/* Definitions for tree.flags bitmap. */
109
#define needsReturn 8 /* Marks first entry as not having been returned yet. */
110
#define hasStat 16 /* The st entry is set. */
111
#define hasLstat 32 /* The lst entry is set. */
114
#ifdef HAVE_DIRENT_D_NAMLEN
115
/* BSD extension; avoids need for a strlen() call. */
116
#define D_NAMELEN(dp) (dp)->d_namlen
118
#define D_NAMELEN(dp) (strlen((dp)->d_name))
124
tree_dump(struct tree *t, FILE *out)
126
struct tree_entry *te;
128
fprintf(out, "\tdepth: %d\n", t->depth);
129
fprintf(out, "\tbuff: %s\n", t->buff);
130
fprintf(out, "\tpwd: "); fflush(stdout); system("pwd");
131
fprintf(out, "\taccess: %s\n", t->basename);
132
fprintf(out, "\tstack:\n");
133
for (te = t->stack; te != NULL; te = te->next) {
134
fprintf(out, "\t\tte->name: %s%s%s\n", te->name,
135
te->flags & needsPreVisit ? "" : " *",
136
t->current == te ? " (current)" : "");
142
* Add a directory path to the current stack.
145
tree_push(struct tree *t, const char *path)
147
struct tree_entry *te;
149
te = malloc(sizeof(*te));
150
memset(te, 0, sizeof(*te));
154
te->name = strdup(path);
155
te->flags = needsPreVisit | needsPostVisit;
156
te->dirname_length = t->dirname_length;
160
* Append a name to the current path.
163
tree_append(struct tree *t, const char *name, size_t name_length)
168
t->buff[t->dirname_length] = '\0';
169
/* Strip trailing '/' from name, unless entire name is "/". */
170
while (name_length > 1 && name[name_length - 1] == '/')
173
/* Resize pathname buffer as needed. */
174
while (name_length + 1 + t->dirname_length >= t->buff_length) {
176
if (t->buff_length < 1024)
177
t->buff_length = 1024;
178
t->buff = realloc(t->buff, t->buff_length);
180
p = t->buff + t->dirname_length;
181
t->path_length = t->dirname_length + name_length;
182
/* Add a separating '/' if it's needed. */
183
if (t->dirname_length > 0 && p[-1] != '/') {
187
strncpy(p, name, name_length);
188
p[name_length] = '\0';
193
* Open a directory tree for traversal.
196
tree_open(const char *path)
200
t = malloc(sizeof(*t));
201
memset(t, 0, sizeof(*t));
202
tree_append(t, path, strlen(path));
203
t->initialDirFd = open(".", O_RDONLY);
205
* During most of the traversal, items are set up and then
206
* returned immediately from tree_next(). That doesn't work
207
* for the very first entry, so we set a flag for this special
210
t->flags = needsReturn;
215
* We've finished a directory; ascend back to the parent.
218
tree_ascend(struct tree *t)
220
struct tree_entry *te;
224
if (te->flags & isDirLink) {
234
* Pop the working stack.
237
tree_pop(struct tree *t)
239
struct tree_entry *te;
241
t->buff[t->dirname_length] = '\0';
242
if (t->stack == t->current && t->current != NULL)
243
t->current = t->current->parent;
246
t->dirname_length = te->dirname_length;
247
t->basename = t->buff + t->dirname_length;
248
/* Special case: starting dir doesn't skip leading '/'. */
249
if (t->dirname_length > 0)
256
* Get the next item in the tree traversal.
259
tree_next(struct tree *t)
261
struct dirent *de = NULL;
263
/* Handle the startup case by returning the initial entry. */
264
if (t->flags & needsReturn) {
265
t->flags &= ~needsReturn;
266
return (t->visit_type = TREE_REGULAR);
269
while (t->stack != NULL) {
270
/* If there's an open dir, get the next entry from there. */
271
while (t->d != NULL) {
276
} else if (de->d_name[0] == '.'
277
&& de->d_name[1] == '\0') {
279
} else if (de->d_name[0] == '.'
280
&& de->d_name[1] == '.'
281
&& de->d_name[2] == '\0') {
285
* Append the path to the current path
288
tree_append(t, de->d_name, D_NAMELEN(de));
289
t->flags &= ~hasLstat;
290
t->flags &= ~hasStat;
291
return (t->visit_type = TREE_REGULAR);
295
/* If the current dir needs to be visited, set it up. */
296
if (t->stack->flags & needsPreVisit) {
297
t->current = t->stack;
298
tree_append(t, t->stack->name, strlen(t->stack->name));
299
t->stack->flags &= ~needsPreVisit;
300
/* If it is a link, set up fd for the ascent. */
301
if (t->stack->flags & isDirLink) {
302
t->stack->fd = open(".", O_RDONLY);
304
if (t->openCount > t->maxOpenCount)
305
t->maxOpenCount = t->openCount;
307
t->dirname_length = t->path_length;
308
if (chdir(t->stack->name) != 0) {
309
/* chdir() failed; return error */
311
t->tree_errno = errno;
312
return (t->visit_type = TREE_ERROR_DIR);
317
tree_ascend(t); /* Undo "chdir" */
319
t->tree_errno = errno;
320
return (t->visit_type = TREE_ERROR_DIR);
322
t->flags &= ~hasLstat;
323
t->flags &= ~hasStat;
325
return (t->visit_type = TREE_POSTDESCENT);
328
/* We've done everything necessary for the top stack entry. */
329
if (t->stack->flags & needsPostVisit) {
332
t->flags &= ~hasLstat;
333
t->flags &= ~hasStat;
334
return (t->visit_type = TREE_POSTASCENT);
337
return (t->visit_type = 0);
344
tree_errno(struct tree *t)
346
return (t->tree_errno);
350
* Called by the client to mark the directory just returned from
351
* tree_next() as needing to be visited.
354
tree_descend(struct tree *t)
356
if (t->visit_type != TREE_REGULAR)
359
if (tree_current_is_physical_dir(t)) {
360
tree_push(t, t->basename);
361
t->stack->flags |= isDir;
362
} else if (tree_current_is_dir(t)) {
363
tree_push(t, t->basename);
364
t->stack->flags |= isDirLink;
369
* Get the stat() data for the entry just returned from tree_next().
372
tree_current_stat(struct tree *t)
374
if (!(t->flags & hasStat)) {
375
if (stat(t->basename, &t->st) != 0)
383
* Get the lstat() data for the entry just returned from tree_next().
386
tree_current_lstat(struct tree *t)
388
if (!(t->flags & hasLstat)) {
389
if (lstat(t->basename, &t->lst) != 0)
391
t->flags |= hasLstat;
397
* Test whether current entry is a dir or dir link.
400
tree_current_is_dir(struct tree *t)
402
/* If we've already pulled stat(), just use that. */
403
if (t->flags & hasStat)
404
return (S_ISDIR(tree_current_stat(t)->st_mode));
406
/* If we've already pulled lstat(), we may be able to use that. */
407
if (t->flags & hasLstat) {
408
/* If lstat() says it's a dir, it must be a dir. */
409
if (S_ISDIR(tree_current_lstat(t)->st_mode))
411
/* If it's not a dir and not a link, we're done. */
412
if (!S_ISLNK(tree_current_lstat(t)->st_mode))
415
* If the above two tests fail, then it's a link, but
416
* we don't know whether it's a link to a dir or a
421
/* TODO: Use a more efficient mechanism when available. */
422
return (S_ISDIR(tree_current_stat(t)->st_mode));
426
* Test whether current entry is a physical directory.
429
tree_current_is_physical_dir(struct tree *t)
431
/* If we've already pulled lstat(), just use that. */
432
if (t->flags & hasLstat)
433
return (S_ISDIR(tree_current_lstat(t)->st_mode));
435
/* TODO: Use a more efficient mechanism when available. */
436
return (S_ISDIR(tree_current_lstat(t)->st_mode));
440
* Test whether current entry is a symbolic link.
443
tree_current_is_physical_link(struct tree *t)
445
/* If we've already pulled lstat(), just use that. */
446
if (t->flags & hasLstat)
447
return (S_ISLNK(tree_current_lstat(t)->st_mode));
449
/* TODO: Use a more efficient mechanism when available. */
450
return (S_ISLNK(tree_current_lstat(t)->st_mode));
454
* Return the access path for the entry just returned from tree_next().
457
tree_current_access_path(struct tree *t)
459
return (t->basename);
463
* Return the full path for the entry just returned from tree_next().
466
tree_current_path(struct tree *t)
472
* Return the length of the path for the entry just returned from tree_next().
475
tree_current_pathlen(struct tree *t)
477
return (t->path_length);
481
* Return the nesting depth of the entry just returned from tree_next().
484
tree_current_depth(struct tree *t)
490
* Terminate the traversal and release any resources.
493
tree_close(struct tree *t)
495
/* Release anything remaining in the stack. */
496
while (t->stack != NULL)
500
/* chdir() back to where we started. */
501
if (t->initialDirFd >= 0) {
502
fchdir(t->initialDirFd);
503
close(t->initialDirFd);
504
t->initialDirFd = -1;