bsdtar now uses the "tree" package instead of "fts" for walking

directory heirarchies.
This commit is contained in:
Tim Kientzle 2005-04-24 05:53:37 +00:00
parent 2f28b97311
commit 22ede7639d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=145471
7 changed files with 743 additions and 1518 deletions

View File

@ -6,8 +6,8 @@
#
PROG= bsdtar
VERSION= 1.01.023
DIST_SRCS= bsdtar.c getdate.y matching.c read.c util.c write.c
VERSION= 1.02.022
DIST_SRCS= bsdtar.c getdate.y matching.c read.c tree.c util.c write.c
SRCS= ${DIST_SRCS}
WARNS?= 5
DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
@ -25,7 +25,7 @@ DIST_FILES= ${DIST_SRCS}
DIST_FILES+= ${MAN}
DIST_FILES+= bsdtar.h bsdtar_platform.h
DIST_FILES+= Makefile.am
DIST_FILES+= fts.c fts.h
DIST_FILES+= tree.h
distfile:
rm -rf ${DIST_BUILD_DIR}

View File

@ -7,11 +7,11 @@ bsdtar_SOURCES= \
bsdtar.c \
bsdtar.h \
bsdtar_platform.h \
fts.c \
fts.h \
getdate.y \
matching.c \
read.c \
tree.c \
tree.h \
util.c \
write.c
bsdtar_LDADD= -larchive -lbz2 -lz

File diff suppressed because it is too large Load Diff

View File

@ -1,147 +0,0 @@
/*-
* This file is not used on BSD systems, because the libc version
* works. On Linux, the fts in libc is compiled for a 32-bit
* off_t, which doesn't match the 64-bit off_t used by the rest
* of bsdtar.
*
* The remainder of this file is an exact copy of:
* FreeBSD: src/include/fts.h,v 1.7 2002/09/21 01:28:36 wollman Exp
*/
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)fts.h 8.3 (Berkeley) 8/14/94
* $FreeBSD$
*/
#ifndef _FTS_H_
#define _FTS_H_
typedef struct {
struct _ftsent *fts_cur; /* current node */
struct _ftsent *fts_child; /* linked list of children */
struct _ftsent **fts_array; /* sort array */
dev_t fts_dev; /* starting device # */
char *fts_path; /* path for this descent */
int fts_rfd; /* fd for root */
int fts_pathlen; /* sizeof(path) */
int fts_nitems; /* elements in the sort array */
int (*fts_compar) /* compare function */
(const struct _ftsent * const *, const struct _ftsent * const *);
#define FTS_COMFOLLOW 0x001 /* follow command line symlinks */
#define FTS_LOGICAL 0x002 /* logical walk */
#define FTS_NOCHDIR 0x004 /* don't change directories */
#define FTS_NOSTAT 0x008 /* don't get stat info */
#define FTS_PHYSICAL 0x010 /* physical walk */
#define FTS_SEEDOT 0x020 /* return dot and dot-dot */
#define FTS_XDEV 0x040 /* don't cross devices */
#define FTS_WHITEOUT 0x080 /* return whiteout information */
#define FTS_OPTIONMASK 0x0ff /* valid user option mask */
#define FTS_NAMEONLY 0x100 /* (private) child names only */
#define FTS_STOP 0x200 /* (private) unrecoverable error */
int fts_options; /* fts_open options, global flags */
void *fts_clientptr; /* thunk for sort function */
} FTS;
typedef struct _ftsent {
struct _ftsent *fts_cycle; /* cycle node */
struct _ftsent *fts_parent; /* parent directory */
struct _ftsent *fts_link; /* next file in directory */
long fts_number; /* local numeric value */
void *fts_pointer; /* local address value */
char *fts_accpath; /* access path */
char *fts_path; /* root path */
int fts_errno; /* errno for this node */
int fts_symfd; /* fd for symlink */
u_short fts_pathlen; /* strlen(fts_path) */
u_short fts_namelen; /* strlen(fts_name) */
ino_t fts_ino; /* inode */
dev_t fts_dev; /* device */
nlink_t fts_nlink; /* link count */
#define FTS_ROOTPARENTLEVEL -1
#define FTS_ROOTLEVEL 0
short fts_level; /* depth (-1 to N) */
#define FTS_D 1 /* preorder directory */
#define FTS_DC 2 /* directory that causes cycles */
#define FTS_DEFAULT 3 /* none of the above */
#define FTS_DNR 4 /* unreadable directory */
#define FTS_DOT 5 /* dot or dot-dot */
#define FTS_DP 6 /* postorder directory */
#define FTS_ERR 7 /* error; errno is set */
#define FTS_F 8 /* regular file */
#define FTS_INIT 9 /* initialized only */
#define FTS_NS 10 /* stat(2) failed */
#define FTS_NSOK 11 /* no stat(2) requested */
#define FTS_SL 12 /* symbolic link */
#define FTS_SLNONE 13 /* symbolic link without target */
#define FTS_W 14 /* whiteout object */
u_short fts_info; /* user flags for FTSENT structure */
#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */
#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */
#define FTS_ISW 0x04 /* this is a whiteout object */
u_short fts_flags; /* private flags for FTSENT structure */
#define FTS_AGAIN 1 /* read node again */
#define FTS_FOLLOW 2 /* follow symbolic link */
#define FTS_NOINSTR 3 /* no instructions */
#define FTS_SKIP 4 /* discard node */
u_short fts_instr; /* fts_set() instructions */
struct stat *fts_statp; /* stat(2) information */
char *fts_name; /* file name */
FTS *fts_fts; /* back pointer to main FTS */
} FTSENT;
#include <sys/cdefs.h>
__BEGIN_DECLS
FTSENT *fts_children(FTS *, int);
int fts_close(FTS *);
void *fts_get_clientptr(FTS *);
#define fts_get_clientptr(fts) ((fts)->fts_clientptr)
FTS *fts_get_stream(FTSENT *);
#define fts_get_stream(ftsent) ((ftsent)->fts_fts)
FTS *fts_open(char * const *, int,
int (*)(const FTSENT * const *, const FTSENT * const *));
FTSENT *fts_read(FTS *);
int fts_set(FTS *, FTSENT *, int);
void fts_set_clientptr(FTS *, void *);
__END_DECLS
#endif /* !_FTS_H_ */

