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:
mm 2011-12-20 20:06:33 +00:00
parent 3e7916ec1c
commit de78128d73
23 changed files with 387 additions and 155 deletions

View File

@ -52,7 +52,7 @@
/* These should match the types used in 'struct stat' */ /* These should match the types used in 'struct stat' */
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
#define __LA_INT64_T __int64 #define __LA_INT64_T __int64
# if defined(_SSIZE_T_DEFINED) # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
# define __LA_SSIZE_T ssize_t # define __LA_SSIZE_T ssize_t
# elif defined(_WIN64) # elif defined(_WIN64)
# define __LA_SSIZE_T __int64 # define __LA_SSIZE_T __int64
@ -98,6 +98,13 @@
# define __LA_DECL # define __LA_DECL
#endif #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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -129,13 +136,13 @@ extern "C" {
* (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
* #endif * #endif
*/ */
#define ARCHIVE_VERSION_NUMBER 2008004 #define ARCHIVE_VERSION_NUMBER 2008005
__LA_DECL int archive_version_number(void); __LA_DECL int archive_version_number(void);
/* /*
* Textual name/version of the library, useful for version displays. * 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); __LA_DECL const char * archive_version_string(void);
#if ARCHIVE_VERSION_NUMBER < 3000000 #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 int archive_format(struct archive *);
__LA_DECL void archive_clear_error(struct archive *); __LA_DECL void archive_clear_error(struct archive *);
__LA_DECL void archive_set_error(struct archive *, int _err, __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, __LA_DECL void archive_copy_error(struct archive *dest,
struct archive *src); struct archive *src);
__LA_DECL int archive_file_count(struct archive *); __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. * 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 * Don't assume we actually read anything or performed any non-trivial
* initialization. * initialization.
*/ */

View File

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

View File

@ -488,9 +488,9 @@ read_more:
switch (uudecode->state) { switch (uudecode->state) {
default: default:
case ST_FIND_HEAD: case ST_FIND_HEAD:
if (len - nl > 13 && memcmp(b, "begin ", 6) == 0) if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
l = 6; l = 6;
else if (len - nl > 18 && else if (len - nl >= 18 &&
memcmp(b, "begin-base64 ", 13) == 0) memcmp(b, "begin-base64 ", 13) == 0)
l = 13; l = 13;
else 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. */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */
if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) { if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
/* TODO: Store file location of start of block. */ /* TODO: Store file location of start of block. */
archive_set_error(&a->archive, 0, NULL); archive_clear_error(&a->archive);
return (ARCHIVE_EOF); return (ARCHIVE_EOF);
} }
/* Detect and record hardlinks to previously-extracted entries. */ /* 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, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Ignoring out-of-order directory (%s) %jd > %jd", "Ignoring out-of-order directory (%s) %jd > %jd",
parent->name.s, parent->name.s,
iso9660->current_position, (intmax_t)iso9660->current_position,
parent->offset); (intmax_t)parent->offset);
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
} }
if (parent->offset + parent->size > iso9660->volume_size) { if (parent->offset + parent->size > iso9660->volume_size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Directory is beyond end-of-media: %s", "Directory is beyond end-of-media: %s",
parent->name); parent->name.s);
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
} }
if (iso9660->current_position < parent->offset) { 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) { if (file->offset + file->size > iso9660->volume_size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 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_bytes_remaining = 0;
iso9660->entry_sparse_offset = 0; iso9660->entry_sparse_offset = 0;
return (ARCHIVE_WARN); return (ARCHIVE_WARN);
@ -1198,10 +1198,10 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
if ((file->mode & AE_IFMT) != AE_IFDIR && if ((file->mode & AE_IFMT) != AE_IFDIR &&
file->offset < iso9660->current_position) { file->offset < iso9660->current_position) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Ignoring out-of-order file @%x (%s) %jd < %jd", "Ignoring out-of-order file (%s) %jd < %jd",
file,
iso9660->pathname.s, iso9660->pathname.s,
file->offset, iso9660->current_position); (intmax_t)file->offset,
(intmax_t)iso9660->current_position);
iso9660->entry_bytes_remaining = 0; iso9660->entry_bytes_remaining = 0;
iso9660->entry_sparse_offset = 0; iso9660->entry_sparse_offset = 0;
return (ARCHIVE_WARN); 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, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Ignoring out-of-order file (%s) %jd < %jd", "Ignoring out-of-order file (%s) %jd < %jd",
iso9660->pathname.s, iso9660->pathname.s,
iso9660->entry_content->offset, (intmax_t)iso9660->entry_content->offset,
iso9660->current_position); (intmax_t)iso9660->current_position);
*buff = NULL; *buff = NULL;
*size = 0; *size = 0;
*offset = iso9660->entry_sparse_offset; *offset = iso9660->entry_sparse_offset;
@ -1626,8 +1626,8 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
*/ */
if (location > 0 && if (location > 0 &&
(location + ((fsize + iso9660->logical_block_size -1) (location + ((fsize + iso9660->logical_block_size -1)
/ iso9660->logical_block_size)) > / iso9660->logical_block_size))
(unsigned int)iso9660->volume_block) { > (uint32_t)iso9660->volume_block) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Invalid location of extent of file"); "Invalid location of extent of file");
return (NULL); 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); h = __archive_read_ahead(a, 512, NULL);
if (h != NULL) if (h != NULL)
__archive_read_consume(a, 512); __archive_read_consume(a, 512);
archive_set_error(&a->archive, 0, NULL); archive_clear_error(&a->archive);
if (a->archive.archive_format_name == NULL) { if (a->archive.archive_format_name == NULL) {
a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format = ARCHIVE_FORMAT_TAR;
a->archive.archive_format_name = "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. */ /* Get 4k of data beyond where we stopped. */
buff = __archive_read_ahead(a, offset + 4096, buff = __archive_read_ahead(a, offset + 4096,
&bytes_avail); &bytes_avail);
if (bytes_avail < offset + 1) if (buff == NULL)
break; break;
p = (const char *)buff + offset; p = (const char *)buff + offset;
while (p + 9 < (const char *)buff + bytes_avail) { while (p + 9 < (const char *)buff + bytes_avail) {

View File

@ -44,6 +44,8 @@
#include <wchar.h> #include <wchar.h>
#endif #endif
#include "archive.h"
/* /*
* Basic resizable/reusable string support a la Java's "StringBuffer." * 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. */ /* Like 'vsprintf', but resizes the underlying string as necessary. */
void __archive_string_vsprintf(struct archive_string *, const char *, void __archive_string_vsprintf(struct archive_string *, const char *,
va_list); va_list) __LA_PRINTF(2, 0);
#define archive_string_vsprintf __archive_string_vsprintf #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 #define archive_string_sprintf __archive_string_sprintf
/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it. /* 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) { if (unlink(path) != 0) {
archive_set_error(&a->archive, errno, archive_set_error(&a->archive, errno,
"Can't create directory '%s': " "Can't create directory '%s': "
"Conflicting file cannot be removed"); "Conflicting file cannot be removed", path);
return (ARCHIVE_FAILED); return (ARCHIVE_FAILED);
} }
} else if (errno != ENOENT && errno != ENOTDIR) { } else if (errno != ENOENT && errno != ENOTDIR) {

View File

@ -132,9 +132,10 @@ static int
archive_compressor_xz_init_stream(struct archive_write *a, archive_compressor_xz_init_stream(struct archive_write *a,
struct private_data *state) struct private_data *state)
{ {
static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
int ret; int ret;
state->stream = (lzma_stream)LZMA_STREAM_INIT; state->stream = lzma_stream_init_data;
state->stream.next_out = state->compressed; state->stream.next_out = state->compressed;
state->stream.avail_out = state->compressed_buffer_size; state->stream.avail_out = state->compressed_buffer_size;
if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ) 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) { if (ar->entry_padding != 1) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Padding wrong size: %d should be 1 or 0", "Padding wrong size: %d should be 1 or 0",
ar->entry_padding); (int)ar->entry_padding);
return (ARCHIVE_WARN); 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) { if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
archive_string_sprintf(&shar->work, "chflags %s ", archive_string_sprintf(&shar->work, "chflags %s ", p);
p, archive_entry_pathname(shar->entry));
shar_quote(&shar->work, shar_quote(&shar->work,
archive_entry_pathname(shar->entry), 1); archive_entry_pathname(shar->entry), 1);
archive_strcat(&shar->work, "\n"); archive_strcat(&shar->work, "\n");

View File

@ -168,7 +168,7 @@ archive_write_set_format_ustar(struct archive *_a)
/* Basic internal sanity test. */ /* Basic internal sanity test. */
if (sizeof(template_header) != 512) { 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); return (ARCHIVE_FATAL);
} }

View File

@ -68,6 +68,7 @@
#endif #endif
#define HAVE_BSDXML_H 1 #define HAVE_BSDXML_H 1
#define HAVE_BZLIB_H 1
#define HAVE_CHFLAGS 1 #define HAVE_CHFLAGS 1
#define HAVE_CHOWN 1 #define HAVE_CHOWN 1
#define HAVE_DECL_EXTATTR_NAMESPACE_USER 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, areas of the header record, extending the header to multiple records,
or by storing special entries that modify the interpretation of or by storing special entries that modify the interpretation of
subsequent entries. subsequent entries.
.Pp
.Bl -tag -width indent .Bl -tag -width indent
.It Cm gnutar .It Cm gnutar
The The

View File

@ -177,6 +177,8 @@ which provides a slightly more efficient interface.
You may prefer to use the higher-level You may prefer to use the higher-level
.Fn archive_read_data_skip , .Fn archive_read_data_skip ,
which reads and discards the data for this entry, 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 , .Fn archive_read_data_to_file ,
which copies the data to the provided file descriptor, or which copies the data to the provided file descriptor, or
.Fn archive_read_extract , .Fn archive_read_extract ,

View File

@ -52,7 +52,7 @@ TESTS= \
test_read_format_gtar_gz.c \ test_read_format_gtar_gz.c \
test_read_format_gtar_lzma.c \ test_read_format_gtar_lzma.c \
test_read_format_gtar_sparse.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_iso_multi_extent.c \
test_read_format_isorr_rr_moved.c \ test_read_format_isorr_rr_moved.c \
test_read_format_isojoliet_bz2.c \ test_read_format_isojoliet_bz2.c \

View File

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

View File

@ -47,7 +47,7 @@ test1(void)
ARCHIVE_COMPRESSION_COMPRESS); ARCHIVE_COMPRESSION_COMPRESS);
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660);
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a));
} }
static static
@ -87,10 +87,10 @@ void test2(void)
ARCHIVE_COMPRESSION_COMPRESS); ARCHIVE_COMPRESSION_COMPRESS);
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660);
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); 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(); test1();
test2(); test2();

