Update libarchive, tar and cpio to version 2.8.5

The following additional vendor revisions are applied:

Revision 3740:
Use archive_clear_error() to clear the error markers.

Obtained from:	http://code.google.com/p/libarchive
MFC after:	2 weeks
This commit is contained in:
Martin Matuska 2011-12-20 20:06:33 +00:00
parent f873e4d1bd
commit f6ccfb42a2
23 changed files with 387 additions and 155 deletions

View File

@ -52,7 +52,7 @@
/* These should match the types used in 'struct stat' */
#if defined(_WIN32) && !defined(__CYGWIN__)
#define __LA_INT64_T __int64
# if defined(_SSIZE_T_DEFINED)
# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
# define __LA_SSIZE_T ssize_t
# elif defined(_WIN64)
# define __LA_SSIZE_T __int64
@ -98,6 +98,13 @@
# define __LA_DECL
#endif
#if defined(__GNUC__) && __GNUC__ >= 3
#define __LA_PRINTF(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#else
#define __LA_PRINTF(fmtarg, firstvararg) /* nothing */
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -129,13 +136,13 @@ extern "C" {
* (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
* #endif
*/
#define ARCHIVE_VERSION_NUMBER 2008004
#define ARCHIVE_VERSION_NUMBER 2008005
__LA_DECL int archive_version_number(void);
/*
* Textual name/version of the library, useful for version displays.
*/
#define ARCHIVE_VERSION_STRING "libarchive 2.8.4"
#define ARCHIVE_VERSION_STRING "libarchive 2.8.5"
__LA_DECL const char * archive_version_string(void);
#if ARCHIVE_VERSION_NUMBER < 3000000
@ -717,7 +724,7 @@ __LA_DECL const char *archive_format_name(struct archive *);
__LA_DECL int archive_format(struct archive *);
__LA_DECL void archive_clear_error(struct archive *);
__LA_DECL void archive_set_error(struct archive *, int _err,
const char *fmt, ...);
const char *fmt, ...) __LA_PRINTF(3, 4);
__LA_DECL void archive_copy_error(struct archive *dest,
struct archive *src);
__LA_DECL int archive_file_count(struct archive *);

View File

@ -715,7 +715,7 @@ archive_read_data_block(struct archive *_a,
/*
* Close the file and release most resources.
*
* Be careful: client might just call read_new and then read_finish.
* Be careful: client might just call read_new and then read_free.
* Don't assume we actually read anything or performed any non-trivial
* initialization.
*/

View File

@ -350,4 +350,4 @@ bzip2_filter_close(struct archive_read_filter *self)
return (ret);
}
#endif /* HAVE_BZLIB_H */
#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */

View File

@ -488,9 +488,9 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff)
switch (uudecode->state) {
default:
case ST_FIND_HEAD:
if (len - nl > 13 && memcmp(b, "begin ", 6) == 0)
if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
l = 6;
else if (len - nl > 18 &&
else if (len - nl >= 18 &&
memcmp(b, "begin-base64 ", 13) == 0)
l = 13;
else

View File

@ -264,9 +264,9 @@ archive_read_format_cpio_read_header(struct archive_read *a,
/* Compare name to "TRAILER!!!" to test for end-of-archive. */
if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
/* TODO: Store file location of start of block. */
archive_set_error(&a->archive, 0, NULL);
return (ARCHIVE_EOF);
/* TODO: Store file location of start of block. */
archive_clear_error(&a->archive);
return (ARCHIVE_EOF);
}
/* Detect and record hardlinks to previously-extracted entries. */

View File

@ -934,14 +934,14 @@ read_children(struct archive_read *a, struct file_info *parent)
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Ignoring out-of-order directory (%s) %jd > %jd",
parent->name.s,
iso9660->current_position,
parent->offset);
(intmax_t)iso9660->current_position,
(intmax_t)parent->offset);
return (ARCHIVE_WARN);
}
if (parent->offset + parent->size > iso9660->volume_size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Directory is beyond end-of-media: %s",
parent->name);
parent->name.s);
return (ARCHIVE_WARN);
}
if (iso9660->current_position < parent->offset) {
@ -1139,7 +1139,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
if (file->offset + file->size > iso9660->volume_size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"File is beyond end-of-media: %s", file->name);
"File is beyond end-of-media: %s", file->name.s);
iso9660->entry_bytes_remaining = 0;
iso9660->entry_sparse_offset = 0;
return (ARCHIVE_WARN);
@ -1198,10 +1198,10 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
if ((file->mode & AE_IFMT) != AE_IFDIR &&
file->offset < iso9660->current_position) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Ignoring out-of-order file @%x (%s) %jd < %jd",
file,
"Ignoring out-of-order file (%s) %jd < %jd",
iso9660->pathname.s,
file->offset, iso9660->current_position);
(intmax_t)file->offset,
(intmax_t)iso9660->current_position);
iso9660->entry_bytes_remaining = 0;
iso9660->entry_sparse_offset = 0;
return (ARCHIVE_WARN);
@ -1524,8 +1524,8 @@ archive_read_format_iso9660_read_data(struct archive_read *a,
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Ignoring out-of-order file (%s) %jd < %jd",
iso9660->pathname.s,
iso9660->entry_content->offset,
iso9660->current_position);
(intmax_t)iso9660->entry_content->offset,
(intmax_t)iso9660->current_position);
*buff = NULL;
*size = 0;
*offset = iso9660->entry_sparse_offset;
@ -1626,8 +1626,8 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
*/
if (location > 0 &&
(location + ((fsize + iso9660->logical_block_size -1)
/ iso9660->logical_block_size)) >
(unsigned int)iso9660->volume_block) {
/ iso9660->logical_block_size))
> (uint32_t)iso9660->volume_block) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Invalid location of extent of file");
return (NULL);