506
usr.bin/tar/tree.c Normal file
View File

@ -0,0 +1,506 @@
/*-
* Copyright (c) 2003-2004 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* This is a new directory-walking system that addresses a number
* of problems I've had with fts(3). In particular, it has no
* pathname-length limits (other than the size of 'int'), handles
* deep logical traversals, uses considerably less memory, and has
* an opaque interface (easier to modify in the future).
*
* Internally, it keeps a single list of "tree_entry" items that
* represent filesystem objects that require further attention.
* Non-directories are not kept in memory: they are pulled from
* readdir(), returned to the client, then freed as soon as possible.
* Any directory entry to be traversed gets pushed onto the stack.
*
* There is surprisingly little information that needs to be kept for
* each item on the stack. Just the name, depth (represented here as the
* string length of the parent directory's pathname), and some markers
* indicating how to get back to the parent (via chdir("..") for a
* regular dir or via fchdir(2) for a symlink).
*/
#include "bsdtar_platform.h"
__FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tree.h"
/*
* TODO:
* 1) Loop checking.
* 3) Arbitrary logical traversals by closing/reopening intermediate fds.
*/
struct tree_entry {
struct tree_entry *next;
struct tree_entry *parent;
char *name;
size_t dirname_length;
dev_t dev;
ino_t ino;
int fd;
int flags;
};
/* Definitions for tree_entry.flags bitmap. */
#define isDir 1 /* This entry is a regular directory. */
#define isDirLink 2 /* This entry is a symbolic link to a directory. */
#define needsPreVisit 4 /* This entry needs to be previsited. */
#define needsPostVisit 8 /* This entry needs to be postvisited. */
/*
* Local data for this package.
*/
struct tree {
struct tree_entry *stack;
struct tree_entry *current;
DIR *d;
int initialDirFd;
int flags;
int visit_type;
int tree_errno; /* Error code from last failed operation. */
char *buff;
const char *basename;
size_t buff_length;
size_t path_length;
size_t dirname_length;
int depth;
int openCount;
int maxOpenCount;
struct stat lst;
struct stat st;
};
/* Definitions for tree.flags bitmap. */
#define needsReturn 8 /* Marks first entry as not having been returned yet. */
#define hasStat 16 /* The st entry is set. */
#define hasLstat 32 /* The lst entry is set. */
#ifdef HAVE_DIRENT_D_NAMLEN
/* BSD extension; avoids need for a strlen() call. */
#define D_NAMELEN(dp) (dp)->d_namlen
#else
#define D_NAMELEN(dp) (strlen((dp)->d_name))
#endif
#if 0
#include <stdio.h>
void
tree_dump(struct tree *t, FILE *out)
{
struct tree_entry *te;
fprintf(out, "\tdepth: %d\n", t->depth);
fprintf(out, "\tbuff: %s\n", t->buff);
fprintf(out, "\tpwd: "); fflush(stdout); system("pwd");
fprintf(out, "\taccess: %s\n", t->basename);
fprintf(out, "\tstack:\n");
for(te = t->stack; te != NULL; te = te->next) {
fprintf(out, "\t\tte->name: %s%s%s\n", te->name,
te->flags & needsPreVisit ? "" : " *",
t->current == te ? " (current)" : "");
}
}
#endif
/*
* Add a directory path to the current stack.
*/
static void
tree_push(struct tree *t, const char *path)
{
struct tree_entry *te;
te = malloc(sizeof(*te));
memset(te, 0, sizeof(*te));
te->next = t->stack;
t->stack = te;
te->fd = -1;
te->name = strdup(path);
te->flags = needsPreVisit | needsPostVisit;
te->dirname_length = t->dirname_length;
}
/*
* Append a name to the current path.
*/
static void
tree_append(struct tree *t, const char *name, size_t name_length)
{
char *p;
if (t->buff != NULL)
t->buff[t->dirname_length] = '\0';
/* Strip trailing '/' from name, unless entire name is "/". */
while (name_length > 1 && name[name_length - 1] == '/')
name_length--;
/* Resize pathname buffer as needed. */
while (name_length + 1 + t->dirname_length >= t->buff_length) {
t->buff_length *= 2;
if (t->buff_length < 1024)
t->buff_length = 1024;
t->buff = realloc(t->buff, t->buff_length);
}
p = t->buff + t->dirname_length;
t->path_length = t->dirname_length + name_length;
/* Add a separating '/' if it's needed. */
if (t->dirname_length > 0 && p[-1] != '/') {
*p++ = '/';
t->path_length ++;
}
strncpy(p, name, name_length);
p[name_length] = '\0';
t->basename = p;
}
/*
* Open a directory tree for traversal.
*/
struct tree *
tree_open(const char *path)
{
struct tree *t;
t = malloc(sizeof(*t));
memset(t, 0, sizeof(*t));
tree_append(t, path, strlen(path));
t->initialDirFd = open(".", O_RDONLY);
/*
* During most of the traversal, items are set up and then
* returned immediately from tree_next(). That doesn't work
* for the very first entry, so we set a flag for this special
* case.
*/
t->flags = needsReturn;
return (t);
}
/*
* We've finished a directory; ascend back to the parent.
*/
static void
tree_ascend(struct tree *t)
{
struct tree_entry *te;
te = t->stack;
t->depth--;
if (te->flags & isDirLink) {
fchdir(te->fd);
close(te->fd);
t->openCount--;
} else {
chdir("..");
}
}
/*
* Pop the working stack.
*/
static void
tree_pop(struct tree *t)
{
struct tree_entry *te;
t->buff[t->dirname_length] = '\0';
if (t->stack == t->current && t->current != NULL)
t->current = t->current->parent;
te = t->stack;
t->stack = te->next;
t->dirname_length = te->dirname_length;
t->basename = t->buff + t->dirname_length;
/* Special case: starting dir doesn't skip leading '/'. */
if (t->dirname_length > 0)
t->basename++;
free(te->name);
free(te);
}
/*
* Get the next item in the tree traversal.
*/
int
tree_next(struct tree *t)
{
struct dirent *de = NULL;
/* Handle the startup case by returning the initial entry. */
if (t->flags & needsReturn) {
t->flags &= ~needsReturn;
return (t->visit_type = TREE_REGULAR);
}
while (t->stack != NULL) {
/* If there's an open dir, get the next entry from there. */
while (t->d != NULL) {
de = readdir(t->d);
if (de == NULL) {
closedir(t->d);
t->d = NULL;
} else if (de->d_name[0] == '.'
&& de->d_name[1] == '\0') {
/* Skip '.' */
} else if (de->d_name[0] == '.'
&& de->d_name[1] == '.'
&& de->d_name[2] == '\0') {
/* Skip '..' */
} else {
/*
* Append the path to the current path
* and return it.
*/
tree_append(t, de->d_name, D_NAMELEN(de));
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
return (t->visit_type = TREE_REGULAR);
}
}
/* If the current dir needs to be visited, set it up. */
if (t->stack->flags & needsPreVisit) {
t->current = t->stack;
tree_append(t, t->stack->name, strlen(t->stack->name));
t->stack->flags &= ~needsPreVisit;
/* If it is a link, set up fd for the ascent. */
if (t->stack->flags & isDirLink) {
t->stack->fd = open(".", O_RDONLY);
t->openCount++;
if (t->openCount > t->maxOpenCount)
t->maxOpenCount = t->openCount;
}
t->dirname_length = t->path_length;
if (chdir(t->stack->name) != 0) {
/* chdir() failed; return error */
tree_pop(t);
t->tree_errno = errno;
return (t->visit_type = TREE_ERROR);
}
t->d = opendir(".");
if (t->d == NULL) {
tree_pop(t);
t->tree_errno = errno;
return (t->visit_type = TREE_ERROR);
}
t->depth++;
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
t->basename = ".";
return (t->visit_type = TREE_POSTDESCENT);
}
/* We've done everything necessary for the top stack entry. */
if (t->stack->flags & needsPostVisit) {
tree_ascend(t);
tree_pop(t);
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
return (t->visit_type = TREE_POSTASCENT);
}
}
return (t->visit_type = 0);
}
/*
* Return error code.
*/
int
tree_errno(struct tree *t)
{
return (t->tree_errno);
}
/*
* Called by the client to mark the directory just returned from
* tree_next() as needing to be visited.
*/
void
tree_descend(struct tree *t)
{
if (t->visit_type != TREE_REGULAR)
return;
if (tree_current_is_physical_dir(t)) {
tree_push(t, t->basename);
t->stack->flags |= isDir;
} else if (tree_current_is_dir(t)) {
tree_push(t, t->basename);
t->stack->flags |= isDirLink;
}
}
/*
* Get the stat() data for the entry just returned from tree_next().
*/
const struct stat *
tree_current_stat(struct tree *t)
{
if (!(t->flags & hasStat)) {
if (stat(t->basename, &t->st) != 0)
return NULL;
t->flags |= hasStat;
}
return (&t->st);
}
/*
* Get the lstat() data for the entry just returned from tree_next().
*/
const struct stat *
tree_current_lstat(struct tree *t)
{
if (!(t->flags & hasLstat)) {
if (lstat(t->basename, &t->lst) != 0)
return NULL;
t->flags |= hasLstat;
}
return (&t->lst);
}
/*
* Test whether current entry is a dir or dir link.
*/
int
tree_current_is_dir(struct tree *t)
{
/* If we've already pulled stat(), just use that. */
if (t->flags & hasStat)
return (S_ISDIR(tree_current_stat(t)->st_mode));
/* If we've already pulled lstat(), we may be able to use that. */
if (t->flags & hasLstat) {
/* If lstat() says it's a dir, it must be a dir. */
if (S_ISDIR(tree_current_lstat(t)->st_mode))
return 1;
/* If it's not a dir and not a link, we're done. */
if (!S_ISLNK(tree_current_lstat(t)->st_mode))
return 0;
/*
* If the above two tests fail, then it's a link, but
* we don't know whether it's a link to a dir or a
* non-dir.
*/
}
/* TODO: Use a more efficient mechanism when available. */
return (S_ISDIR(tree_current_stat(t)->st_mode));
}
/*
* Test whether current entry is a physical directory.
*/
int
tree_current_is_physical_dir(struct tree *t)
{
/* If we've already pulled lstat(), just use that. */
if (t->flags & hasLstat)
return (S_ISDIR(tree_current_lstat(t)->st_mode));
/* TODO: Use a more efficient mechanism when available. */
return (S_ISDIR(tree_current_lstat(t)->st_mode));
}
/*
* Test whether current entry is a symbolic link.
*/
int
tree_current_is_physical_link(struct tree *t)
{
/* If we've already pulled lstat(), just use that. */
if (t->flags & hasLstat)
return (S_ISLNK(tree_current_lstat(t)->st_mode));
/* TODO: Use a more efficient mechanism when available. */
return (S_ISLNK(tree_current_lstat(t)->st_mode));
}
/*
* Return the access path for the entry just returned from tree_next().
*/
const char *
tree_current_access_path(struct tree *t)
{
return (t->basename);
}
/*
* Return the full path for the entry just returned from tree_next().
*/
const char *
tree_current_path(struct tree *t)
{
return (t->buff);
}
/*
* Return the length of the path for the entry just returned from tree_next().
*/
size_t
tree_current_pathlen(struct tree *t)
{
return (t->path_length);
}
/*
* Return the nesting depth of the entry just returned from tree_next().
*/
int
tree_current_depth(struct tree *t)
{
return (t->depth);
}
/*
* Terminate the traversal and release any resources.
*/
void
tree_close(struct tree *t)
{
/* Release anything remaining in the stack. */
while (t->stack != NULL)
tree_pop(t);
if (t->buff)
free(t->buff);
/* chdir() back to where we started. */
if (t->initialDirFd >= 0) {
fchdir(t->initialDirFd);
close(t->initialDirFd);
t->initialDirFd = -1;
}
free(t);
}