View File

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

View File

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

View File

@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_STAT_H #ifdef HAVE_SYS_STAT_H
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#ifdef HAVE_DIRENT_H #ifdef HAVE_DIRENT_H
#include <dirent.h> #include <dirent.h>
#endif #endif
@ -66,6 +69,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
#include <windows.h>
#endif
#include "tree.h" #include "tree.h"
@ -76,27 +82,38 @@ __FBSDID("$FreeBSD$");
*/ */
struct tree_entry { struct tree_entry {
int depth;
struct tree_entry *next; struct tree_entry *next;
struct tree_entry *parent; struct tree_entry *parent;
char *name; char *name;
size_t dirname_length; size_t dirname_length;
dev_t dev; dev_t dev;
ino_t ino; ino_t ino;
int flags;
/* How to return back to the parent of a symlink. */
#ifdef HAVE_FCHDIR #ifdef HAVE_FCHDIR
int fd; int symlink_parent_fd;
#elif defined(_WIN32) && !defined(__CYGWIN__) #elif defined(_WIN32) && !defined(__CYGWIN__)
char *fullpath; char *symlink_parent_path;
#else #else
#error fchdir function required. #error fchdir function required.
#endif #endif
int flags;
}; };
/* Definitions for tree_entry.flags bitmap. */ /* Definitions for tree_entry.flags bitmap. */
#define isDir 1 /* This entry is a regular directory. */ #define isDir 1 /* This entry is a regular directory. */
#define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */
#define needsPreVisit 4 /* This entry needs to be previsited. */ #define needsFirstVisit 4 /* This is an initial entry. */
#define needsPostVisit 8 /* This entry needs to be postvisited. */ #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. * Local data for this package.
@ -104,21 +121,28 @@ struct tree_entry {
struct tree { struct tree {
struct tree_entry *stack; struct tree_entry *stack;
struct tree_entry *current; 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; DIR *d;
#ifdef HAVE_FCHDIR #define INVALID_DIR_HANDLE NULL
int initialDirFd; struct dirent *de;
#elif defined(_WIN32) && !defined(__CYGWIN__)
char *initialDir;
#endif #endif
int flags; int flags;
int visit_type; int visit_type;
int tree_errno; /* Error code from last failed operation. */ int tree_errno; /* Error code from last failed operation. */
/* Dynamically-sized buffer for holding path */
char *buff; char *buff;
const char *basename;
size_t buff_length; 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 depth;
int openCount; int openCount;
@ -129,10 +153,17 @@ struct tree {
}; };
/* Definitions for tree.flags bitmap. */ /* Definitions for tree.flags bitmap. */
#define needsReturn 8 /* Marks first entry as not having been returned yet. */ #define hasStat 16 /* The st entry is valid. */
#define hasStat 16 /* The st entry is set. */ #define hasLstat 32 /* The lst entry is valid. */
#define hasLstat 32 /* The lst entry is set. */ #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 #ifdef HAVE_DIRENT_D_NAMLEN
/* BSD extension; avoids need for a strlen() call. */ /* BSD extension; avoids need for a strlen() call. */
@ -141,25 +172,32 @@ struct tree {
#define D_NAMELEN(dp) (strlen((dp)->d_name)) #define D_NAMELEN(dp) (strlen((dp)->d_name))
#endif #endif
#if 0
#include <stdio.h> #include <stdio.h>
void void
tree_dump(struct tree *t, FILE *out) tree_dump(struct tree *t, FILE *out)
{ {
char buff[300];
struct tree_entry *te; struct tree_entry *te;
fprintf(out, "\tdepth: %d\n", t->depth); fprintf(out, "\tdepth: %d\n", t->depth);
fprintf(out, "\tbuff: %s\n", t->buff); fprintf(out, "\tbuff: %s\n", t->buff);
fprintf(out, "\tpwd: "); fflush(stdout); system("pwd"); fprintf(out, "\tpwd: %s\n", getcwd(buff, sizeof(buff)));
fprintf(out, "\taccess: %s\n", t->basename); fprintf(out, "\tbasename: %s\n", t->basename);
fprintf(out, "\tstack:\n"); fprintf(out, "\tstack:\n");
for (te = t->stack; te != NULL; te = te->next) { for (te = t->stack; te != NULL; te = te->next) {
fprintf(out, "\t\tte->name: %s%s%s\n", te->name, fprintf(out, "\t\t%s%d:\"%s\" %s%s%s%s%s%s\n",
te->flags & needsPreVisit ? "" : " *", t->current == te ? "*" : " ",
t->current == te ? " (current)" : ""); 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. * Add a directory path to the current stack.
@ -172,24 +210,29 @@ tree_push(struct tree *t, const char *path)
te = malloc(sizeof(*te)); te = malloc(sizeof(*te));
memset(te, 0, sizeof(*te)); memset(te, 0, sizeof(*te));
te->next = t->stack; te->next = t->stack;
te->parent = t->current;
if (te->parent)
te->depth = te->parent->depth + 1;
t->stack = te; t->stack = te;
#ifdef HAVE_FCHDIR #ifdef HAVE_FCHDIR
te->fd = -1; te->symlink_parent_fd = -1;
#elif defined(_WIN32) && !defined(__CYGWIN__)
te->fullpath = NULL;
#endif
te->name = strdup(path); 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; te->dirname_length = t->dirname_length;
} }
/* /*
* Append a name to the current path. * Append a name to the current dir path.
*/ */
static void static void
tree_append(struct tree *t, const char *name, size_t name_length) tree_append(struct tree *t, const char *name, size_t name_length)
{ {
char *p; char *p;
size_t size_needed;
if (t->buff != NULL) if (t->buff != NULL)
t->buff[t->dirname_length] = '\0'; t->buff[t->dirname_length] = '\0';
@ -198,12 +241,16 @@ tree_append(struct tree *t, const char *name, size_t name_length)
name_length--; name_length--;
/* Resize pathname buffer as needed. */ /* Resize pathname buffer as needed. */
while (name_length + 1 + t->dirname_length >= t->buff_length) { size_needed = name_length + 1 + t->dirname_length;
t->buff_length *= 2; if (t->buff_length < size_needed) {
if (t->buff_length < 1024) if (t->buff_length < 1024)
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); t->buff = realloc(t->buff, t->buff_length);
} }
if (t->buff == NULL)
abort();
p = t->buff + t->dirname_length; p = t->buff + t->dirname_length;
t->path_length = t->dirname_length + name_length; t->path_length = t->dirname_length + name_length;
/* Add a separating '/' if it's needed. */ /* Add a separating '/' if it's needed. */
@ -211,7 +258,11 @@ tree_append(struct tree *t, const char *name, size_t name_length)
*p++ = '/'; *p++ = '/';
t->path_length ++; 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); strncpy(p, name, name_length);
#endif
p[name_length] = '\0'; p[name_length] = '\0';
t->basename = p; t->basename = p;
} }
@ -222,24 +273,55 @@ tree_append(struct tree *t, const char *name, size_t name_length)
struct tree * struct tree *
tree_open(const char *path) tree_open(const char *path)
{ {
#ifdef HAVE_FCHDIR
struct tree *t; struct tree *t;
t = malloc(sizeof(*t)); t = malloc(sizeof(*t));
memset(t, 0, sizeof(*t)); memset(t, 0, sizeof(*t));
tree_append(t, path, strlen(path)); /* First item is set up a lot like a symlink traversal. */
#ifdef HAVE_FCHDIR tree_push(t, path);
t->initialDirFd = open(".", O_RDONLY); t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
#elif defined(_WIN32) && !defined(__CYGWIN__) t->stack->symlink_parent_fd = open(".", O_RDONLY);
t->initialDir = getcwd(NULL, 0); t->openCount++;
#endif t->d = INVALID_DIR_HANDLE;
/*
* 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); 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--; t->depth--;
if (te->flags & isDirLink) { if (te->flags & isDirLink) {
#ifdef HAVE_FCHDIR #ifdef HAVE_FCHDIR
if (fchdir(te->fd) != 0) { if (fchdir(te->symlink_parent_fd) != 0) {
t->tree_errno = errno; t->tree_errno = errno;
r = TREE_ERROR_FATAL; r = TREE_ERROR_FATAL;
} }
close(te->fd); close(te->symlink_parent_fd);
#elif defined(_WIN32) && !defined(__CYGWIN__) #elif defined(_WIN32) && !defined(__CYGWIN__)
if (chdir(te->fullpath) != 0) { if (SetCurrentDirectory(te->symlink_parent_path) == 0) {
t->tree_errno = errno; t->tree_errno = errno;
r = TREE_ERROR_FATAL; r = TREE_ERROR_FATAL;
} }
free(te->fullpath); free(te->symlink_parent_path);
te->fullpath = NULL; te->symlink_parent_path = NULL;
#endif #endif
t->openCount--; t->openCount--;
} else { } else {
#if defined(_WIN32) && !defined(__CYGWIN__)
if (SetCurrentDirectory("..") == 0) {
#else
if (chdir("..") != 0) { if (chdir("..") != 0) {
#endif
t->tree_errno = errno; t->tree_errno = errno;
r = TREE_ERROR_FATAL; r = TREE_ERROR_FATAL;
} }
@ -286,16 +372,18 @@ tree_pop(struct tree *t)
{ {
struct tree_entry *te; 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) if (t->stack == t->current && t->current != NULL)
t->current = t->current->parent; t->current = t->current->parent;
te = t->stack; te = t->stack;
t->stack = te->next; t->stack = te->next;
t->dirname_length = te->dirname_length; t->dirname_length = te->dirname_length;
t->basename = t->buff + t->dirname_length; if (t->buff) {
/* Special case: starting dir doesn't skip leading '/'. */ t->basename = t->buff + t->dirname_length;
if (t->dirname_length > 0) while (t->basename[0] == '/')
t->basename++; t->basename++;
}
free(te->name); free(te->name);
free(te); free(te);
} }
@ -306,101 +394,179 @@ tree_pop(struct tree *t)
int int
tree_next(struct tree *t) tree_next(struct tree *t)
{ {
struct dirent *de = NULL;
int r; int r;
/* If we're called again after a fatal error, that's an API /* If we're called again after a fatal error, that's an API
* violation. Just crash now. */ * violation. Just crash now. */
if (t->visit_type == TREE_ERROR_FATAL) { if (t->visit_type == TREE_ERROR_FATAL) {
const char *msg = "Unable to continue traversing" fprintf(stderr, "Unable to continue traversing"
" directory hierarchy after a fatal error."; " directory hierarchy after a fatal error.");
write(2, msg, strlen(msg)); abort();
*(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);
} }
while (t->stack != NULL) { while (t->stack != NULL) {
/* If there's an open dir, get the next entry from there. */ /* If there's an open dir, get the next entry from there. */
while (t->d != NULL) { if (t->d != INVALID_DIR_HANDLE) {
de = readdir(t->d); #if defined(_WIN32) && !defined(__CYGWIN__)
if (de == NULL) { r = tree_dir_next_windows(t, NULL);
closedir(t->d); #else
t->d = NULL; r = tree_dir_next_posix(t);
} else if (de->d_name[0] == '.' #endif
&& de->d_name[1] == '\0') { if (r == 0)
/* Skip '.' */ continue;
} else if (de->d_name[0] == '.' return (r);
&& 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 & needsFirstVisit) {
if (t->stack->flags & needsPreVisit) { #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; t->current = t->stack;
tree_append(t, t->stack->name, strlen(t->stack->name)); 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 it is a link, set up fd for the ascent. */
if (t->stack->flags & isDirLink) { if (t->stack->flags & isDirLink) {
#ifdef HAVE_FCHDIR #ifdef HAVE_FCHDIR
t->stack->fd = open(".", O_RDONLY); t->stack->symlink_parent_fd = open(".", O_RDONLY);
#elif defined(_WIN32) && !defined(__CYGWIN__)
t->stack->fullpath = getcwd(NULL, 0);
#endif
t->openCount++; t->openCount++;
if (t->openCount > t->maxOpenCount) if (t->openCount > t->maxOpenCount)
t->maxOpenCount = t->openCount; t->maxOpenCount = t->openCount;
#elif defined(_WIN32) && !defined(__CYGWIN__)
t->stack->symlink_parent_path = _getcwd(NULL, 0);
#endif
} }
t->dirname_length = t->path_length; 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 */ /* chdir() failed; return error */
tree_pop(t); tree_pop(t);
t->tree_errno = errno; t->tree_errno = errno;
return (t->visit_type = TREE_ERROR_DIR); return (t->visit_type = TREE_ERROR_DIR);
} }
t->depth++; t->depth++;
t->d = opendir("."); return (t->visit_type = TREE_POSTDESCENT);
if (t->d == NULL) { } 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" */ r = tree_ascend(t); /* Undo "chdir" */
tree_pop(t); tree_pop(t);
t->tree_errno = errno; t->tree_errno = errno;
t->visit_type = r != 0 ? r : TREE_ERROR_DIR; t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
return (t->visit_type); return (t->visit_type);
} }
t->flags &= ~hasLstat; t->findData = &t->_findData;
t->flags &= ~hasStat; pattern = NULL;
t->basename = "."; } else if (!FindNextFile(t->d, &t->_findData)) {
return (t->visit_type = TREE_POSTDESCENT); 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->d == NULL) {
if (t->stack->flags & needsPostVisit) { if ((t->d = opendir(".")) == NULL) {
r = tree_ascend(t); r = tree_ascend(t); /* Undo "chdir" */
tree_pop(t); tree_pop(t);
t->flags &= ~hasLstat; t->tree_errno = errno;
t->flags &= ~hasStat; t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
t->visit_type = r != 0 ? r : TREE_POSTASCENT;
return (t->visit_type); 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. * Return error code.
@ -437,25 +603,51 @@ const struct stat *
tree_current_stat(struct tree *t) tree_current_stat(struct tree *t)
{ {
if (!(t->flags & hasStat)) { if (!(t->flags & hasStat)) {
if (stat(t->basename, &t->st) != 0) if (stat(tree_current_access_path(t), &t->st) != 0)
return NULL; return NULL;
t->flags |= hasStat; t->flags |= hasStat;
} }
return (&t->st); 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(). * Get the lstat() data for the entry just returned from tree_next().
*/ */
const struct stat * const struct stat *
tree_current_lstat(struct tree *t) tree_current_lstat(struct tree *t)
{ {
#if defined(_WIN32) && !defined(__CYGWIN__)
return (tree_current_stat(t));
#else
if (!(t->flags & hasLstat)) { if (!(t->flags & hasLstat)) {
if (lstat(t->basename, &t->lst) != 0) if (lstat(tree_current_access_path(t), &t->lst) != 0)
return NULL; return NULL;
t->flags |= hasLstat; t->flags |= hasLstat;
} }
return (&t->lst); return (&t->lst);
#endif
} }
/* /*
@ -464,8 +656,14 @@ tree_current_lstat(struct tree *t)
int int
tree_current_is_dir(struct tree *t) 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; const struct stat *st;
/* /*
* If we already have lstat() info, then try some * If we already have lstat() info, then try some
* cheap tests to determine if this is a dir. * cheap tests to determine if this is a dir.
@ -490,6 +688,7 @@ tree_current_is_dir(struct tree *t)
return 0; return 0;
/* Use the definitive test. Hopefully this is cached. */ /* Use the definitive test. Hopefully this is cached. */
return (S_ISDIR(st->st_mode)); return (S_ISDIR(st->st_mode));
#endif
} }
/* /*
@ -500,6 +699,11 @@ tree_current_is_dir(struct tree *t)
int int
tree_current_is_physical_dir(struct tree *t) 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; const struct stat *st;
/* /*
@ -523,6 +727,7 @@ tree_current_is_physical_dir(struct tree *t)
return 0; return 0;
/* Use the definitive test. Hopefully this is cached. */ /* Use the definitive test. Hopefully this is cached. */
return (S_ISDIR(st->st_mode)); return (S_ISDIR(st->st_mode));
#endif
} }
/* /*
@ -531,10 +736,21 @@ tree_current_is_physical_dir(struct tree *t)
int int
tree_current_is_physical_link(struct tree *t) 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); const struct stat *st = tree_current_lstat(t);
if (st == NULL) if (st == NULL)
return 0; return 0;
return (S_ISLNK(st->st_mode)); return (S_ISLNK(st->st_mode));
#endif
} }
/* /*
@ -582,21 +798,23 @@ tree_close(struct tree *t)
/* Release anything remaining in the stack. */ /* Release anything remaining in the stack. */
while (t->stack != NULL) while (t->stack != NULL)
tree_pop(t); tree_pop(t);
if (t->buff) free(t->buff);
free(t->buff); /* TODO: Ensure that premature close() resets cwd */
/* chdir() back to where we started. */ #if 0
#ifdef HAVE_FCHDIR #ifdef HAVE_FCHDIR
if (t->initialDirFd >= 0) { if (t->initialDirFd >= 0) {
fchdir(t->initialDirFd); int s = fchdir(t->initialDirFd);
(void)s; /* UNUSED */
close(t->initialDirFd); close(t->initialDirFd);
t->initialDirFd = -1; t->initialDirFd = -1;
} }
#elif defined(_WIN32) && !defined(__CYGWIN__) #elif defined(_WIN32) && !defined(__CYGWIN__)
if (t->initialDir != NULL) { if (t->initialDir != NULL) {
chdir(t->initialDir); SetCurrentDir(t->initialDir);
free(t->initialDir); free(t->initialDir);
t->initialDir = NULL; t->initialDir = NULL;
} }
#endif
#endif #endif
free(t); free(t);
} }