View File

@ -576,7 +576,7 @@ tar_read_header(struct archive_read *a, struct tar *tar,
h = __archive_read_ahead(a, 512, NULL);
if (h != NULL)
__archive_read_consume(a, 512);
archive_set_error(&a->archive, 0, NULL);
archive_clear_error(&a->archive);
if (a->archive.archive_format_name == NULL) {
a->archive.archive_format = ARCHIVE_FORMAT_TAR;
a->archive.archive_format_name = "tar";

View File

@ -211,7 +211,7 @@ archive_read_format_zip_bid(struct archive_read *a)
/* Get 4k of data beyond where we stopped. */
buff = __archive_read_ahead(a, offset + 4096,
&bytes_avail);
if (bytes_avail < offset + 1)
if (buff == NULL)
break;
p = (const char *)buff + offset;
while (p + 9 < (const char *)buff + bytes_avail) {

View File

@ -44,6 +44,8 @@
#include <wchar.h>
#endif
#include "archive.h"
/*
* Basic resizable/reusable string support a la Java's "StringBuffer."
*
@ -134,10 +136,11 @@ void __archive_string_free(struct archive_string *);
/* Like 'vsprintf', but resizes the underlying string as necessary. */
void __archive_string_vsprintf(struct archive_string *, const char *,
va_list);
va_list) __LA_PRINTF(2, 0);
#define archive_string_vsprintf __archive_string_vsprintf
void __archive_string_sprintf(struct archive_string *, const char *, ...);
void __archive_string_sprintf(struct archive_string *, const char *, ...)
__LA_PRINTF(2, 3);
#define archive_string_sprintf __archive_string_sprintf
/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.

View File

@ -1730,7 +1730,7 @@ create_dir(struct archive_write_disk *a, char *path)
if (unlink(path) != 0) {
archive_set_error(&a->archive, errno,
"Can't create directory '%s': "
"Conflicting file cannot be removed");
"Conflicting file cannot be removed", path);
return (ARCHIVE_FAILED);
}
} else if (errno != ENOENT && errno != ENOTDIR) {

View File

@ -132,9 +132,10 @@ static int
archive_compressor_xz_init_stream(struct archive_write *a,
struct private_data *state)
{
static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
int ret;
state->stream = (lzma_stream)LZMA_STREAM_INIT;
state->stream = lzma_stream_init_data;
state->stream.next_out = state->compressed;
state->stream.avail_out = state->compressed_buffer_size;
if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ)

View File

@ -440,7 +440,7 @@ archive_write_ar_finish_entry(struct archive_write *a)
if (ar->entry_padding != 1) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Padding wrong size: %d should be 1 or 0",
ar->entry_padding);
(int)ar->entry_padding);
return (ARCHIVE_WARN);
}

View File

@ -537,8 +537,7 @@ archive_write_shar_finish_entry(struct archive_write *a)
}
if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
archive_string_sprintf(&shar->work, "chflags %s ",
p, archive_entry_pathname(shar->entry));
archive_string_sprintf(&shar->work, "chflags %s ", p);
shar_quote(&shar->work,
archive_entry_pathname(shar->entry), 1);
archive_strcat(&shar->work, "\n");