116
usr.bin/tar/tree.h Normal file
View File

@ -0,0 +1,116 @@
/*-
* Copyright (c) 2003-2004 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
/*-
* A set of routines for traversing directory trees.
* Similar in concept to the fts library, but with a few
* important differences:
* * Uses less memory. In particular, fts stores an entire directory
* in memory at a time. This package only keeps enough subdirectory
* information in memory to track the traversal. Information
* about non-directories is discarded as soon as possible.
* * Supports very deep logical traversals. The fts package
* uses "non-chdir" approach for logical traversals. This
* package does use a chdir approach for logical traversals
* and can therefore handle pathnames much longer than
* PATH_MAX.
* * Supports deep physical traversals "out of the box."
* Due to the memory optimizations above, there's no need to
* limit dir names to 32k.
*/
#include <sys/stat.h>
#include <stdio.h>
struct tree;
/* Initiate/terminate a tree traversal. */
struct tree *tree_open(const char * /* pathname */);
void tree_close(struct tree *);
/*
* tree_next() returns Zero if there is no next entry, non-zero if there is.
* Note that directories are potentially visited three times. The first
* time as "regular" file. If tree_descend() is invoked at that time,
* the directory is added to a work list and will be visited two more
* times: once just after descending into the directory and again
* just after ascending back to the parent.
*
* TREE_ERROR is returned if the descent failed (because the
* directory couldn't be opened, for instance). This is returned
* instead of TREE_PREVISIT/TREE_POSTVISIT.
*/
#define TREE_REGULAR 1
#define TREE_POSTDESCENT 2
#define TREE_POSTASCENT 3
#define TREE_ERROR -1
int tree_next(struct tree *);
int tree_errno(struct tree *);
/*
* Request that current entry be visited. If you invoke it on every
* directory, you'll get a physical traversal. This is ignored if the
* current entry isn't a directory or a link to a directory. So, if
* you invoke this on every returned path, you'll get a full logical
* traversal.
*/
void tree_descend(struct tree *);
/*
* Return information about the current entry.
*/
int tree_current_depth(struct tree *);
/*
* The current full pathname, length of the full pathname,
* and a name that can be used to access the file.
* Because tree does use chdir extensively, the access path is
* almost never the same as the full current path.
*/
const char *tree_current_path(struct tree *);
size_t tree_current_pathlen(struct tree *);
const char *tree_current_access_path(struct tree *);
/*
* Request the lstat() or stat() data for the current path. Since the
* tree package needs to do some of this anyway, and caches the
* results, you should take advantage of it here if you need it rather
* than make a redundant stat() or lstat() call of your own.
*/
const struct stat *tree_current_stat(struct tree *);
const struct stat *tree_current_lstat(struct tree *);
/* The following tests may use mechanisms much faster than stat()/lstat(). */
/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
int tree_current_is_physical_dir(struct tree *);
/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */
int tree_current_is_physical_link(struct tree *);
/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
int tree_current_is_dir(struct tree *);
/* For testing/debugging: Dump the internal status to the given filehandle. */
void tree_dump(struct tree *, FILE *);

