summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libarchive/archive.h15
-rw-r--r--lib/libarchive/archive_read.c2
-rw-r--r--lib/libarchive/archive_read_support_compression_bzip2.c2
-rw-r--r--lib/libarchive/archive_read_support_compression_uu.c4
-rw-r--r--lib/libarchive/archive_read_support_format_cpio.c6
-rw-r--r--lib/libarchive/archive_read_support_format_iso9660.c22
-rw-r--r--lib/libarchive/archive_read_support_format_tar.c2
-rw-r--r--lib/libarchive/archive_read_support_format_zip.c2
-rw-r--r--lib/libarchive/archive_string.h7
-rw-r--r--lib/libarchive/archive_write_disk.c2
-rw-r--r--lib/libarchive/archive_write_set_compression_xz.c3
-rw-r--r--lib/libarchive/archive_write_set_format_ar.c2
-rw-r--r--lib/libarchive/archive_write_set_format_shar.c3
-rw-r--r--lib/libarchive/archive_write_set_format_ustar.c2
-rw-r--r--lib/libarchive/config_freebsd.h1
-rw-r--r--lib/libarchive/libarchive-formats.51
-rw-r--r--lib/libarchive/libarchive.32
-rw-r--r--lib/libarchive/test/Makefile2
-rw-r--r--lib/libarchive/test/test_compat_zip.c2
-rw-r--r--lib/libarchive/test/test_read_format_iso_Z.c (renamed from lib/libarchive/test/test_read_format_iso_gz.c)6
-rw-r--r--usr.bin/cpio/Makefile2
-rw-r--r--usr.bin/tar/Makefile2
-rw-r--r--usr.bin/tar/tree.c446
23 files changed, 385 insertions, 153 deletions
diff --git a/lib/libarchive/archive.h b/lib/libarchive/archive.h
index 7075e26..bac93f0 100644
--- a/lib/libarchive/archive.h
+++ b/lib/libarchive/archive.h
@@ -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 *);
diff --git a/lib/libarchive/archive_read.c b/lib/libarchive/archive_read.c
index 7873d45..fb72407 100644
--- a/lib/libarchive/archive_read.c
+++ b/lib/libarchive/archive_read.c
@@ -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.
*/
diff --git a/lib/libarchive/archive_read_support_compression_bzip2.c b/lib/libarchive/archive_read_support_compression_bzip2.c
index fc419d4..891742f 100644
--- a/lib/libarchive/archive_read_support_compression_bzip2.c
+++ b/lib/libarchive/archive_read_support_compression_bzip2.c
@@ -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 */
diff --git a/lib/libarchive/archive_read_support_compression_uu.c b/lib/libarchive/archive_read_support_compression_uu.c
index cb1dae5..1b6a54c 100644
--- a/lib/libarchive/archive_read_support_compression_uu.c
+++ b/lib/libarchive/archive_read_support_compression_uu.c
@@ -488,9 +488,9 @@ read_more:
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
diff --git a/lib/libarchive/archive_read_support_format_cpio.c b/lib/libarchive/archive_read_support_format_cpio.c
index 24f3d35..790fa7a 100644
--- a/lib/libarchive/archive_read_support_format_cpio.c
+++ b/lib/libarchive/archive_read_support_format_cpio.c
@@ -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. */
diff --git a/lib/libarchive/archive_read_support_format_iso9660.c b/lib/libarchive/archive_read_support_format_iso9660.c
index 0216461..6d8d304 100644
--- a/lib/libarchive/archive_read_support_format_iso9660.c
+++ b/lib/libarchive/archive_read_support_format_iso9660.c
@@ -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);
diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c
index 20e1293..62be309 100644
--- a/lib/libarchive/archive_read_support_format_tar.c
+++ b/lib/libarchive/archive_read_support_format_tar.c
@@ -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";
diff --git a/lib/libarchive/archive_read_support_format_zip.c b/lib/libarchive/archive_read_support_format_zip.c
index 0fa1fa3..a02a2c2 100644
--- a/lib/libarchive/archive_read_support_format_zip.c
+++ b/lib/libarchive/archive_read_support_format_zip.c
@@ -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) {
diff --git a/lib/libarchive/archive_string.h b/lib/libarchive/archive_string.h
index 68fbf16..f0655d1 100644
--- a/lib/libarchive/archive_string.h
+++ b/lib/libarchive/archive_string.h
@@ -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.
diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c
index 8a4fd04..2319220 100644
--- a/lib/libarchive/archive_write_disk.c
+++ b/lib/libarchive/archive_write_disk.c
@@ -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) {
diff --git a/lib/libarchive/archive_write_set_compression_xz.c b/lib/libarchive/archive_write_set_compression_xz.c
index d97bca7..15b6cc2 100644
--- a/lib/libarchive/archive_write_set_compression_xz.c
+++ b/lib/libarchive/archive_write_set_compression_xz.c
@@ -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)
diff --git a/lib/libarchive/archive_write_set_format_ar.c b/lib/libarchive/archive_write_set_format_ar.c
index bffe07c..cb8ddaa 100644
--- a/lib/libarchive/archive_write_set_format_ar.c
+++ b/lib/libarchive/archive_write_set_format_ar.c
@@ -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);
}
diff --git a/lib/libarchive/archive_write_set_format_shar.c b/lib/libarchive/archive_write_set_format_shar.c
index 6cb58e8..bd62768 100644
--- a/lib/libarchive/archive_write_set_format_shar.c
+++ b/lib/libarchive/archive_write_set_format_shar.c
@@ -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");
diff --git a/lib/libarchive/archive_write_set_format_ustar.c b/lib/libarchive/archive_write_set_format_ustar.c
index 2de0cf1..54893d4 100644
--- a/lib/libarchive/archive_write_set_format_ustar.c
+++ b/lib/libarchive/archive_write_set_format_ustar.c
@@ -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);
}
diff --git a/lib/libarchive/config_freebsd.h b/lib/libarchive/config_freebsd.h
index a39a1fb..b4694ea1 100644
--- a/lib/libarchive/config_freebsd.h
+++ b/lib/libarchive/config_freebsd.h
@@ -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
diff --git a/lib/libarchive/libarchive-formats.5 b/lib/libarchive/libarchive-formats.5
index ef4b6ca..e6849b9 100644
--- a/lib/libarchive/libarchive-formats.5
+++ b/lib/libarchive/libarchive-formats.5
@@ -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
diff --git a/lib/libarchive/libarchive.3 b/lib/libarchive/libarchive.3
index 115a79c..70059dc 100644
--- a/lib/libarchive/libarchive.3
+++ b/lib/libarchive/libarchive.3
@@ -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 ,
diff --git a/lib/libarchive/test/Makefile b/lib/libarchive/test/Makefile
index ba72db7..e05ff1f 100644
--- a/lib/libarchive/test/Makefile
+++ b/lib/libarchive/test/Makefile
@@ -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 \
diff --git a/lib/libarchive/test/test_compat_zip.c b/lib/libarchive/test/test_compat_zip.c
index adb377c..7c1bde7 100644
--- a/lib/libarchive/test/test_compat_zip.c
+++ b/lib/libarchive/test/test_compat_zip.c
@@ -67,7 +67,7 @@ finish:
#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
}
diff --git a/lib/libarchive/test/test_read_format_iso_gz.c b/lib/libarchive/test/test_read_format_iso_Z.c
index d0d6b3c..2d60d41 100644
--- a/lib/libarchive/test/test_read_format_iso_gz.c
+++ b/lib/libarchive/test/test_read_format_iso_Z.c
@@ -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();
diff --git a/usr.bin/cpio/Makefile b/usr.bin/cpio/Makefile
index 4c2bddb..fb40ef9 100644
--- a/usr.bin/cpio/Makefile
+++ b/usr.bin/cpio/Makefile
@@ -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
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile
index 3cd4ec3..af1fe25 100644
--- a/usr.bin/tar/Makefile
+++ b/usr.bin/tar/Makefile
@@ -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 \
diff --git a/usr.bin/tar/tree.c b/usr.bin/tar/tree.c
index 27cbd71..a94f809 100644
--- a/usr.bin/tar/tree.c
+++ b/usr.bin/tar/tree.c
@@ -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;
+ te->symlink_parent_fd = -1;
+ te->name = strdup(path);
#elif defined(_WIN32) && !defined(__CYGWIN__)
- te->fullpath = NULL;
-#endif
+ te->symlink_parent_path = NULL;
te->name = strdup(path);
- te->flags = needsPreVisit | needsPostVisit;
+#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);
+ /* 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__)
- 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;
+ 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->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 &= ~needsPreVisit;
+ 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);
}
OpenPOWER on IntegriCloud