View File

@ -168,7 +168,7 @@ archive_write_set_format_ustar(struct archive *_a)
/* Basic internal sanity test. */
if (sizeof(template_header) != 512) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header));
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", (int)sizeof(template_header));
return (ARCHIVE_FATAL);
}

View File

@ -68,6 +68,7 @@
#endif
#define HAVE_BSDXML_H 1
#define HAVE_BZLIB_H 1
#define HAVE_CHFLAGS 1
#define HAVE_CHOWN 1
#define HAVE_DECL_EXTATTR_NAMESPACE_USER 1

View File

@ -65,6 +65,7 @@ Later variants have extended this by either appropriating undefined
areas of the header record, extending the header to multiple records,
or by storing special entries that modify the interpretation of
subsequent entries.
.Pp
.Bl -tag -width indent
.It Cm gnutar
The

View File

@ -177,6 +177,8 @@ which provides a slightly more efficient interface.
You may prefer to use the higher-level
.Fn archive_read_data_skip ,
which reads and discards the data for this entry,
.Fn archive_read_data_to_buffer ,
which reads the data into an in-memory buffer,
.Fn archive_read_data_to_file ,
which copies the data to the provided file descriptor, or
.Fn archive_read_extract ,

View File

@ -52,7 +52,7 @@ TESTS= \
test_read_format_gtar_gz.c \
test_read_format_gtar_lzma.c \
test_read_format_gtar_sparse.c \
test_read_format_iso_gz.c \
test_read_format_iso_Z.c \
test_read_format_iso_multi_extent.c \
test_read_format_isorr_rr_moved.c \
test_read_format_isojoliet_bz2.c \

View File

@ -67,7 +67,7 @@ test_compat_zip_1(void)
#if ARCHIVE_VERSION_NUMBER < 2000000
archive_read_finish(a);
#else
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
#endif
}

View File