View File

@ -37,7 +37,6 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <fts.h>
#include <grp.h>
#include <limits.h>
#include <pwd.h>
@ -51,6 +50,7 @@ __FBSDID("$FreeBSD$");
#endif
#include "bsdtar.h"
#include "tree.h"
/* Fixed size of uname/gname caches. */
#define name_cache_size 101
@ -514,205 +514,146 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, const char *filename)
static void
write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
{
FTS *fts;
FTSENT *ftsent;
int ftsoptions;
char *fts_argv[2];
struct tree *tree;
char symlink_mode = bsdtar->symlink_mode;
dev_t first_dev = 0;
int dev_recorded = 0;
int tree_ret;
#ifdef __linux
int fd, r;
unsigned long fflags;
#endif
/*
* Sigh: fts_open modifies it's first parameter, so we have to
* copy 'path' to mutable storage.
*/
fts_argv[0] = strdup(path);
if (fts_argv[0] == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM, "Can't open %s", path);
fts_argv[1] = NULL;
ftsoptions = FTS_PHYSICAL;
switch (bsdtar->symlink_mode) {
case 'H':
ftsoptions |= FTS_COMFOLLOW;
break;
case 'L':
ftsoptions = FTS_COMFOLLOW | FTS_LOGICAL;
break;
}
if (bsdtar->option_dont_traverse_mounts)
ftsoptions |= FTS_XDEV;
tree = tree_open(path);
fts = fts_open(fts_argv, ftsoptions, NULL);
if (!fts) {
if (!tree) {
bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path);
bsdtar->return_value = 1;
return;
}
while ((ftsent = fts_read(fts))) {
switch (ftsent->fts_info) {
case FTS_NS:
bsdtar_warnc(bsdtar, ftsent->fts_errno,
"%s: Could not stat", ftsent->fts_path);
while ((tree_ret = tree_next(tree))) {
const char *name = tree_current_path(tree);
const struct stat *st = NULL, *lst = NULL;
int descend;
if (tree_ret == TREE_ERROR)
bsdtar_warnc(bsdtar, errno, "%s", name);
if (tree_ret != TREE_REGULAR)
continue;
lst = tree_current_lstat(tree);
if (lst == NULL) {
/* Couldn't lstat(); must not exist. */
bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path);
bsdtar->return_value = 1;
break;
case FTS_ERR:
bsdtar_warnc(bsdtar, ftsent->fts_errno, "%s",
ftsent->fts_path);
bsdtar->return_value = 1;
break;
case FTS_DNR:
bsdtar_warnc(bsdtar, ftsent->fts_errno,
"%s: Cannot read directory contents",
ftsent->fts_path);
bsdtar->return_value = 1;
break;
case FTS_W: /* Skip Whiteout entries */
break;
case FTS_DC: /* Directory that causes cycle */
/* XXX Does this need special handling ? */
break;
case FTS_D:
/*
* If this dir is flagged "nodump" and we're
* honoring such flags, tell FTS to skip the
* entire tree and don't write the entry for the
* directory itself.
*/
continue;
}
if (S_ISLNK(lst->st_mode))
st = tree_current_stat(tree);
/* Default: descend into any dir or symlink to dir. */
/* We'll adjust this later on. */
descend = 0;
if ((st != NULL) && S_ISDIR(st->st_mode))
descend = 1;
if ((lst != NULL) && S_ISDIR(lst->st_mode))
descend = 1;
/*
* If user has asked us not to cross mount points,
* then don't descend into into a dir on a different
* device.
*/
if (!dev_recorded) {
first_dev = lst->st_dev;
dev_recorded = 1;
}
if (bsdtar->option_dont_traverse_mounts) {
if (lst != NULL && lst->st_dev != first_dev)
descend = 0;
}
/*
* If this file/dir is flagged "nodump" and we're
* honoring such flags, skip this file/dir.
*/
#ifdef HAVE_CHFLAGS
if (bsdtar->option_honor_nodump &&
(ftsent->fts_statp->st_flags & UF_NODUMP)) {
fts_set(fts, ftsent, FTS_SKIP);
break;
}
if (bsdtar->option_honor_nodump &&
(lst->st_flags & UF_NODUMP))
continue;
#endif
#ifdef __linux
/*
* Linux has a nodump flag too but to read it
* we have to open() the dir and do an ioctl on it...
*/
if (bsdtar->option_honor_nodump &&
((fd = open(ftsent->fts_name, O_RDONLY|O_NONBLOCK)) >= 0) &&
((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)),
close(fd), r) >= 0 &&
(fflags & EXT2_NODUMP_FL)) {
fts_set(fts, ftsent, FTS_SKIP);
break;
}
/*
* Linux has a nodump flag too but to read it
* we have to open() the file/dir and do an ioctl on it...
*/
if (bsdtar->option_honor_nodump &&
((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0) &&
((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)),
close(fd), r) >= 0 &&
(fflags & EXT2_NODUMP_FL))
continue;
#endif
/*
* In -u mode, we need to check whether this
* is newer than what's already in the archive.
* In all modes, we need to obey --newerXXX flags.
*/
if (!new_enough(bsdtar, ftsent->fts_path,
ftsent->fts_statp))
break;
/*
* If this dir is excluded by a filename
* pattern, tell FTS to skip the entire tree
* and don't write the entry for the directory
* itself.
*/
if (excluded(bsdtar, ftsent->fts_path)) {
fts_set(fts, ftsent, FTS_SKIP);
break;
}
/*
* If this file/dir is excluded by a filename
* pattern, skip it.
*/
if (excluded(bsdtar, name))
continue;
/*
* If the user vetoes the directory, skip
* the whole thing.
*/
if (bsdtar->option_interactive &&
!yes("add '%s'", ftsent->fts_path)) {
fts_set(fts, ftsent, FTS_SKIP);
break;
}
/*
* If the user vetoes this file/directory, skip it.
*/
if (bsdtar->option_interactive &&
!yes("add '%s'", name))
continue;
/*
* If we're not recursing, tell FTS to skip the
* tree but do fall through and write the entry
* for the dir itself.
*/
if (bsdtar->option_no_subdirs)
fts_set(fts, ftsent, FTS_SKIP);
write_entry(bsdtar, a, ftsent->fts_statp,
ftsent->fts_path, ftsent->fts_pathlen,
ftsent->fts_accpath);
/*
* If this is a dir, decide whether or not to recurse.
*/
if (bsdtar->option_no_subdirs)
descend = 0;
/*
* Distinguish 'L'/'P'/'H' symlink following.
*/
switch(symlink_mode) {
case 'H':
/* 'H': First item (from command line) like 'L'. */
lst = tree_current_stat(tree);
/* 'H': After the first item, rest like 'P'. */
symlink_mode = 'P';
break;
case FTS_F:
case FTS_SL:
case FTS_SLNONE:
case FTS_DEFAULT:
/*
* Skip this file if it's flagged "nodump" and we're
* honoring that flag.
*/
#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
if (bsdtar->option_honor_nodump &&
(ftsent->fts_statp->st_flags & UF_NODUMP))
break;
#endif
#ifdef __linux
/*
* Linux has a nodump flag too but to read it
* we have to open() the file and do an ioctl on it...
*/
if (bsdtar->option_honor_nodump &&
S_ISREG(ftsent->fts_statp->st_mode) &&
((fd = open(ftsent->fts_name, O_RDONLY|O_NONBLOCK)) >= 0) &&
((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)),
close(fd), r) >= 0 &&
(fflags & EXT2_NODUMP_FL))
break;
#endif
/*
* Skip this file if it's excluded by a
* filename pattern.
*/
if (excluded(bsdtar, ftsent->fts_path))
break;
/*
* In -u mode, we need to check whether this
* is newer than what's already in the archive.
*/
if (!new_enough(bsdtar, ftsent->fts_path,
ftsent->fts_statp))
break;
if (bsdtar->option_interactive &&
!yes("add '%s'", ftsent->fts_path)) {
break;
}
write_entry(bsdtar, a, ftsent->fts_statp,
ftsent->fts_path, ftsent->fts_pathlen,
ftsent->fts_accpath);
break;
case FTS_DP:
case 'L':
/* 'L': Do descend through a symlink to dir. */
/* 'L': Archive symlink to file as file. */
lst = tree_current_stat(tree);
break;
default:
bsdtar_warnc(bsdtar, 0,
"%s: Hierarchy traversal error %d\n",
ftsent->fts_path,
ftsent->fts_info);
/* 'P': Don't descend through a symlink to dir. */
if (!S_ISDIR(lst->st_mode))
descend = 0;
/* 'P': Archive symlink to file as symlink. */
/* lst = tree_current_lstat(tree); */
break;
}
if (descend)
tree_descend(tree);
/*
* In -u mode, we need to check whether this
* is newer than what's already in the archive.
* In all modes, we need to obey --newerXXX flags.
*/
if (new_enough(bsdtar, name, lst)) {
write_entry(bsdtar, a, lst, name,
tree_current_pathlen(tree),
tree_current_access_path(tree));
}
}
if (errno)
bsdtar_warnc(bsdtar, errno, "%s", path);
if (fts_close(fts))
bsdtar_warnc(bsdtar, errno, "fts_close failed");
free(fts_argv[0]);
tree_close(tree);
}
/*
@ -750,7 +691,7 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
/* Display entry as we process it. This format is required by SUSv2. */
if (bsdtar->verbose)
safe_fprintf(stderr, "a %s", pathname);
safe_fprintf(stderr, "a %s", archive_entry_pathname(entry));
/* Read symbolic link information. */
if ((st->st_mode & S_IFMT) == S_IFLNK) {
@ -784,7 +725,7 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
#ifdef __linux
if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) &&
((fd = open(accpath, O_RDONLY|O_NONBLOCK)) >= 0) &&
((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), r) >= 0 &&
((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), (fd = -1), r) >= 0 &&
stflags) {
archive_entry_set_fflags(entry, stflags, 0);
}