bsdtar now uses the "tree" package instead of "fts" for walking
directory heirarchies.
This commit is contained in:
parent
2f28b97311
commit
22ede7639d
@ -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}
|
||||
|
@ -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
|
||||
|
1191
usr.bin/tar/fts.c
1191
usr.bin/tar/fts.c
File diff suppressed because it is too large
Load Diff
@ -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
506
usr.bin/tar/tree.c
Normal 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
116
usr.bin/tar/tree.h
Normal 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 *);
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user