@ -47,7 +47,7 @@ test1(void)
ARCHIVE_COMPRESSION_COMPRESS);
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660);
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
static
@ -87,10 +87,10 @@ void test2(void)
ARCHIVE_COMPRESSION_COMPRESS);
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660);
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
DEFINE_TEST(test_read_format_iso_gz)
DEFINE_TEST(test_read_format_iso_Z)
{
test1();
test2();

View File

@ -3,7 +3,7 @@
.include <bsd.own.mk>
PROG= bsdcpio
BSDCPIO_VERSION_STRING=2.8.4
BSDCPIO_VERSION_STRING=2.8.5
SRCS= cpio.c cmdline.c

View File

@ -2,7 +2,7 @@
.include <bsd.own.mk>
PROG= bsdtar
BSDTAR_VERSION_STRING=2.8.4
BSDTAR_VERSION_STRING=2.8.5
SRCS= bsdtar.c \
cmdline.c \
getdate.c \

View File

@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
@ -66,6 +69,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
#include <windows.h>
#endif
#include "tree.h"
@ -76,27 +82,38 @@ __FBSDID("$FreeBSD$");
*/
struct tree_entry {
int depth;
struct tree_entry *next;
struct tree_entry *parent;
char *name;
size_t dirname_length;
dev_t dev;
ino_t ino;
int flags;
/* How to return back to the parent of a symlink. */
#ifdef HAVE_FCHDIR
int fd;
int symlink_parent_fd;
#elif defined(_WIN32) && !defined(__CYGWIN__)
char *fullpath;
char *symlink_parent_path;
#else
#error fchdir function required.
#endif
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. */
#define needsFirstVisit 4 /* This is an initial entry. */
#define needsDescent 8 /* This entry needs to be previsited. */
#define needsOpen 16 /* This is a directory that needs to be opened. */
#define needsAscent 32 /* This entry needs to be postvisited. */
/*
* On Windows, "first visit" is handled as a pattern to be handed to
* _findfirst(). This is consistent with Windows conventions that
* file patterns are handled within the application. On Posix,
* "first visit" is just returned to the client.
*/
/*
* Local data for this package.
@ -104,21 +121,28 @@ struct tree_entry {
struct tree {
struct tree_entry *stack;
struct tree_entry *current;
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
HANDLE d;
BY_HANDLE_FILE_INFORMATION fileInfo;
#define INVALID_DIR_HANDLE INVALID_HANDLE_VALUE
WIN32_FIND_DATA _findData;
WIN32_FIND_DATA *findData;
#else
DIR *d;
#ifdef HAVE_FCHDIR
int initialDirFd;
#elif defined(_WIN32) && !defined(__CYGWIN__)
char *initialDir;
#define INVALID_DIR_HANDLE NULL
struct dirent *de;
#endif
int flags;
int visit_type;
int tree_errno; /* Error code from last failed operation. */
/* Dynamically-sized buffer for holding path */
char *buff;
const char *basename;
size_t buff_length;
size_t path_length;
size_t dirname_length;
const char *basename; /* Last path element */
size_t dirname_length; /* Leading dir length */
size_t path_length; /* Total path length */
int depth;
int openCount;
@ -129,10 +153,17 @@ struct tree {
};
/* 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. */
#define hasStat 16 /* The st entry is valid. */
#define hasLstat 32 /* The lst entry is valid. */
#define hasFileInfo 64 /* The Windows fileInfo entry is valid. */
#if defined(_WIN32) && !defined(__CYGWIN__)
static int
tree_dir_next_windows(struct tree *t, const char *pattern);
#else
static int
tree_dir_next_posix(struct tree *t);
#endif
#ifdef HAVE_DIRENT_D_NAMLEN
/* BSD extension; avoids need for a strlen() call. */
@ -141,25 +172,32 @@ struct tree {
#define D_NAMELEN(dp) (strlen((dp)->d_name))
#endif
#if 0
#include <stdio.h>
void
tree_dump(struct tree *t, FILE *out)
{
char buff[300];
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, "\tpwd: %s\n", getcwd(buff, sizeof(buff)));
fprintf(out, "\tbasename: %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)" : "");
fprintf(out, "\t\t%s%d:\"%s\" %s%s%s%s%s%s\n",
t->current == te ? "*" : " ",
te->depth,
te->name,
te->flags & needsFirstVisit ? "V" : "",
te->flags & needsDescent ? "D" : "",
te->flags & needsOpen ? "O" : "",
te->flags & needsAscent ? "A" : "",
te->flags & isDirLink ? "L" : "",
(t->current == te && t->d) ? "+" : ""
);
}
}
#endif
/*
* Add a directory path to the current stack.
@ -172,24 +210,29 @@ tree_push(struct tree *t, const char *path)
te = malloc(sizeof(*te));
memset(te, 0, sizeof(*te));
te->next = t->stack;
te->parent = t->current;
if (te->parent)
te->depth = te->parent->depth + 1;
t->stack = te;
#ifdef HAVE_FCHDIR
te->fd = -1;
#elif defined(_WIN32) && !defined(__CYGWIN__)
te->fullpath = NULL;
#endif
te->symlink_parent_fd = -1;
te->name = strdup(path);
te->flags = needsPreVisit | needsPostVisit;
#elif defined(_WIN32) && !defined(__CYGWIN__)
te->symlink_parent_path = NULL;
te->name = strdup(path);
#endif
te->flags = needsDescent | needsOpen | needsAscent;
te->dirname_length = t->dirname_length;
}
/*
* Append a name to the current path.
* Append a name to the current dir path.
*/
static void
tree_append(struct tree *t, const char *name, size_t name_length)
{
char *p;
size_t size_needed;
if (t->buff != NULL)
t->buff[t->dirname_length] = '\0';
@ -198,12 +241,16 @@ tree_append(struct tree *t, const char *name, size_t name_length)
name_length--;
/* Resize pathname buffer as needed. */
while (name_length + 1 + t->dirname_length >= t->buff_length) {
t->buff_length *= 2;
size_needed = name_length + 1 + t->dirname_length;
if (t->buff_length < size_needed) {
if (t->buff_length < 1024)
t->buff_length = 1024;
while (t->buff_length < size_needed)
t->buff_length *= 2;
t->buff = realloc(t->buff, t->buff_length);
}
if (t->buff == NULL)
abort();
p = t->buff + t->dirname_length;
t->path_length = t->dirname_length + name_length;
/* Add a separating '/' if it's needed. */
@ -211,7 +258,11 @@ tree_append(struct tree *t, const char *name, size_t name_length)
*p++ = '/';
t->path_length ++;
}
#if HAVE_STRNCPY_S
strncpy_s(p, t->buff_length - (p - t->buff), name, name_length);
#else
strncpy(p, name, name_length);
#endif
p[name_length] = '\0';
t->basename = p;
}
@ -222,24 +273,55 @@ tree_append(struct tree *t, const char *name, size_t name_length)
struct tree *
tree_open(const char *path)
{
#ifdef HAVE_FCHDIR
struct tree *t;
t = malloc(sizeof(*t));
memset(t, 0, sizeof(*t));
tree_append(t, path, strlen(path));
#ifdef HAVE_FCHDIR
t->initialDirFd = open(".", O_RDONLY);
#elif defined(_WIN32) && !defined(__CYGWIN__)
t->initialDir = getcwd(NULL, 0);
#endif
/*
* 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;
/* First item is set up a lot like a symlink traversal. */
tree_push(t, path);
t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
t->stack->symlink_parent_fd = open(".", O_RDONLY);
t->openCount++;
t->d = INVALID_DIR_HANDLE;
return (t);
#elif defined(_WIN32) && !defined(__CYGWIN__)
struct tree *t;
char *cwd = _getcwd(NULL, 0);
char *pathname = strdup(path), *p, *base;
if (pathname == NULL)
abort();
for (p = pathname; *p != '\0'; ++p) {
if (*p == '\\')
*p = '/';
}
base = pathname;
t = malloc(sizeof(*t));
memset(t, 0, sizeof(*t));
/* First item is set up a lot like a symlink traversal. */
/* printf("Looking for wildcard in %s\n", path); */
/* TODO: wildcard detection here screws up on \\?\c:\ UNC names */
if (strchr(base, '*') || strchr(base, '?')) {
// It has a wildcard in it...
// Separate the last element.
p = strrchr(base, '/');
if (p != NULL) {
*p = '\0';
chdir(base);
tree_append(t, base, p - base);
t->dirname_length = t->path_length;
base = p + 1;
}
}
tree_push(t, base);
free(pathname);
t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
t->stack->symlink_parent_path = cwd;
t->d = INVALID_DIR_HANDLE;
return (t);
#endif
}
/*
@ -255,22 +337,26 @@ tree_ascend(struct tree *t)
t->depth--;
if (te->flags & isDirLink) {
#ifdef HAVE_FCHDIR
if (fchdir(te->fd) != 0) {
if (fchdir(te->symlink_parent_fd) != 0) {
t->tree_errno = errno;
r = TREE_ERROR_FATAL;
}
close(te->fd);
close(te->symlink_parent_fd);
#elif defined(_WIN32) && !defined(__CYGWIN__)
if (chdir(te->fullpath) != 0) {
if (SetCurrentDirectory(te->symlink_parent_path) == 0) {
t->tree_errno = errno;
r = TREE_ERROR_FATAL;
}
free(te->fullpath);
te->fullpath = NULL;
free(te->symlink_parent_path);
te->symlink_parent_path = NULL;
#endif
t->openCount--;
} else {
#if defined(_WIN32) && !defined(__CYGWIN__)
if (SetCurrentDirectory("..") == 0) {
#else
if (chdir("..") != 0) {
#endif
t->tree_errno = errno;
r = TREE_ERROR_FATAL;
}
@ -286,16 +372,18 @@ tree_pop(struct tree *t)
{
struct tree_entry *te;
t->buff[t->dirname_length] = '\0';
if (t->buff)
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++;
if (t->buff) {
t->basename = t->buff + t->dirname_length;
while (t->basename[0] == '/')
t->basename++;
}
free(te->name);
free(te);
}
@ -306,101 +394,179 @@ tree_pop(struct tree *t)
int
tree_next(struct tree *t)
{
struct dirent *de = NULL;
int r;
/* If we're called again after a fatal error, that's an API
* violation. Just crash now. */
if (t->visit_type == TREE_ERROR_FATAL) {
const char *msg = "Unable to continue traversing"
" directory hierarchy after a fatal error.";
write(2, msg, strlen(msg));
*(volatile int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */
exit(1); /* In case the SEGV didn't work. */
}
/* Handle the startup case by returning the initial entry. */
if (t->flags & needsReturn) {
t->flags &= ~needsReturn;
return (t->visit_type = TREE_REGULAR);
fprintf(stderr, "Unable to continue traversing"
" directory hierarchy after a fatal error.");
abort();
}
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 (t->d != INVALID_DIR_HANDLE) {
#if defined(_WIN32) && !defined(__CYGWIN__)
r = tree_dir_next_windows(t, NULL);
#else
r = tree_dir_next_posix(t);
#endif
if (r == 0)
continue;
return (r);
}
/* If the current dir needs to be visited, set it up. */
if (t->stack->flags & needsPreVisit) {
if (t->stack->flags & needsFirstVisit) {
#if defined(_WIN32) && !defined(__CYGWIN__)
char *d = t->stack->name;
t->stack->flags &= ~needsFirstVisit;
if (strchr(d, '*') || strchr(d, '?')) {
r = tree_dir_next_windows(t, d);
if (r == 0)
continue;
return (r);
}
// Not a pattern, handle it as-is...
#endif
/* Top stack item needs a regular visit. */
t->current = t->stack;
tree_append(t, t->stack->name, strlen(t->stack->name));
t->stack->flags &= ~needsPreVisit;
//t->dirname_length = t->path_length;
//tree_pop(t);
t->stack->flags &= ~needsFirstVisit;
return (t->visit_type = TREE_REGULAR);
} else if (t->stack->flags & needsDescent) {
/* Top stack item is dir to descend into. */
t->current = t->stack;
tree_append(t, t->stack->name, strlen(t->stack->name));
t->stack->flags &= ~needsDescent;
/* If it is a link, set up fd for the ascent. */
if (t->stack->flags & isDirLink) {
#ifdef HAVE_FCHDIR
t->stack->fd = open(".", O_RDONLY);
#elif defined(_WIN32) && !defined(__CYGWIN__)
t->stack->fullpath = getcwd(NULL, 0);
#endif
t->stack->symlink_parent_fd = open(".", O_RDONLY);
t->openCount++;
if (t->openCount > t->maxOpenCount)
t->maxOpenCount = t->openCount;
#elif defined(_WIN32) && !defined(__CYGWIN__)
t->stack->symlink_parent_path = _getcwd(NULL, 0);
#endif
}
t->dirname_length = t->path_length;
if (chdir(t->stack->name) != 0) {
#if defined(_WIN32) && !defined(__CYGWIN__)
if (t->path_length == 259 || !SetCurrentDirectory(t->stack->name) != 0)
#else
if (chdir(t->stack->name) != 0)
#endif
{
/* chdir() failed; return error */
tree_pop(t);
t->tree_errno = errno;
return (t->visit_type = TREE_ERROR_DIR);
}
t->depth++;
t->d = opendir(".");
if (t->d == NULL) {
return (t->visit_type = TREE_POSTDESCENT);
} else if (t->stack->flags & needsOpen) {
t->stack->flags &= ~needsOpen;
#if defined(_WIN32) && !defined(__CYGWIN__)
r = tree_dir_next_windows(t, "*");
#else
r = tree_dir_next_posix(t);
#endif
if (r == 0)
continue;
return (r);
} else if (t->stack->flags & needsAscent) {
/* Top stack item is dir and we're done with it. */
r = tree_ascend(t);
tree_pop(t);
t->visit_type = r != 0 ? r : TREE_POSTASCENT;
return (t->visit_type);
} else {
/* Top item on stack is dead. */
tree_pop(t);
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
}
}
return (t->visit_type = 0);
}
#if defined(_WIN32) && !defined(__CYGWIN__)
static int
tree_dir_next_windows(struct tree *t, const char *pattern)
{
const char *name;
size_t namelen;
int r;
for (;;) {
if (pattern != NULL) {
t->d = FindFirstFile(pattern, &t->_findData);
if (t->d == INVALID_DIR_HANDLE) {
r = tree_ascend(t); /* Undo "chdir" */
tree_pop(t);
t->tree_errno = errno;
t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
return (t->visit_type);
}
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
t->basename = ".";
return (t->visit_type = TREE_POSTDESCENT);
t->findData = &t->_findData;
pattern = NULL;
} else if (!FindNextFile(t->d, &t->_findData)) {
FindClose(t->d);
t->d = INVALID_DIR_HANDLE;
t->findData = NULL;
return (0);
}
name = t->findData->cFileName;
namelen = strlen(name);
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
if (name[0] == '.' && name[1] == '\0')
continue;
if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
continue;
tree_append(t, name, namelen);
return (t->visit_type = TREE_REGULAR);
}
}
#else
static int
tree_dir_next_posix(struct tree *t)
{
int r;
const char *name;
size_t namelen;
/* We've done everything necessary for the top stack entry. */
if (t->stack->flags & needsPostVisit) {
r = tree_ascend(t);
if (t->d == NULL) {
if ((t->d = opendir(".")) == NULL) {
r = tree_ascend(t); /* Undo "chdir" */
tree_pop(t);
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
t->visit_type = r != 0 ? r : TREE_POSTASCENT;
t->tree_errno = errno;
t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
return (t->visit_type);
}
}
return (t->visit_type = 0);
for (;;) {
t->de = readdir(t->d);
if (t->de == NULL) {
closedir(t->d);
t->d = INVALID_DIR_HANDLE;
return (0);
}
name = t->de->d_name;
namelen = D_NAMELEN(t->de);
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
if (name[0] == '.' && name[1] == '\0')
continue;
if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
continue;
tree_append(t, name, namelen);
return (t->visit_type = TREE_REGULAR);
}
}
#endif
/*
* Return error code.
@ -437,25 +603,51 @@ const struct stat *
tree_current_stat(struct tree *t)
{
if (!(t->flags & hasStat)) {
if (stat(t->basename, &t->st) != 0)
if (stat(tree_current_access_path(t), &t->st) != 0)
return NULL;
t->flags |= hasStat;
}
return (&t->st);
}
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
const BY_HANDLE_FILE_INFORMATION *
tree_current_file_information(struct tree *t)
{
if (!(t->flags & hasFileInfo)) {
HANDLE h = CreateFile(tree_current_access_path(t),
0, 0, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (h == INVALID_HANDLE_VALUE)
return NULL;
if (!GetFileInformationByHandle(h, &t->fileInfo)) {
CloseHandle(h);
return NULL;
}
CloseHandle(h);
t->flags |= hasFileInfo;
}
return (&t->fileInfo);
}
#endif
/*
* Get the lstat() data for the entry just returned from tree_next().
*/
const struct stat *
tree_current_lstat(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
return (tree_current_stat(t));
#else
if (!(t->flags & hasLstat)) {
if (lstat(t->basename, &t->lst) != 0)
if (lstat(tree_current_access_path(t), &t->lst) != 0)
return NULL;
t->flags |= hasLstat;
}
return (&t->lst);
#endif
}
/*
@ -464,8 +656,14 @@ tree_current_lstat(struct tree *t)
int
tree_current_is_dir(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
if (t->findData)
return (t->findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
if (tree_current_file_information(t))
return (t->fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
return (0);
#else
const struct stat *st;
/*
* If we already have lstat() info, then try some
* cheap tests to determine if this is a dir.
@ -490,6 +688,7 @@ tree_current_is_dir(struct tree *t)
return 0;
/* Use the definitive test. Hopefully this is cached. */
return (S_ISDIR(st->st_mode));
#endif
}
/*
@ -500,6 +699,11 @@ tree_current_is_dir(struct tree *t)
int
tree_current_is_physical_dir(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
if (tree_current_is_physical_link(t))
return (0);
return (tree_current_is_dir(t));
#else
const struct stat *st;
/*
@ -523,6 +727,7 @@ tree_current_is_physical_dir(struct tree *t)
return 0;
/* Use the definitive test. Hopefully this is cached. */
return (S_ISDIR(st->st_mode));
#endif
}
/*
@ -531,10 +736,21 @@ tree_current_is_physical_dir(struct tree *t)
int
tree_current_is_physical_link(struct tree *t)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
#ifndef IO_REPARSE_TAG_SYMLINK
/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
#endif
if (t->findData)
return ((t->findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
&& (t->findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK));
return (0);
#else
const struct stat *st = tree_current_lstat(t);
if (st == NULL)
return 0;
return (S_ISLNK(st->st_mode));
#endif
}
/*
@ -582,21 +798,23 @@ 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. */
free(t->buff);
/* TODO: Ensure that premature close() resets cwd */
#if 0
#ifdef HAVE_FCHDIR
if (t->initialDirFd >= 0) {
fchdir(t->initialDirFd);
int s = fchdir(t->initialDirFd);
(void)s; /* UNUSED */
close(t->initialDirFd);
t->initialDirFd = -1;
}
#elif defined(_WIN32) && !defined(__CYGWIN__)
if (t->initialDir != NULL) {
chdir(t->initialDir);
SetCurrentDir(t->initialDir);
free(t->initialDir);
t->initialDir = NULL;
}
#endif
#endif
free(t);
}