[Midnightbsd-cvs] src: usr.bin/tar:
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Fri Mar 27 15:51:49 EDT 2009
Log Message:
-----------
Modified Files:
--------------
src/usr.bin/tar:
COPYING (r1.3 -> r1.4)
Makefile (r1.3 -> r1.4)
bsdtar.1 (r1.3 -> r1.4)
bsdtar.c (r1.3 -> r1.4)
bsdtar.h (r1.3 -> r1.4)
bsdtar_platform.h (r1.3 -> r1.4)
config_midnightbsd.h (r1.1 -> r1.2)
matching.c (r1.3 -> r1.4)
read.c (r1.3 -> r1.4)
tree.c (r1.3 -> r1.4)
util.c (r1.3 -> r1.4)
write.c (r1.3 -> r1.4)
Added Files:
-----------
src/usr.bin/tar:
siginfo.c (r1.1)
subst.c (r1.1)
src/usr.bin/tar/test:
Makefile (r1.1)
main.c (r1.1)
test.h (r1.1)
test_0.c (r1.1)
test_basic.c (r1.1)
test_copy.c (r1.1)
test_getdate.c (r1.1)
test_help.c (r1.1)
test_option_T.c (r1.1)
test_option_q.c (r1.1)
test_patterns.c (r1.1)
test_patterns_2.tgz.uu (r1.1)
test_patterns_3.tgz.uu (r1.1)
test_stdio.c (r1.1)
test_strip_components.c (r1.1)
test_symlink_dir.c (r1.1)
test_version.c (r1.1)
-------------- next part --------------
Index: util.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/util.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/util.c -L usr.bin/tar/util.c -u -r1.3 -r1.4
--- usr.bin/tar/util.c
+++ usr.bin/tar/util.c
@@ -24,8 +24,7 @@
*/
#include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.17 2007/04/18 04:36:11 kientzle Exp $");
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.17.2.3 2008/12/11 05:56:47 kientzle Exp $");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
@@ -52,6 +51,7 @@
static void bsdtar_vwarnc(struct bsdtar *, int code,
const char *fmt, va_list ap);
+static const char *strip_components(const char *path, int elements);
/*
* Print a string, taking care with any non-printable characters.
@@ -179,7 +179,7 @@
fprintf(stderr, " (y/N)? ");
fflush(stderr);
- l = read(2, buff, sizeof(buff));
+ l = read(2, buff, sizeof(buff) - 1);
if (l <= 0)
return (0);
buff[l] = 0;
@@ -200,56 +200,6 @@
return (0);
}
-void
-bsdtar_strmode(struct archive_entry *entry, char *bp)
-{
- static const char *perms = "?rwxrwxrwx ";
- static const mode_t permbits[] =
- { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP,
- S_IROTH, S_IWOTH, S_IXOTH };
- mode_t mode;
- int i;
-
- /* Fill in a default string, then selectively override. */
- strcpy(bp, perms);
-
- mode = archive_entry_mode(entry);
- switch (mode & S_IFMT) {
- case S_IFREG: bp[0] = '-'; break;
- case S_IFBLK: bp[0] = 'b'; break;
- case S_IFCHR: bp[0] = 'c'; break;
- case S_IFDIR: bp[0] = 'd'; break;
- case S_IFLNK: bp[0] = 'l'; break;
- case S_IFSOCK: bp[0] = 's'; break;
-#ifdef S_IFIFO
- case S_IFIFO: bp[0] = 'p'; break;
-#endif
-#ifdef S_IFWHT
- case S_IFWHT: bp[0] = 'w'; break;
-#endif
- }
-
- for (i = 0; i < 9; i++)
- if (!(mode & permbits[i]))
- bp[i+1] = '-';
-
- if (mode & S_ISUID) {
- if (mode & S_IXUSR) bp[3] = 's';
- else bp[3] = 'S';
- }
- if (mode & S_ISGID) {
- if (mode & S_IXGRP) bp[6] = 's';
- else bp[6] = 'S';
- }
- if (mode & S_ISVTX) {
- if (mode & S_IXOTH) bp[9] = 't';
- else bp[9] = 'T';
- }
- if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
- bp[10] = '+';
-}
-
-
/*
* Read lines from file and do something with each one. If option_null
* is set, lines are terminated with zero bytes; otherwise, they're
@@ -266,7 +216,7 @@
{
FILE *f;
char *buff, *buff_end, *line_start, *line_end, *p;
- size_t buff_length, bytes_read, bytes_wanted;
+ size_t buff_length, new_buff_length, bytes_read, bytes_wanted;
int separator;
int ret;
@@ -313,7 +263,12 @@
line_start = buff;
} else {
/* Line is too big; enlarge the buffer. */
- p = realloc(buff, buff_length *= 2);
+ new_buff_length = buff_length * 2;
+ if (new_buff_length <= buff_length)
+ bsdtar_errc(bsdtar, 1, ENOMEM,
+ "Line too long in %s", pathname);
+ buff_length = new_buff_length;
+ p = realloc(buff, buff_length);
if (p == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM,
"Line too long in %s", pathname);
@@ -392,6 +347,31 @@
bsdtar->pending_chdir = NULL;
}
+const char *
+strip_components(const char *path, int elements)
+{
+ const char *p = path;
+
+ while (elements > 0) {
+ switch (*p++) {
+ case '/':
+ elements--;
+ path = p;
+ break;
+ case '\0':
+ /* Path is too short, skip it. */
+ return (NULL);
+ }
+ }
+
+ while (*path == '/')
+ ++path;
+ if (*path == '\0')
+ return (NULL);
+
+ return (path);
+}
+
/*
* Handle --strip-components and any future path-rewriting options.
* Returns non-zero if the pathname should not be extracted.
@@ -402,22 +382,65 @@
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
{
const char *name = archive_entry_pathname(entry);
+#if HAVE_REGEX_H
+ char *subst_name;
+#endif
+ int r;
+
+#if HAVE_REGEX_H
+ r = apply_substitution(bsdtar, name, &subst_name, 0);
+ if (r == -1) {
+ bsdtar_warnc(bsdtar, 0, "Invalid substituion, skipping entry");
+ return 1;
+ }
+ if (r == 1) {
+ archive_entry_copy_pathname(entry, subst_name);
+ if (*subst_name == '\0') {
+ free(subst_name);
+ return -1;
+ } else
+ free(subst_name);
+ name = archive_entry_pathname(entry);
+ }
+
+ if (archive_entry_hardlink(entry)) {
+ r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 1);
+ if (r == -1) {
+ bsdtar_warnc(bsdtar, 0, "Invalid substituion, skipping entry");
+ return 1;
+ }
+ if (r == 1) {
+ archive_entry_copy_hardlink(entry, subst_name);
+ free(subst_name);
+ }
+ }
+ if (archive_entry_symlink(entry) != NULL) {
+ r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1);
+ if (r == -1) {
+ bsdtar_warnc(bsdtar, 0, "Invalid substituion, skipping entry");
+ return 1;
+ }
+ if (r == 1) {
+ archive_entry_copy_symlink(entry, subst_name);
+ free(subst_name);
+ }
+ }
+#endif
/* Strip leading dir names as per --strip-components option. */
if (bsdtar->strip_components > 0) {
- int r = bsdtar->strip_components;
- const char *p = name;
+ const char *linkname = archive_entry_hardlink(entry);
+
+ name = strip_components(name, bsdtar->strip_components);
+ if (name == NULL)
+ return (1);
- while (r > 0) {
- switch (*p++) {
- case '/':
- r--;
- name = p;
- break;
- case '\0':
- /* Path is too short, skip it. */
+ if (linkname != NULL) {
+ linkname = strip_components(linkname,
+ bsdtar->strip_components);
+ if (linkname == NULL)
return (1);
- }
+ archive_entry_copy_hardlink(entry, linkname);
}
}
--- /dev/null
+++ usr.bin/tar/siginfo.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright 2008 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bsdtar_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/siginfo.c,v 1.2.2.1 2008/08/10 07:07:00 kientzle Exp $");
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bsdtar.h"
+
+/* Is there a pending SIGINFO or SIGUSR1? */
+static volatile sig_atomic_t siginfo_received = 0;
+
+struct siginfo_data {
+ /* What sort of operation are we doing? */
+ char * oper;
+
+ /* What path are we handling? */
+ char * path;
+
+ /* How large is the archive entry? */
+ int64_t size;
+
+ /* Old signal handlers. */
+#ifdef SIGINFO
+ void (*siginfo_old)(int);
+#endif
+ void (*sigusr1_old)(int);
+};
+
+static void siginfo_handler(int sig);
+
+/* Handler for SIGINFO / SIGUSR1. */
+static void
+siginfo_handler(int sig)
+{
+
+ (void)sig; /* UNUSED */
+
+ /* Record that SIGINFO or SIGUSR1 has been received. */
+ siginfo_received = 1;
+}
+
+void
+siginfo_init(struct bsdtar *bsdtar)
+{
+
+ /* Allocate space for internal structure. */
+ if ((bsdtar->siginfo = malloc(sizeof(struct siginfo_data))) == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "malloc failed");
+
+ /* Set the strings to NULL so that free() is safe. */
+ bsdtar->siginfo->path = bsdtar->siginfo->oper = NULL;
+
+#ifdef SIGINFO
+ /* We want to catch SIGINFO, if it exists. */
+ bsdtar->siginfo->siginfo_old = signal(SIGINFO, siginfo_handler);
+#endif
+ /* ... and treat SIGUSR1 the same way as SIGINFO. */
+ bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler);
+}
+
+void
+siginfo_setinfo(struct bsdtar *bsdtar, const char * oper, const char * path,
+ int64_t size)
+{
+
+ /* Free old operation and path strings. */
+ free(bsdtar->siginfo->oper);
+ free(bsdtar->siginfo->path);
+
+ /* Duplicate strings and store entry size. */
+ if ((bsdtar->siginfo->oper = strdup(oper)) == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Cannot strdup");
+ if ((bsdtar->siginfo->path = strdup(path)) == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Cannot strdup");
+ bsdtar->siginfo->size = size;
+}
+
+void
+siginfo_printinfo(struct bsdtar *bsdtar, off_t progress)
+{
+
+ /* If there's a signal to handle and we know what we're doing... */
+ if ((siginfo_received == 1) &&
+ (bsdtar->siginfo->path != NULL) &&
+ (bsdtar->siginfo->oper != NULL)) {
+ if (bsdtar->verbose)
+ fprintf(stderr, "\n");
+ if (bsdtar->siginfo->size > 0) {
+ safe_fprintf(stderr, "%s %s (%ju / %" PRId64 ")",
+ bsdtar->siginfo->oper, bsdtar->siginfo->path,
+ (uintmax_t)progress, bsdtar->siginfo->size);
+ } else {
+ safe_fprintf(stderr, "%s %s",
+ bsdtar->siginfo->oper, bsdtar->siginfo->path);
+ }
+ if (!bsdtar->verbose)
+ fprintf(stderr, "\n");
+ siginfo_received = 0;
+ }
+}
+
+void
+siginfo_done(struct bsdtar *bsdtar)
+{
+
+#ifdef SIGINFO
+ /* Restore old SIGINFO handler. */
+ signal(SIGINFO, bsdtar->siginfo->siginfo_old);
+#endif
+ /* And the old SIGUSR1 handler, too. */
+ signal(SIGUSR1, bsdtar->siginfo->sigusr1_old);
+
+ /* Free strings. */
+ free(bsdtar->siginfo->path);
+ free(bsdtar->siginfo->oper);
+
+ /* Free internal data structure. */
+ free(bsdtar->siginfo);
+}
Index: COPYING
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/COPYING,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/COPYING -L usr.bin/tar/COPYING -u -r1.3 -r1.4
--- usr.bin/tar/COPYING
+++ usr.bin/tar/COPYING
@@ -1,3 +1,5 @@
+$FreeBSD: src/usr.bin/tar/COPYING,v 1.2.2.1 2008/02/10 23:24:16 kientzle Exp $
+
All of the C source code and documentation in this package is subject
to the following:
@@ -24,4 +26,37 @@
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-$FreeBSD: src/usr.bin/tar/COPYING,v 1.2 2007/01/09 08:12:17 kientzle Exp $
+Some of the filename pattern matching code is based on code subject
+to the following license:
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
Index: matching.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/matching.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/matching.c -L usr.bin/tar/matching.c -u -r1.3 -r1.4
--- usr.bin/tar/matching.c
+++ usr.bin/tar/matching.c
@@ -24,8 +24,7 @@
*/
#include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/matching.c,v 1.11 2007/03/11 10:36:42 kientzle Exp $");
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: src/usr.bin/tar/matching.c,v 1.11.2.4 2008/08/27 04:59:00 kientzle Exp $");
#ifdef HAVE_ERRNO_H
#include <errno.h>
@@ -60,6 +59,7 @@
static void initialize_matching(struct bsdtar *);
static int match_exclusion(struct match *, const char *pathname);
static int match_inclusion(struct match *, const char *pathname);
+static int pathmatch(const char *p, const char *s);
/*
* The matching logic here needs to be re-thought. I started out to
@@ -119,8 +119,6 @@
match = malloc(sizeof(*match) + strlen(pattern) + 1);
if (match == NULL)
bsdtar_errc(bsdtar, 1, errno, "Out of memory");
- if (pattern[0] == '/')
- pattern++;
strcpy(match->pattern, pattern);
/* Both "foo/" and "foo" should match "foo/bar". */
if (match->pattern[strlen(match->pattern)-1] == '/')
@@ -158,7 +156,7 @@
*/
if (match->matches == 0) {
match->matches++;
- matching->inclusions_unmatched_count++;
+ matching->inclusions_unmatched_count--;
return (0);
}
/*
@@ -196,12 +194,12 @@
const char *p;
if (*match->pattern == '*' || *match->pattern == '/')
- return (bsdtar_fnmatch(match->pattern, pathname) == 0);
+ return (pathmatch(match->pattern, pathname) == 0);
for (p = pathname; p != NULL; p = strchr(p, '/')) {
if (*p == '/')
p++;
- if (bsdtar_fnmatch(match->pattern, p) == 0)
+ if (pathmatch(match->pattern, p) == 0)
return (1);
}
return (0);
@@ -214,7 +212,7 @@
int
match_inclusion(struct match *match, const char *pathname)
{
- return (bsdtar_fnmatch(match->pattern, pathname) == 0);
+ return (pathmatch(match->pattern, pathname) == 0);
}
void
@@ -260,6 +258,64 @@
}
+int
+unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg)
+{
+ struct matching *matching;
+ struct match *p;
+
+ matching = bsdtar->matching;
+ if (matching == NULL)
+ return (0);
+
+ p = matching->inclusions;
+ while (p != NULL) {
+ if (p->matches == 0) {
+ bsdtar->return_value = 1;
+ bsdtar_warnc(bsdtar, 0, "%s: %s",
+ p->pattern, msg);
+ }
+ p = p->next;
+ }
+ return (matching->inclusions_unmatched_count);
+}
+
+/*
+ * TODO: Extend this so that the following matches work:
+ * "foo//bar" == "foo/bar"
+ * "foo/./bar" == "foo/bar"
+ * "./foo" == "foo"
+ *
+ * The POSIX fnmatch() function doesn't handle any of these, but
+ * all are common situations that arise when paths are generated within
+ * large scripts. E.g., the following is quite common:
+ * MYPATH=foo/ TARGET=$MYPATH/bar
+ * It may be worthwhile to edit such paths at write time as well,
+ * especially when such editing may avoid the need for long pathname
+ * extensions.
+ */
+static int
+pathmatch(const char *pattern, const char *string)
+{
+ /*
+ * Strip leading "./" or ".//" so that, e.g.,
+ * "foo" matches "./foo". In particular, this
+ * opens up an optimization for the writer to
+ * elide leading "./".
+ */
+ if (pattern[0] == '.' && pattern[1] == '/') {
+ pattern += 2;
+ while (pattern[0] == '/')
+ ++pattern;
+ }
+ if (string[0] == '.' && string[1] == '/') {
+ string += 2;
+ while (string[0] == '/')
+ ++string;
+ }
+ return (bsdtar_fnmatch(pattern, string));
+}
+
#if defined(HAVE_FNMATCH) && defined(HAVE_FNM_LEADING_DIR)
Index: config_midnightbsd.h
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/config_midnightbsd.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L usr.bin/tar/config_midnightbsd.h -L usr.bin/tar/config_midnightbsd.h -u -r1.1 -r1.2
--- usr.bin/tar/config_midnightbsd.h
+++ usr.bin/tar/config_midnightbsd.h
@@ -22,21 +22,25 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/usr.bin/tar/config_freebsd.h,v 1.1 2007/03/11 10:36:42 kientzle Exp $
- # $MidnightBSD$
+ * $FreeBSD: src/usr.bin/tar/config_freebsd.h,v 1.1.4.3 2008/08/10 07:35:55 kientzle Exp $
*/
/* A default configuration for FreeBSD, used if there is no config.h. */
-#define PACKAGE_NAME "bsdtar"
+#include <sys/param.h> /* __FreeBSD_version */
+#if __FreeBSD__ > 4
#define HAVE_ACL_GET_PERM 0
#define HAVE_ACL_GET_PERM_NP 1
#define HAVE_ACL_PERMSET_T 1
#define HAVE_ACL_USER 1
+#endif
#undef HAVE_ATTR_XATTR_H
#define HAVE_BZLIB_H 1
#define HAVE_CHFLAGS 1
+#define HAVE_CHROOT 1
+#define HAVE_DECL_OPTARG 1
+#define HAVE_DECL_OPTIND 1
#define HAVE_DIRENT_D_NAMLEN 1
#define HAVE_DIRENT_H 1
#define HAVE_D_MD_ORDER 1
@@ -68,9 +72,12 @@
#define HAVE_MEMMOVE 1
#define HAVE_MEMORY_H 1
#define HAVE_MEMSET 1
+#if __FreeBSD_version >= 450002 /* nl_langinfo introduced */
#define HAVE_NL_LANGINFO 1
+#endif
#define HAVE_PATHS_H 1
#define HAVE_PWD_H 1
+#define HAVE_REGEX_H 1
#define HAVE_SETLOCALE 1
#define HAVE_STDARG_H 1
#define HAVE_STDINT_H 1
@@ -84,7 +91,6 @@
#define HAVE_STRRCHR 1
#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
-#define HAVE_STRUCT_STAT_ST_RDEV 1
#define HAVE_SYS_ACL_H 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_PARAM_H 1
Index: bsdtar.h
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/bsdtar.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/bsdtar.h -L usr.bin/tar/bsdtar.h -u -r1.3 -r1.4
--- usr.bin/tar/bsdtar.h
+++ usr.bin/tar/bsdtar.h
@@ -22,8 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.28 2007/05/29 05:39:10 kientzle Exp $
- * $MidnightBSD$
+ * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.28.2.6 2008/08/10 07:48:41 kientzle Exp $
*/
#include "bsdtar_platform.h"
@@ -58,6 +57,7 @@
char create_compression; /* j, y, or z */
const char *compress_program;
char option_absolute_paths; /* -P */
+ char option_chroot; /* --chroot */
char option_dont_traverse_mounts; /* --one-file-system */
char option_fast_read; /* --fast-read */
char option_honor_nodump; /* --nodump */
@@ -65,6 +65,7 @@
char option_no_owner; /* -o */
char option_no_subdirs; /* -n */
char option_null; /* --null */
+ char option_numeric_owner; /* --numeric-owner */
char option_stdout; /* -O */
char option_totals; /* --totals */
char option_unlink_first; /* -U */
@@ -90,17 +91,19 @@
* Data for various subsystems. Full definitions are located in
* the file where they are used.
*/
+ struct archive_entry_linkresolver *resolver;
struct archive_dir *archive_dir; /* for write.c */
struct name_cache *gname_cache; /* for write.c */
- struct links_cache *links_cache; /* for write.c */
+ char *buff; /* for write.c */
struct matching *matching; /* for matching.c */
struct security *security; /* for read.c */
struct name_cache *uname_cache; /* for write.c */
+ struct siginfo_data *siginfo; /* for siginfo.c */
+ struct substitution *substitution; /* for subst.c */
};
void bsdtar_errc(struct bsdtar *, int _eval, int _code,
- const char *fmt, ...);
-void bsdtar_strmode(struct archive_entry *entry, char *bp);
+ const char *fmt, ...) __dead2;
void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...);
void cleanup_exclusions(struct bsdtar *);
void do_chdir(struct bsdtar *);
@@ -115,12 +118,23 @@
int (*process)(struct bsdtar *, const char *));
void safe_fprintf(FILE *, const char *fmt, ...);
void set_chdir(struct bsdtar *, const char *newdir);
+void siginfo_init(struct bsdtar *);
+void siginfo_setinfo(struct bsdtar *, const char * oper,
+ const char * path, int64_t size);
+void siginfo_printinfo(struct bsdtar *, off_t progress);
+void siginfo_done(struct bsdtar *);
void tar_mode_c(struct bsdtar *bsdtar);
void tar_mode_r(struct bsdtar *bsdtar);
void tar_mode_t(struct bsdtar *bsdtar);
void tar_mode_u(struct bsdtar *bsdtar);
void tar_mode_x(struct bsdtar *bsdtar);
int unmatched_inclusions(struct bsdtar *bsdtar);
+int unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg);
void usage(struct bsdtar *);
int yes(const char *fmt, ...);
+#if HAVE_REGEX_H
+void add_substitution(struct bsdtar *, const char *);
+int apply_substitution(struct bsdtar *, const char *, char **, int);
+void cleanup_substitution(struct bsdtar *);
+#endif
Index: write.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/write.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/write.c -L usr.bin/tar/write.c -u -r1.3 -r1.4
--- usr.bin/tar/write.c
+++ usr.bin/tar/write.c
@@ -24,8 +24,7 @@
*/
#include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.63 2007/05/29 05:39:10 kientzle Exp $");
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.63.2.11 2008/11/28 20:13:23 kientzle Exp $");
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -63,9 +62,6 @@
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h> /* for Linux file flags */
#endif
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h> /* for Linux file flags */
-#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
@@ -83,14 +79,14 @@
#include "bsdtar.h"
#include "tree.h"
+/* Size of buffer for holding file data prior to writing. */
+#define FILEDATABUFLEN 65536
+
/* Fixed size of uname/gname caches. */
#define name_cache_size 101
static const char * const NO_NAME = "(noname)";
-/* Initial size of link cache. */
-#define links_cache_initial_size 1024
-
struct archive_dir_entry {
struct archive_dir_entry *next;
time_t mtime_sec;
@@ -102,21 +98,6 @@
struct archive_dir_entry *head, *tail;
};
-struct links_cache {
- unsigned long number_entries;
- size_t number_buckets;
- struct links_entry **buckets;
-};
-
-struct links_entry {
- struct links_entry *next;
- struct links_entry *previous;
- int links;
- dev_t dev;
- ino_t ino;
- char *name;
-};
-
struct name_cache {
int probes;
int hits;
@@ -140,13 +121,10 @@
static int copy_file_data(struct bsdtar *bsdtar,
struct archive *a, struct archive *ina);
static void create_cleanup(struct bsdtar *);
-static void free_buckets(struct bsdtar *, struct links_cache *);
static void free_cache(struct name_cache *cache);
static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid);
static int lookup_gname_helper(struct bsdtar *bsdtar,
const char **name, id_t gid);
-static void lookup_hardlink(struct bsdtar *,
- struct archive_entry *entry, const struct stat *);
static const char * lookup_uname(struct bsdtar *bsdtar, uid_t uid);
static int lookup_uname_helper(struct bsdtar *bsdtar,
const char **name, id_t uid);
@@ -161,8 +139,10 @@
static void write_entry(struct bsdtar *, struct archive *,
const struct stat *, const char *pathname,
const char *accpath);
+static void write_entry_backend(struct bsdtar *, struct archive *,
+ struct archive_entry *);
static int write_file_data(struct bsdtar *, struct archive *,
- int fd);
+ struct archive_entry *, int fd);
static void write_hierarchy(struct bsdtar *, struct archive *,
const char *);
@@ -221,6 +201,9 @@
archive_write_set_compression_gzip(a);
break;
#endif
+ case 'Z':
+ archive_write_set_compression_compress(a);
+ break;
default:
bsdtar_errc(bsdtar, 1, 0,
"Unrecognized compression option -%c",
@@ -233,13 +216,6 @@
bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
write_archive(a, bsdtar);
-
- if (bsdtar->option_totals) {
- fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n",
- (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a));
- }
-
- archive_write_finish(a);
}
/*
@@ -328,12 +304,6 @@
write_archive(a, bsdtar); /* XXX check return val XXX */
- if (bsdtar->option_totals) {
- fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n",
- (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a));
- }
-
- archive_write_finish(a);
close(bsdtar->fd);
bsdtar->fd = -1;
}
@@ -414,12 +384,6 @@
write_archive(a, bsdtar);
- if (bsdtar->option_totals) {
- fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n",
- (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a));
- }
-
- archive_write_finish(a);
close(bsdtar->fd);
bsdtar->fd = -1;
@@ -440,6 +404,19 @@
write_archive(struct archive *a, struct bsdtar *bsdtar)
{
const char *arg;
+ struct archive_entry *entry, *sparse_entry;
+
+ /* We want to catch SIGINFO and SIGUSR1. */
+ siginfo_init(bsdtar);
+
+ /* Allocate a buffer for file data. */
+ if ((bsdtar->buff = malloc(FILEDATABUFLEN)) == NULL)
+ bsdtar_errc(bsdtar, 1, 0, "cannot allocate memory");
+
+ if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL)
+ bsdtar_errc(bsdtar, 1, 0, "cannot create link resolver");
+ archive_entry_linkresolver_set_strategy(bsdtar->resolver,
+ archive_format(a));
if (bsdtar->names_from_file != NULL)
archive_names_from_file(bsdtar, a);
@@ -455,7 +432,7 @@
bsdtar_warnc(bsdtar, 1, 0,
"Missing argument for -C");
bsdtar->return_value = 1;
- return;
+ goto cleanup;
}
}
set_chdir(bsdtar, arg);
@@ -472,11 +449,34 @@
bsdtar->argv++;
}
+ entry = NULL;
+ archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
+ while (entry != NULL) {
+ write_entry_backend(bsdtar, a, entry);
+ archive_entry_free(entry);
+ entry = NULL;
+ archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
+ }
+
create_cleanup(bsdtar);
if (archive_write_close(a)) {
bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
bsdtar->return_value = 1;
}
+
+cleanup:
+ /* Free file data buffer. */
+ free(bsdtar->buff);
+
+ if (bsdtar->option_totals) {
+ fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n",
+ (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a));
+ }
+
+ archive_write_finish(a);
+
+ /* Restore old SIGINFO + SIGUSR1 handlers. */
+ siginfo_done(bsdtar);
}
/*
@@ -572,6 +572,10 @@
if (bsdtar->verbose)
safe_fprintf(stderr, "a %s",
archive_entry_pathname(in_entry));
+ siginfo_setinfo(bsdtar, "copying",
+ archive_entry_pathname(in_entry),
+ archive_entry_size(in_entry));
+ siginfo_printinfo(bsdtar, 0);
e = archive_write_header(a, in_entry);
if (e != ARCHIVE_OK) {
@@ -585,9 +589,12 @@
if (e == ARCHIVE_FATAL)
exit(1);
- if (e >= ARCHIVE_WARN)
- if (copy_file_data(bsdtar, a, ina))
+ if (e >= ARCHIVE_WARN) {
+ if (archive_entry_size(in_entry) == 0)
+ archive_read_data_skip(ina);
+ else if (copy_file_data(bsdtar, a, ina))
exit(1);
+ }
if (bsdtar->verbose)
fprintf(stderr, "\n");
@@ -601,18 +608,23 @@
static int
copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
{
- char buff[64*1024];
ssize_t bytes_read;
ssize_t bytes_written;
+ off_t progress = 0;
- bytes_read = archive_read_data(ina, buff, sizeof(buff));
+ bytes_read = archive_read_data(ina, bsdtar->buff, FILEDATABUFLEN);
while (bytes_read > 0) {
- bytes_written = archive_write_data(a, buff, bytes_read);
+ siginfo_printinfo(bsdtar, progress);
+
+ bytes_written = archive_write_data(a, bsdtar->buff,
+ bytes_read);
if (bytes_written < bytes_read) {
bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
return (-1);
}
- bytes_read = archive_read_data(ina, buff, sizeof(buff));
+ progress += bytes_written;
+ bytes_read = archive_read_data(ina, bsdtar->buff,
+ FILEDATABUFLEN);
}
return (0);
@@ -647,8 +659,10 @@
const struct stat *st = NULL, *lst = NULL;
int descend;
- if (tree_ret == TREE_ERROR_DIR)
+ if (tree_ret == TREE_ERROR_DIR) {
bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name);
+ bsdtar->return_value = 1;
+ }
if (tree_ret != TREE_REGULAR)
continue;
lst = tree_current_lstat(tree);
@@ -775,25 +789,78 @@
}
/*
+ * Backend for write_entry.
+ */
+static void
+write_entry_backend(struct bsdtar *bsdtar, struct archive *a,
+ struct archive_entry *entry)
+{
+ int fd = -1;
+ int e;
+
+ if (archive_entry_size(entry) > 0) {
+ const char *pathname = archive_entry_sourcepath(entry);
+ fd = open(pathname, O_RDONLY);
+ if (fd == -1) {
+ if (!bsdtar->verbose)
+ bsdtar_warnc(bsdtar, errno,
+ "%s: could not open file", pathname);
+ else
+ fprintf(stderr, ": %s", strerror(errno));
+ return;
+ }
+ }
+
+ e = archive_write_header(a, entry);
+ if (e != ARCHIVE_OK) {
+ if (!bsdtar->verbose)
+ bsdtar_warnc(bsdtar, 0, "%s: %s",
+ archive_entry_pathname(entry),
+ archive_error_string(a));
+ else
+ fprintf(stderr, ": %s", archive_error_string(a));
+ }
+
+ if (e == ARCHIVE_FATAL)
+ exit(1);
+
+ /*
+ * If we opened a file earlier, write it out now. Note that
+ * the format handler might have reset the size field to zero
+ * to inform us that the archive body won't get stored. In
+ * that case, just skip the write.
+ */
+ if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) {
+ if (write_file_data(bsdtar, a, entry, fd))
+ exit(1);
+ }
+
+ /*
+ * If we opened a file, close it now even if there was an error
+ * which made us decide not to write the archive body.
+ */
+ if (fd >= 0)
+ close(fd);
+}
+
+/*
* Add a single filesystem object to the archive.
*/
static void
write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
const char *pathname, const char *accpath)
{
- struct archive_entry *entry;
- int e;
- int fd;
+ struct archive_entry *entry, *sparse_entry;
#ifdef __linux
int r;
unsigned long stflags;
#endif
static char linkbuffer[PATH_MAX+1];
- fd = -1;
entry = archive_entry_new();
archive_entry_set_pathname(entry, pathname);
+ archive_entry_copy_sourcepath(entry, accpath);
/*
* Rewrite the pathname to be archived. If rewrite
@@ -809,9 +876,6 @@
if (!new_enough(bsdtar, archive_entry_pathname(entry), st))
goto abort;
- if (!S_ISDIR(st->st_mode) && (st->st_nlink > 1))
- lookup_hardlink(bsdtar, entry, st);
-
/* Display entry as we process it. This format is required by SUSv2. */
if (bsdtar->verbose)
safe_fprintf(stderr, "a %s", archive_entry_pathname(entry));
@@ -846,6 +910,7 @@
#endif
#ifdef __linux
+ int fd;
if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) &&
((fd = open(accpath, O_RDONLY|O_NONBLOCK)) >= 0) &&
((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), (fd = -1), r) >= 0 &&
@@ -858,76 +923,50 @@
setup_acls(bsdtar, entry, accpath);
setup_xattrs(bsdtar, entry, accpath);
- /*
- * If it's a regular file (and non-zero in size) make sure we
- * can open it before we start to write. In particular, note
- * that we can always archive a zero-length file, even if we
- * can't read it.
- */
- if (S_ISREG(st->st_mode) && st->st_size > 0) {
- fd = open(accpath, O_RDONLY);
- if (fd < 0) {
- if (!bsdtar->verbose)
- bsdtar_warnc(bsdtar, errno, "%s: could not open file", pathname);
- else
- fprintf(stderr, ": %s", strerror(errno));
- goto cleanup;
- }
- }
-
/* Non-regular files get archived with zero size. */
if (!S_ISREG(st->st_mode))
archive_entry_set_size(entry, 0);
- e = archive_write_header(a, entry);
- if (e != ARCHIVE_OK) {
- if (!bsdtar->verbose)
- bsdtar_warnc(bsdtar, 0, "%s: %s", pathname,
- archive_error_string(a));
- else
- fprintf(stderr, ": %s", archive_error_string(a));
- }
+ /* Record what we're doing, for the benefit of SIGINFO / SIGUSR1. */
+ siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry),
+ archive_entry_size(entry));
+ archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
- if (e == ARCHIVE_FATAL)
- exit(1);
+ /* Handle SIGINFO / SIGUSR1 request if one was made. */
+ siginfo_printinfo(bsdtar, 0);
- /*
- * If we opened a file earlier, write it out now. Note that
- * the format handler might have reset the size field to zero
- * to inform us that the archive body won't get stored. In
- * that case, just skip the write.
- */
- if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0)
- if (write_file_data(bsdtar, a, fd))
- exit(1);
+ while (entry != NULL) {
+ write_entry_backend(bsdtar, a, entry);
+ archive_entry_free(entry);
+ entry = sparse_entry;
+ sparse_entry = NULL;
+ }
cleanup:
if (bsdtar->verbose)
fprintf(stderr, "\n");
abort:
- if (fd >= 0)
- close(fd);
-
if (entry != NULL)
archive_entry_free(entry);
}
-/* Helper function to copy file to archive, with stack-allocated buffer. */
+/* Helper function to copy file to archive. */
static int
-write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd)
+write_file_data(struct bsdtar *bsdtar, struct archive *a,
+ struct archive_entry *entry, int fd)
{
- char buff[64*1024];
ssize_t bytes_read;
ssize_t bytes_written;
+ off_t progress = 0;
- /* XXX TODO: Allocate buffer on heap and store pointer to
- * it in bsdtar structure; arrange cleanup as well. XXX */
-
- bytes_read = read(fd, buff, sizeof(buff));
+ bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
while (bytes_read > 0) {
- bytes_written = archive_write_data(a, buff, bytes_read);
+ siginfo_printinfo(bsdtar, progress);
+
+ bytes_written = archive_write_data(a, bsdtar->buff,
+ FILEDATABUFLEN);
if (bytes_written < 0) {
/* Write failed; this is bad */
bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
@@ -936,10 +975,12 @@
if (bytes_written < bytes_read) {
/* Write was truncated; warn but continue. */
bsdtar_warnc(bsdtar, 0,
- "Truncated write; file may have grown while being archived.");
+ "%s: Truncated write; file may have grown while being archived.",
+ archive_entry_pathname(entry));
return (0);
}
- bytes_read = read(fd, buff, sizeof(buff));
+ progress += bytes_written;
+ bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
}
return 0;
}
@@ -948,169 +989,12 @@
static void
create_cleanup(struct bsdtar *bsdtar)
{
- /* Free inode->pathname map used for hardlink detection. */
- if (bsdtar->links_cache != NULL) {
- free_buckets(bsdtar, bsdtar->links_cache);
- free(bsdtar->links_cache);
- bsdtar->links_cache = NULL;
- }
-
free_cache(bsdtar->uname_cache);
bsdtar->uname_cache = NULL;
free_cache(bsdtar->gname_cache);
bsdtar->gname_cache = NULL;
}
-
-static void
-free_buckets(struct bsdtar *bsdtar, struct links_cache *links_cache)
-{
- size_t i;
-
- if (links_cache->buckets == NULL)
- return;
-
- for (i = 0; i < links_cache->number_buckets; i++) {
- while (links_cache->buckets[i] != NULL) {
- struct links_entry *lp = links_cache->buckets[i]->next;
- if (bsdtar->option_warn_links)
- bsdtar_warnc(bsdtar, 0, "Missing links to %s",
- links_cache->buckets[i]->name);
- if (links_cache->buckets[i]->name != NULL)
- free(links_cache->buckets[i]->name);
- free(links_cache->buckets[i]);
- links_cache->buckets[i] = lp;
- }
- }
- free(links_cache->buckets);
- links_cache->buckets = NULL;
-}
-
-static void
-lookup_hardlink(struct bsdtar *bsdtar, struct archive_entry *entry,
- const struct stat *st)
-{
- struct links_cache *links_cache;
- struct links_entry *le, **new_buckets;
- int hash;
- size_t i, new_size;
-
- /* If necessary, initialize the links cache. */
- links_cache = bsdtar->links_cache;
- if (links_cache == NULL) {
- bsdtar->links_cache = malloc(sizeof(struct links_cache));
- if (bsdtar->links_cache == NULL)
- bsdtar_errc(bsdtar, 1, ENOMEM,
- "No memory for hardlink detection.");
- links_cache = bsdtar->links_cache;
- memset(links_cache, 0, sizeof(struct links_cache));
- links_cache->number_buckets = links_cache_initial_size;
- links_cache->buckets = malloc(links_cache->number_buckets *
- sizeof(links_cache->buckets[0]));
- if (links_cache->buckets == NULL) {
- bsdtar_errc(bsdtar, 1, ENOMEM,
- "No memory for hardlink detection.");
- }
- for (i = 0; i < links_cache->number_buckets; i++)
- links_cache->buckets[i] = NULL;
- }
-
- /* If the links cache overflowed and got flushed, don't bother. */
- if (links_cache->buckets == NULL)
- return;
-
- /* If the links cache is getting too full, enlarge the hash table. */
- if (links_cache->number_entries > links_cache->number_buckets * 2)
- {
- new_size = links_cache->number_buckets * 2;
- new_buckets = malloc(new_size * sizeof(struct links_entry *));
-
- if (new_buckets != NULL) {
- memset(new_buckets, 0,
- new_size * sizeof(struct links_entry *));
- for (i = 0; i < links_cache->number_buckets; i++) {
- while (links_cache->buckets[i] != NULL) {
- /* Remove entry from old bucket. */
- le = links_cache->buckets[i];
- links_cache->buckets[i] = le->next;
-
- /* Add entry to new bucket. */
- hash = (le->dev ^ le->ino) % new_size;
-
- if (new_buckets[hash] != NULL)
- new_buckets[hash]->previous =
- le;
- le->next = new_buckets[hash];
- le->previous = NULL;
- new_buckets[hash] = le;
- }
- }
- free(links_cache->buckets);
- links_cache->buckets = new_buckets;
- links_cache->number_buckets = new_size;
- } else {
- free_buckets(bsdtar, links_cache);
- bsdtar_warnc(bsdtar, ENOMEM,
- "No more memory for recording hard links");
- bsdtar_warnc(bsdtar, 0,
- "Remaining links will be dumped as full files");
- }
- }
-
- /* Try to locate this entry in the links cache. */
- hash = ( st->st_dev ^ st->st_ino ) % links_cache->number_buckets;
- for (le = links_cache->buckets[hash]; le != NULL; le = le->next) {
- if (le->dev == st->st_dev && le->ino == st->st_ino) {
- archive_entry_copy_hardlink(entry, le->name);
-
- /*
- * Decrement link count each time and release
- * the entry if it hits zero. This saves
- * memory and is necessary for proper -l
- * implementation.
- */
- if (--le->links <= 0) {
- if (le->previous != NULL)
- le->previous->next = le->next;
- if (le->next != NULL)
- le->next->previous = le->previous;
- if (le->name != NULL)
- free(le->name);
- if (links_cache->buckets[hash] == le)
- links_cache->buckets[hash] = le->next;
- links_cache->number_entries--;
- free(le);
- }
-
- return;
- }
- }
-
- /* Add this entry to the links cache. */
- le = malloc(sizeof(struct links_entry));
- if (le != NULL)
- le->name = strdup(archive_entry_pathname(entry));
- if ((le == NULL) || (le->name == NULL)) {
- free_buckets(bsdtar, links_cache);
- bsdtar_warnc(bsdtar, ENOMEM,
- "No more memory for recording hard links");
- bsdtar_warnc(bsdtar, 0,
- "Remaining hard links will be dumped as full files");
- if (le != NULL)
- free(le);
- return;
- }
- if (links_cache->buckets[hash] != NULL)
- links_cache->buckets[hash]->previous = le;
- links_cache->number_entries++;
- le->next = links_cache->buckets[hash];
- le->previous = NULL;
- links_cache->buckets[hash] = le;
- le->dev = st->st_dev;
- le->ino = st->st_ino;
- le->links = st->st_nlink - 1;
-}
-
#ifdef HAVE_POSIX_ACL
static void setup_acl(struct bsdtar *bsdtar,
struct archive_entry *entry, const char *accpath,
@@ -1532,7 +1416,7 @@
{
struct stat s;
- if (*bsdtar->argv == NULL)
+ if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL)
bsdtar_errc(bsdtar, 1, 0, "no files or directories specified");
if (bsdtar->filename == NULL)
bsdtar_errc(bsdtar, 1, 0, "Cannot append to stdout.");
@@ -1544,7 +1428,7 @@
if (stat(bsdtar->filename, &s) != 0)
return;
- if (!S_ISREG(s.st_mode))
+ if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
bsdtar_errc(bsdtar, 1, 0,
"Cannot append to %s: not a regular file.",
bsdtar->filename);
--- /dev/null
+++ usr.bin/tar/subst.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bsdtar_platform.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/subst.c,v 1.4.2.1 2008/08/10 07:35:55 kientzle Exp $");
+
+#if HAVE_REGEX_H
+#include "bsdtar.h"
+
+#include <errno.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef REG_BASIC
+#define REG_BASIC 0
+#endif
+
+struct subst_rule {
+ struct subst_rule *next;
+ regex_t re;
+ char *result;
+ unsigned int global:1, print:1, symlink:1;
+};
+
+struct substitution {
+ struct subst_rule *first_rule, *last_rule;
+};
+
+static void
+init_substitution(struct bsdtar *bsdtar)
+{
+ struct substitution *subst;
+
+ bsdtar->substitution = subst = malloc(sizeof(*subst));
+ if (subst == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Out of memory");
+ subst->first_rule = subst->last_rule = NULL;
+}
+
+void
+add_substitution(struct bsdtar *bsdtar, const char *rule_text)
+{
+ struct subst_rule *rule;
+ struct substitution *subst;
+ const char *end_pattern, *start_subst;
+ char *pattern;
+ int r;
+
+ if ((subst = bsdtar->substitution) == NULL) {
+ init_substitution(bsdtar);
+ subst = bsdtar->substitution;
+ }
+
+ rule = malloc(sizeof(*rule));
+ if (rule == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Out of memory");
+ rule->next = NULL;
+
+ if (subst->last_rule == NULL)
+ subst->first_rule = rule;
+ else
+ subst->last_rule->next = rule;
+ subst->last_rule = rule;
+
+ if (*rule_text == '\0')
+ bsdtar_errc(bsdtar, 1, 0, "Empty replacement string");
+ end_pattern = strchr(rule_text + 1, *rule_text);
+ if (end_pattern == NULL)
+ bsdtar_errc(bsdtar, 1, 0, "Invalid replacement string");
+
+ pattern = malloc(end_pattern - rule_text);
+ if (pattern == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Out of memory");
+ memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
+ pattern[end_pattern - rule_text - 1] = '\0';
+
+ if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
+ char buf[80];
+ regerror(r, &rule->re, buf, sizeof(buf));
+ bsdtar_errc(bsdtar, 1, 0, "Invalid regular expression: %s", buf);
+ }
+ free(pattern);
+
+ start_subst = end_pattern + 1;
+ end_pattern = strchr(start_subst, *rule_text);
+ if (end_pattern == NULL)
+ bsdtar_errc(bsdtar, 1, 0, "Invalid replacement string");
+
+ rule->result = malloc(end_pattern - start_subst + 1);
+ if (rule->result == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Out of memory");
+ memcpy(rule->result, start_subst, end_pattern - start_subst);
+ rule->result[end_pattern - start_subst] = '\0';
+
+ rule->global = 0;
+ rule->print = 0;
+ rule->symlink = 0;
+
+ while (*++end_pattern) {
+ switch (*end_pattern) {
+ case 'g':
+ case 'G':
+ rule->global = 1;
+ break;
+ case 'p':
+ case 'P':
+ rule->print = 1;
+ break;
+ case 's':
+ case 'S':
+ rule->symlink = 1;
+ break;
+ default:
+ bsdtar_errc(bsdtar, 1, 0, "Invalid replacement flag %c", *end_pattern);
+ }
+ }
+}
+
+static void
+realloc_strncat(struct bsdtar *bsdtar, char **str, const char *append, size_t len)
+{
+ char *new_str;
+ size_t old_len;
+
+ if (*str == NULL)
+ old_len = 0;
+ else
+ old_len = strlen(*str);
+
+ new_str = malloc(old_len + len + 1);
+ if (new_str == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Out of memory");
+ memcpy(new_str, *str, old_len);
+ memcpy(new_str + old_len, append, len);
+ new_str[old_len + len] = '\0';
+ free(*str);
+ *str = new_str;
+}
+
+static void
+realloc_strcat(struct bsdtar *bsdtar, char **str, const char *append)
+{
+ char *new_str;
+ size_t old_len;
+
+ if (*str == NULL)
+ old_len = 0;
+ else
+ old_len = strlen(*str);
+
+ new_str = malloc(old_len + strlen(append) + 1);
+ if (new_str == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Out of memory");
+ memcpy(new_str, *str, old_len);
+ strcpy(new_str + old_len, append);
+ free(*str);
+ *str = new_str;
+}
+
+int
+apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only)
+{
+ const char *path = name;
+ regmatch_t matches[10];
+ size_t i, j;
+ struct subst_rule *rule;
+ struct substitution *subst;
+ int c, got_match, print_match;
+
+ *result = NULL;
+
+ if ((subst = bsdtar->substitution) == NULL)
+ return 0;
+
+ got_match = 0;
+ print_match = 0;
+
+ for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
+ if (symlink_only && !rule->symlink)
+ continue;
+ if (regexec(&rule->re, name, 10, matches, 0))
+ break;
+
+ got_match = 1;
+ print_match |= rule->print;
+ realloc_strncat(bsdtar, result, name, matches[0].rm_so);
+
+ for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
+ if (rule->result[i] == '~') {
+ realloc_strncat(bsdtar, result, rule->result + j, i - j);
+ realloc_strncat(bsdtar, result, name, matches[0].rm_eo);
+ j = i + 1;
+ continue;
+ }
+ if (rule->result[i] != '\\')
+ continue;
+
+ ++i;
+ c = rule->result[i];
+ switch (c) {
+ case '~':
+ case '\\':
+ realloc_strncat(bsdtar, result, rule->result + j, i - j - 1);
+ j = i;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ realloc_strncat(bsdtar, result, rule->result + j, i - j - 1);
+ if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
+ free(*result);
+ *result = NULL;
+ return -1;
+ }
+ realloc_strncat(bsdtar, result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
+ j = i + 1;
+ break;
+ default:
+ /* Just continue; */
+ break;
+ }
+
+ }
+
+ realloc_strcat(bsdtar, result, rule->result + j);
+
+ name += matches[0].rm_eo;
+
+ if (!rule->global)
+ break;
+ }
+
+ if (got_match)
+ realloc_strcat(bsdtar, result, name);
+
+ if (print_match)
+ fprintf(stderr, "%s >> %s\n", path, *result);
+
+ return got_match;
+}
+
+void
+cleanup_substitution(struct bsdtar *bsdtar)
+{
+ struct subst_rule *rule;
+ struct substitution *subst;
+
+ if ((subst = bsdtar->substitution) == NULL)
+ return;
+
+ while ((rule = subst->first_rule) != NULL) {
+ subst->first_rule = rule->next;
+ free(rule->result);
+ free(rule);
+ }
+ free(subst);
+}
+#endif /* HAVE_REGEX_H */
Index: bsdtar.1
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/bsdtar.1,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/bsdtar.1 -L usr.bin/tar/bsdtar.1 -u -r1.3 -r1.4
--- usr.bin/tar/bsdtar.1
+++ usr.bin/tar/bsdtar.1
@@ -22,10 +22,9 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.35 2007/05/29 05:39:10 kientzle Exp $
-.\" $MidnightBSD$
+.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.35.2.8 2008/08/10 07:47:34 kientzle Exp $
.\"
-.Dd April 13, 2004
+.Dd May 15, 2008
.Dt BSDTAR 1
.Os
.Sh NAME
@@ -148,13 +147,19 @@
.It Fl -check-links ( Fl W Cm check-links )
(c and r modes only)
Issue a warning message unless all links to each file are archived.
+.It Fl -chroot ( Fl W Cm chroot )
+(x mode only)
+.Fn chroot
+to the current directory after processing any
+.Fl C
+options and before extracting any files.
.It Fl -exclude Ar pattern ( Fl W Cm exclude Ns = Ns Ar pattern )
Do not process files or directories that match the
specified pattern.
Note that exclusions take precedence over patterns or filenames
specified on the command line.
.It Fl -format Ar format ( Fl W Cm format Ns = Ns Ar format )
-(c mode only)
+(c, r, u mode only)
Use the specified format for the created archive.
Supported formats include
.Dq cpio ,
@@ -165,6 +170,8 @@
Other formats may also be supported; see
.Xr libarchive-formats 5
for more information about currently-supported formats.
+In r and u modes, when extending an existing archive, the format specified
+here must be compatible with the format of the existing archive on disk.
.It Fl f Ar file
Read the archive from or write the archive to the specified file.
The filename can be
@@ -175,15 +182,6 @@
.Fx ,
the default tape device is
.Pa /dev/sa0 . )
-.It Fl -fast-read ( Fl W Cm fast-read )
-(x and t mode only)
-Extract or list only the first archive entry that matches each pattern
-or filename operand.
-Exit as soon as each specified pattern or filename has been matched.
-By default, the archive is always read to the very end, since
-there can be multiple entries with the same name and, by convention,
-later entries overwrite earlier entries.
-This option is provided as a performance optimization.
.It Fl H
(c and r mode only)
Symbolic links named on the command line will be followed; the
@@ -227,22 +225,19 @@
Do not overwrite existing files.
In particular, if a file appears more than once in an archive,
later copies will not overwrite earlier copies.
+.It Fl -keep-newer-files ( Fl W Cm keep-newer-files )
+(x mode only)
+Do not overwrite existing files that are newer than the
+versions appearing in the archive being extracted.
.It Fl L
(c and r mode only)
All symbolic links will be followed.
Normally, symbolic links are archived as such.
With this option, the target of the link will be archived instead.
.It Fl l
-If
-.Ev POSIXLY_CORRECT
-is specified in the environment, this is a synonym for the
+This is a synonym for the
.Fl -check-links
option.
-Otherwise, an error will be displayed.
-Users who desire behavior compatible with GNU tar should use
-the
-.Fl -one-file-system
-option instead.
.It Fl m
(x mode only)
Do not extract modification time.
@@ -283,6 +278,10 @@
.Fl print0
option to
.Xr find 1 .
+.It Fl -numeric-owner
+(x mode only)
+Ignore symbolic user and group names when restoring archives to disk,
+only numeric uid and gid values will be obeyed.
.It Fl O
(x, t modes only)
In extract (-x) mode, files will be written to standard out rather than
@@ -290,7 +289,7 @@
In list (-t) mode, the file listing will be written to stderr rather than
the usual stdout.
.It Fl o
-(x mode only)
+(x mode)
Use the user and group of the user running the program rather
than those specified in the archive.
Note that this has no significance unless
@@ -299,6 +298,10 @@
In this case, the file modes and flags from
the archive will be restored, but ACLs or owner information in
the archive will be discarded.
+.It Fl o
+(c, r, u mode)
+A synonym for
+.Fl -format Ar ustar
.It Fl -one-file-system ( Fl W Cm one-file-system )
(c, r, and u modes)
Do not cross mount points.
@@ -327,12 +330,43 @@
is being run by root, the default is to restore the owner unless the
.Fl o
option is also specified.
+.It Fl q ( Fl -fast-read )
+(x and t mode only)
+Extract or list only the first archive entry that matches each pattern
+or filename operand.
+Exit as soon as each specified pattern or filename has been matched.
+By default, the archive is always read to the very end, since
+there can be multiple entries with the same name and, by convention,
+later entries overwrite earlier entries.
+This option is provided as a performance optimization.
+.It Fl S
+(x mode only)
+Extract files as sparse files.
+For every block on disk, check first if it contains only NULL bytes and seek
+over it otherwise.
+This works similiar to the conv=sparse option of dd.
.It Fl -strip-components Ar count ( Fl W Cm strip-components Ns = Ns Ar count )
(x and t mode only)
Remove the specified number of leading path elements.
Pathnames with fewer elements will be silently skipped.
Note that the pathname is edited after checking inclusion/exclusion patterns
but before security checks.
+.It Fl s Ar pattern
+Modify file or archive member names according to
+.Pa pattern .
+The pattern has the format /old/new/[gps].
+old is a basic regular expression.
+If it doesn't apply, the pattern is skipped.
+new is the replacement string of the matched part.
+~ is substituted with the match, \1 to \9 with the content of
+the corresponding captured group.
+The optional trailing g specifies that matching should continue
+after the matched part and stopped on the first unmatched pattern.
+The optional trailing s specifies that the pattern applies to the value
+of symbolic links.
+The optional trailing p specifies that after a successful substitution
+the original path name and the new path name should be printed to
+standard error.
.It Fl T Ar filename
In x or t mode,
.Nm
@@ -378,6 +412,12 @@
Additional
.Fl v
options will provide additional detail.
+.It Fl -version
+Print version of
+.Nm
+and
+.Nm libarchive ,
+and exit.
.It Fl W Ar longopt=value
Long options (preceded by
.Fl - )
@@ -413,6 +453,15 @@
.Nm tar
implementations, this implementation recognizes gzip compression
automatically when reading archives.
+.It Fl Z
+(c mode only)
+Compress the resulting archive with
+.Xr compress 1 .
+In extract or list modes, this option is ignored.
+Note that, unlike other
+.Nm tar
+implementations, this implementation recognizes compress compression
+automatically when reading archives.
.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
@@ -423,11 +472,6 @@
See
.Xr environ 7
for more information.
-.It Ev POSIXLY_CORRECT
-If this environment variable is defined, the
-.Fl l
-option will be interpreted in accordance with
-.St -p1003.1-96 .
.It Ev TAPE
The default tape device.
The
@@ -500,6 +544,17 @@
.Pa foo2
to the output archive.
.Pp
+An input file in
+.Xr mtree 5
+format can be used to create an output archive with arbitrary ownership,
+permissions, or names that differ from existing data on disk:
+.Pp
+.Dl $ cat input.mtree
+.Dl #mtree
+.Dl usr/bin uid=0 gid=0 mode=0755 type=dir
+.Dl usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls
+.Dl $ tar -cvf output.tar @input.mtree
+.Pp
The
.Fl -newer
and
@@ -641,6 +696,7 @@
components, or symlinks to other directories.
.Sh SEE ALSO
.Xr bzip2 1 ,
+.Xr compress 1 ,
.Xr cpio 1 ,
.Xr gzip 1 ,
.Xr mt 1 ,
@@ -665,7 +721,7 @@
.Sh HISTORY
A
.Nm tar
-command appeared in Seventh Edition Unix.
+command appeared in Seventh Edition Unix, which was released in January, 1979.
There have been numerous other implementations,
many of which extended the file format.
John Gilmore's
@@ -682,13 +738,16 @@
.Xr libarchive 3
library.
.Sh BUGS
-POSIX and GNU violently disagree about the meaning of the
+This program follows
+.St -p1003.1-96
+for the definition of the
.Fl l
option.
-Because of the potential for disaster if someone expects
-one behavior and gets the other, the
+Note that GNU tar prior to version 1.15 treated
.Fl l
-option is deliberately broken in this implementation.
+as a synonym for the
+.Fl -one-file-system
+option.
.Pp
The
.Fl C Pa dir
Index: bsdtar.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/bsdtar.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/bsdtar.c -L usr.bin/tar/bsdtar.c -u -r1.3 -r1.4
--- usr.bin/tar/bsdtar.c
+++ usr.bin/tar/bsdtar.c
@@ -24,8 +24,7 @@
*/
#include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.77 2007/09/09 00:07:18 kientzle Exp $");
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.77.2.13 2008/08/25 02:14:52 kientzle Exp $");
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
@@ -79,6 +78,14 @@
#include "bsdtar.h"
+#if !HAVE_DECL_OPTARG
+extern int optarg;
+#endif
+
+#if !HAVE_DECL_OPTIND
+extern int optind;
+#endif
+
/*
* Per POSIX.1-1988, tar defaults to reading/writing archives to/from
* the default tape device for the system. Pick something reasonable here.
@@ -111,7 +118,7 @@
* non-option. Otherwise, GNU getopt() permutes the arguments and
* screws up -C processing.
*/
-static const char *tar_opts = "+Bb:C:cf:HhI:jkLlmnOoPprtT:UuvW:wX:xyZz";
+static const char *tar_opts = "+Bb:C:cf:HhI:jkLlmnOoPpqrts:ST:UuvW:wX:xyZz";
/*
* Most of these long options are deliberately not documented. They
@@ -129,12 +136,13 @@
/* Fake short equivalents for long options that otherwise lack them. */
enum {
- OPTION_CHECK_LINKS=1,
+ OPTION_CHECK_LINKS = 1,
+ OPTION_CHROOT,
OPTION_EXCLUDE,
- OPTION_FAST_READ,
OPTION_FORMAT,
OPTION_HELP,
OPTION_INCLUDE,
+ OPTION_KEEP_NEWER_FILES,
OPTION_NEWER_CTIME,
OPTION_NEWER_CTIME_THAN,
OPTION_NEWER_MTIME,
@@ -143,6 +151,7 @@
OPTION_NO_SAME_OWNER,
OPTION_NO_SAME_PERMISSIONS,
OPTION_NULL,
+ OPTION_NUMERIC_OWNER,
OPTION_ONE_FILE_SYSTEM,
OPTION_POSIX,
OPTION_STRIP_COMPONENTS,
@@ -164,6 +173,8 @@
{ "bzip2", no_argument, NULL, 'j' },
{ "cd", required_argument, NULL, 'C' },
{ "check-links", no_argument, NULL, OPTION_CHECK_LINKS },
+ { "chroot", no_argument, NULL, OPTION_CHROOT },
+ { "compress", no_argument, NULL, 'Z' },
{ "confirmation", no_argument, NULL, 'w' },
{ "create", no_argument, NULL, 'c' },
{ "dereference", no_argument, NULL, 'L' },
@@ -171,7 +182,7 @@
{ "exclude", required_argument, NULL, OPTION_EXCLUDE },
{ "exclude-from", required_argument, NULL, 'X' },
{ "extract", no_argument, NULL, 'x' },
- { "fast-read", no_argument, NULL, OPTION_FAST_READ },
+ { "fast-read", no_argument, NULL, 'q' },
{ "file", required_argument, NULL, 'f' },
{ "files-from", required_argument, NULL, 'T' },
{ "format", required_argument, NULL, OPTION_FORMAT },
@@ -180,6 +191,8 @@
{ "help", no_argument, NULL, OPTION_HELP },
{ "include", required_argument, NULL, OPTION_INCLUDE },
{ "interactive", no_argument, NULL, 'w' },
+ { "insecure", no_argument, NULL, 'P' },
+ { "keep-newer-files", no_argument, NULL, OPTION_KEEP_NEWER_FILES },
{ "keep-old-files", no_argument, NULL, 'k' },
{ "list", no_argument, NULL, 't' },
{ "modification-time", no_argument, NULL, 'm' },
@@ -195,6 +208,7 @@
{ "no-same-owner", no_argument, NULL, OPTION_NO_SAME_OWNER },
{ "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS },
{ "null", no_argument, NULL, OPTION_NULL },
+ { "numeric-owner", no_argument, NULL, OPTION_NUMERIC_OWNER },
{ "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM },
{ "posix", no_argument, NULL, OPTION_POSIX },
{ "preserve-permissions", no_argument, NULL, 'p' },
@@ -203,6 +217,7 @@
{ "strip-components", required_argument, NULL, OPTION_STRIP_COMPONENTS },
{ "to-stdout", no_argument, NULL, 'O' },
{ "totals", no_argument, NULL, OPTION_TOTALS },
+ { "uncompress", no_argument, NULL, 'Z' },
{ "unlink", no_argument, NULL, 'U' },
{ "unlink-first", no_argument, NULL, 'U' },
{ "update", no_argument, NULL, 'u' },
@@ -314,6 +329,9 @@
case OPTION_CHECK_LINKS: /* GNU tar */
bsdtar->option_warn_links = 1;
break;
+ case OPTION_CHROOT: /* NetBSD */
+ bsdtar->option_chroot = 1;
+ break;
case OPTION_EXCLUDE: /* GNU tar */
if (exclude(bsdtar, optarg))
bsdtar_errc(bsdtar, 1, 0,
@@ -327,9 +345,6 @@
if (strcmp(bsdtar->filename, "-") == 0)
bsdtar->filename = NULL;
break;
- case OPTION_FAST_READ: /* GNU tar */
- bsdtar->option_fast_read = 1;
- break;
case 'H': /* BSD convention */
bsdtar->symlink_mode = 'H';
break;
@@ -381,22 +396,15 @@
case 'k': /* GNU tar */
bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
break;
+ case OPTION_KEEP_NEWER_FILES: /* GNU tar */
+ bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
+ break;
case 'L': /* BSD convention */
bsdtar->symlink_mode = 'L';
break;
- case 'l': /* SUSv2 and GNU conflict badly here */
- if (getenv("POSIXLY_CORRECT") != NULL) {
- /* User has asked for POSIX/SUS behavior. */
- bsdtar->option_warn_links = 1;
- } else {
- fprintf(stderr,
-"Error: -l has different behaviors in different tar programs.\n");
- fprintf(stderr,
-" For the GNU behavior, use --one-file-system instead.\n");
- fprintf(stderr,
-" For the POSIX behavior, use --check-links instead.\n");
- usage(bsdtar);
- }
+ case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
+ /* GNU tar 1.13 used -l for --one-file-system */
+ bsdtar->option_warn_links = 1;
break;
case 'm': /* SUSv2 */
bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
@@ -454,6 +462,9 @@
case OPTION_NULL: /* GNU tar */
bsdtar->option_null++;
break;
+ case OPTION_NUMERIC_OWNER: /* GNU tar */
+ bsdtar->option_numeric_owner++;
+ break;
case 'O': /* GNU tar */
bsdtar->option_stdout = 1;
break;
@@ -487,9 +498,23 @@
case OPTION_POSIX: /* GNU tar */
bsdtar->create_format = "pax";
break;
+ case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
+ bsdtar->option_fast_read = 1;
+ break;
case 'r': /* SUSv2 */
set_mode(bsdtar, opt);
break;
+ case 'S': /* NetBSD pax-as-tar */
+ bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE;
+ break;
+ case 's': /* NetBSD pax-as-tar */
+#if HAVE_REGEX_H
+ add_substitution(bsdtar, optarg);
+#else
+ bsdtar_warnc(bsdtar, 0, "-s is not supported by this version of bsdtar");
+ usage(bsdtar);
+#endif
+ break;
case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
bsdtar->strip_components = atoi(optarg);
break;
@@ -628,17 +653,13 @@
only_mode(bsdtar, "--check-links", "cr");
/* Check other parameters only permitted in certain modes. */
- if (bsdtar->create_compression == 'Z' && bsdtar->mode == 'c') {
- bsdtar_warnc(bsdtar, 0, ".Z compression not supported");
- usage(bsdtar);
- }
if (bsdtar->create_compression != '\0') {
strcpy(buff, "-?");
buff[1] = bsdtar->create_compression;
only_mode(bsdtar, buff, "cxt");
}
if (bsdtar->create_format != NULL)
- only_mode(bsdtar, "--format", "c");
+ only_mode(bsdtar, "--format", "cru");
if (bsdtar->symlink_mode != '\0') {
strcpy(buff, "-?");
buff[1] = bsdtar->symlink_mode;
@@ -669,6 +690,10 @@
}
cleanup_exclusions(bsdtar);
+#if HAVE_REGEX_H
+ cleanup_substitution(bsdtar);
+#endif
+
if (bsdtar->return_value != 0)
bsdtar_warnc(bsdtar, 0,
"Error exit delayed from previous errors.");
@@ -721,8 +746,8 @@
const char *p;
char *src, *dest;
- if (src_argv[0] == NULL ||
- src_argv[1] == NULL || src_argv[1][0] == '-')
+ if (src_argv[0] == NULL || src_argv[1] == NULL ||
+ src_argv[1][0] == '-' || src_argv[1][0] == '\0')
return (src_argv);
*argc += strlen(src_argv[1]) - 1;
@@ -785,8 +810,10 @@
static void
version(void)
{
- printf("bsdtar %s - %s\n", PACKAGE_VERSION, archive_version());
- exit(1);
+ printf("bsdtar %s - %s\n",
+ BSDTAR_VERSION_STRING,
+ archive_version());
+ exit(0);
}
static const char *long_help_msg =
Index: Makefile
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/Makefile,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/Makefile -L usr.bin/tar/Makefile -u -r1.3 -r1.4
--- usr.bin/tar/Makefile
+++ usr.bin/tar/Makefile
@@ -1,13 +1,12 @@
-# $FreeBSD: src/usr.bin/tar/Makefile,v 1.32 2007/07/20 01:24:49 kientzle Exp $
-# $MidnightBSD$
+# $FreeBSD: src/usr.bin/tar/Makefile,v 1.32.2.4 2008/08/25 02:18:12 kientzle Exp $
PROG= bsdtar
-VERSION= 2.2.5
-SRCS= bsdtar.c getdate.y matching.c read.c tree.c util.c write.c
+BSDTAR_VERSION_STRING=2.5.5
+SRCS= bsdtar.c getdate.y matching.c read.c siginfo.c subst.c tree.c util.c write.c
WARNS?= 5
DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
LDADD= -larchive -lbz2 -lz
-CFLAGS+= -DPACKAGE_VERSION=\"${VERSION}\"
+CFLAGS+= -DBSDTAR_VERSION_STRING=\"${BSDTAR_VERSION_STRING}\"
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_midnightbsd.h\"
CFLAGS+= -I${.CURDIR}
SYMLINKS= bsdtar ${BINDIR}/tar
Index: tree.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/tree.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/tree.c -L usr.bin/tar/tree.c -u -r1.3 -r1.4
--- usr.bin/tar/tree.c
+++ usr.bin/tar/tree.c
@@ -44,7 +44,6 @@
*/
#include "bsdtar_platform.h"
__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.8 2007/03/11 10:36:42 kientzle Exp $");
-__MBSDID("$MidnightBSD$");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
Index: read.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/read.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/read.c -L usr.bin/tar/read.c -u -r1.3 -r1.4
--- usr.bin/tar/read.c
+++ usr.bin/tar/read.c
@@ -24,8 +24,7 @@
*/
#include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.34 2007/07/20 01:24:49 kientzle Exp $");
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.34.2.5 2008/08/27 04:59:00 kientzle Exp $");
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -78,12 +77,28 @@
tar_mode_t(struct bsdtar *bsdtar)
{
read_archive(bsdtar, 't');
+ unmatched_inclusions_warn(bsdtar, "Not found in archive");
}
void
tar_mode_x(struct bsdtar *bsdtar)
{
+ /* We want to catch SIGINFO and SIGUSR1. */
+ siginfo_init(bsdtar);
+
read_archive(bsdtar, 'x');
+
+ unmatched_inclusions_warn(bsdtar, "Not found in archive");
+ /* Restore old SIGINFO + SIGUSR1 handlers. */
+ siginfo_done(bsdtar);
+}
+
+static void
+progress_func(void * cookie)
+{
+ struct bsdtar * bsdtar = cookie;
+
+ siginfo_printinfo(bsdtar, 0);
}
/*
@@ -119,6 +134,23 @@
archive_error_string(a));
do_chdir(bsdtar);
+
+ if (mode == 'x') {
+ /* Set an extract callback so that we can handle SIGINFO. */
+ archive_read_extract_set_progress_callback(a, progress_func,
+ bsdtar);
+ }
+
+ if (mode == 'x' && bsdtar->option_chroot) {
+#if HAVE_CHROOT
+ if (chroot(".") != 0)
+ bsdtar_errc(bsdtar, 1, errno, "Can't chroot to \".\"");
+#else
+ bsdtar_errc(bsdtar, 1, 0,
+ "chroot isn't supported on this platform");
+#endif
+ }
+
for (;;) {
/* Support --fast-read option */
if (bsdtar->option_fast_read &&
@@ -140,6 +172,11 @@
if (r == ARCHIVE_FATAL)
break;
+ if (bsdtar->option_numeric_owner) {
+ archive_entry_set_uname(entry, NULL);
+ archive_entry_set_gname(entry, NULL);
+ }
+
/*
* Exclude entries that are too old.
*/
@@ -173,22 +210,17 @@
if (excluded(bsdtar, archive_entry_pathname(entry)))
continue; /* Excluded by a pattern test. */
- /*
- * Modify the pathname as requested by the user. We
- * do this for -t as well to give users a way to
- * preview the effects of their rewrites. We also do
- * this before extraction security checks (including
- * leading '/' removal). Note that some rewrite
- * failures prevent extraction.
- */
- if (edit_pathname(bsdtar, entry))
- continue; /* Excluded by a rewrite failure. */
-
if (mode == 't') {
/* Perversely, gtar uses -O to mean "send to stderr"
* when used with -t. */
out = bsdtar->option_stdout ? stderr : stdout;
+ /*
+ * TODO: Provide some reasonable way to
+ * preview rewrites. gtar always displays
+ * the unedited path in -t output, which means
+ * you cannot easily preview rewrites.
+ */
if (bsdtar->verbose < 2)
safe_fprintf(out, "%s",
archive_entry_pathname(entry));
@@ -215,6 +247,10 @@
}
fprintf(out, "\n");
} else {
+ /* Note: some rewrite failures prevent extraction. */
+ if (edit_pathname(bsdtar, entry))
+ continue; /* Excluded by a rewrite failure. */
+
if (bsdtar->option_interactive &&
!yes("extract '%s'", archive_entry_pathname(entry)))
continue;
@@ -228,6 +264,12 @@
archive_entry_pathname(entry));
fflush(stderr);
}
+
+ /* Tell the SIGINFO-handler code what we're doing. */
+ siginfo_setinfo(bsdtar, "extracting",
+ archive_entry_pathname(entry), 0);
+ siginfo_printinfo(bsdtar, 0);
+
if (bsdtar->option_stdout)
r = archive_read_data_into_fd(a, 1);
else
@@ -292,8 +334,9 @@
}
if (!now)
time(&now);
- bsdtar_strmode(entry, tmp);
- fprintf(out, "%s %d ", tmp, (int)(st->st_nlink));
+ fprintf(out, "%s %d ",
+ archive_entry_strmode(entry),
+ (int)(st->st_nlink));
/* Use uname if it's present, else uid. */
p = archive_entry_uname(entry);
@@ -344,7 +387,7 @@
if (abs(tim - now) > (365/2)*86400)
fmt = bsdtar->day_first ? "%e %b %Y" : "%b %e %Y";
else
- fmt = bsdtar->day_first ? "%e %b %R" : "%b %e %R";
+ fmt = bsdtar->day_first ? "%e %b %H:%M" : "%b %e %H:%M";
strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
fprintf(out, " %s ", tmp);
safe_fprintf(out, "%s", archive_entry_pathname(entry));
Index: bsdtar_platform.h
===================================================================
RCS file: /home/cvs/src/usr.bin/tar/bsdtar_platform.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -L usr.bin/tar/bsdtar_platform.h -L usr.bin/tar/bsdtar_platform.h -u -r1.3 -r1.4
--- usr.bin/tar/bsdtar_platform.h
+++ usr.bin/tar/bsdtar_platform.h
@@ -22,8 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.24 2007/04/12 04:45:32 kientzle Exp $
- * $MidnightBSD$
+ * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.24.2.1 2008/02/10 23:24:16 kientzle Exp $
*/
/*
@@ -50,7 +49,8 @@
#ifdef __FreeBSD__
#include <sys/cdefs.h> /* For __FBSDID */
#else
-#define __FBSDID(a) /* null */
+/* Just leaving this macro replacement empty leads to a dangling semicolon. */
+#define __FBSDID(a) struct _undefined_hack
#endif
#ifdef HAVE_LIBARCHIVE
--- /dev/null
+++ usr.bin/tar/test/test_version.c
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_version.c,v 1.2.2.1 2008/08/10 06:53:28 kientzle Exp $");
+
+/*
+ * Test that --version option works and generates reasonable output.
+ */
+
+DEFINE_TEST(test_version)
+{
+ int r;
+ char *p, *q;
+ size_t s;
+
+
+ r = systemf("%s --version >version.stdout 2>version.stderr", testprog);
+ if (r != 0)
+ r = systemf("%s -W version >version.stdout 2>version.stderr",
+ testprog);
+ failure("Unable to run either %s --version or %s -W version",
+ testprog, testprog);
+ if (!assert(r == 0))
+ return;
+
+ /* --version should generate nothing to stdout. */
+ assertEmptyFile("version.stderr");
+ /* Verify format of version message. */
+ q = p = slurpfile(&s, "version.stdout");
+ /* Version message should start with name of program, then space. */
+ assert(s > 6);
+ failure("Version: %s", p);
+ assertEqualMem(q, "bsdtar ", 7);
+ q += 7; s -= 7;
+ /* Version number is a series of digits and periods. */
+ while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
+ ++q;
+ --s;
+ }
+ /* Version number terminated by space. */
+ failure("Version: %s", p);
+ assert(s > 1);
+ /* Skip a single trailing a,b,c, or d. */
+ if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
+ ++q;
+ failure("Version: %s", p);
+ assert(*q == ' ');
+ ++q; --s;
+ /* Separator. */
+ failure("Version: %s", p);
+ assertEqualMem(q, "- ", 2);
+ q += 2; s -= 2;
+ /* libarchive name and version number */
+ failure("Version: %s", p);
+ assert(s > 11);
+ failure("Version: %s", p);
+ assertEqualMem(q, "libarchive ", 11);
+ q += 11; s -= 11;
+ /* Version number is a series of digits and periods. */
+ while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
+ ++q;
+ --s;
+ }
+ /* Skip a single trailing a,b,c, or d. */
+ if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
+ ++q;
+ /* All terminated by a newline. */
+ assert(s >= 1);
+ assertEqualMem(q, "\n", 1);
+ free(p);
+}
--- /dev/null
+++ usr.bin/tar/test/test_copy.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_copy.c,v 1.2.2.3 2008/08/25 02:01:47 kientzle Exp $");
+
+static void
+create_tree(void)
+{
+ char buff[260];
+ char buff2[260];
+ int i;
+ int fd;
+
+ assertEqualInt(0, mkdir("original", 0775));
+ chdir("original");
+ assertEqualInt(0, mkdir("f", 0775));
+ assertEqualInt(0, mkdir("l", 0775));
+ assertEqualInt(0, mkdir("m", 0775));
+ assertEqualInt(0, mkdir("s", 0775));
+ assertEqualInt(0, mkdir("d", 0775));
+
+ for (i = 0; i < 200; i++) {
+ buff[0] = 'f';
+ buff[1] = '/';
+ /* Create a file named "f/abcdef..." */
+ buff[i + 2] = 'a' + (i % 26);
+ buff[i + 3] = '\0';
+ fd = open(buff, O_CREAT | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(i + 3, write(fd, buff, strlen(buff)));
+ close(fd);
+
+ /* Create a link named "l/abcdef..." to the above. */
+ strcpy(buff2, buff);
+ buff2[0] = 'l';
+ assertEqualInt(0, link(buff, buff2));
+
+ /* Create a link named "m/abcdef..." to the above. */
+ strcpy(buff2, buff);
+ buff2[0] = 'm';
+ assertEqualInt(0, link(buff, buff2));
+
+ /* Create a symlink named "s/abcdef..." to the above. */
+ strcpy(buff2 + 3, buff);
+ buff[0] = 's';
+ buff2[0] = '.';
+ buff2[1] = '.';
+ buff2[2] = '/';
+ assertEqualInt(0, symlink(buff2, buff));
+
+ /* Create a dir named "d/abcdef...". */
+ buff[0] = 'd';
+ assertEqualInt(0, mkdir(buff, 0775));
+ }
+
+ chdir("..");
+}
+
+#define LIMIT_NONE 0
+#define LIMIT_USTAR 1
+
+static void
+verify_tree(int limit)
+{
+ struct stat st, st2;
+ char filename[260];
+ char name1[260];
+ char name2[260];
+ char contents[260];
+ int i, j, r;
+ int fd;
+ int len;
+ const char *p, *dp;
+ DIR *d;
+ struct dirent *de;
+
+ /* Generate the names we know should be there and verify them. */
+ for (i = 1; i < 200; i++) {
+ /* Generate a base name of the correct length. */
+ for (j = 0; j < i; ++j)
+ filename[j] = 'a' + (j % 26);
+#if 0
+ for (n = i; n > 0; n /= 10)
+ filename[--j] = '0' + (n % 10);
+#endif
+ filename[i] = '\0';
+
+ /* Verify a file named "f/abcdef..." */
+ strcpy(name1, "f/");
+ strcat(name1, filename);
+ if (limit != LIMIT_USTAR || strlen(filename) <= 100) {
+ fd = open(name1, O_RDONLY);
+ failure("Couldn't open \"%s\": %s",
+ name1, strerror(errno));
+ if (assert(fd >= 0)) {
+ len = read(fd, contents, i + 10);
+ close(fd);
+ assertEqualInt(len, i + 2);
+ /* Verify contents of 'contents' */
+ contents[len] = '\0';
+ failure("Each test file contains its own name");
+ assertEqualString(name1, contents);
+ /* stat() for dev/ino for next check */
+ assertEqualInt(0, lstat(name1, &st));
+ }
+ }
+
+ /*
+ * ustar allows 100 chars for links, and we have
+ * "original/" as part of the name, so the link
+ * names here can't exceed 91 chars.
+ */
+ strcpy(name2, "l/");
+ strcat(name2, filename);
+ if (limit != LIMIT_USTAR || strlen(name2) <= 100) {
+ /* Verify hardlink "l/abcdef..." */
+ assertEqualInt(0, (r = lstat(name2, &st2)));
+ if (r == 0) {
+ assertEqualInt(st2.st_dev, st.st_dev);
+ assertEqualInt(st2.st_ino, st.st_ino);
+ }
+
+ /* Verify hardlink "m_abcdef..." */
+ name2[0] = 'm';
+ assertEqualInt(0, (r = lstat(name2, &st2)));
+ if (r == 0) {
+ assertEqualInt(st2.st_dev, st.st_dev);
+ assertEqualInt(st2.st_ino, st.st_ino);
+ }
+ }
+
+ /*
+ * Symlink text doesn't include the 'original/' prefix,
+ * so the limit here is 100 characters.
+ */
+ /* Verify symlink "s/abcdef..." */
+ strcpy(name2, "../s/");
+ strcat(name2, filename);
+ if (limit != LIMIT_USTAR || strlen(name2) <= 100) {
+ /* This is a symlink. */
+ failure("Couldn't stat %s (length %d)",
+ filename, strlen(filename));
+ if (assertEqualInt(0, lstat(name2 + 3, &st2))) {
+ assert(S_ISLNK(st2.st_mode));
+ /* This is a symlink to the file above. */
+ failure("Couldn't stat %s", name2 + 3);
+ if (assertEqualInt(0, stat(name2 + 3, &st2))) {
+ assertEqualInt(st2.st_dev, st.st_dev);
+ assertEqualInt(st2.st_ino, st.st_ino);
+ }
+ }
+ }
+
+ /* Verify dir "d/abcdef...". */
+ strcpy(name1, "d/");
+ strcat(name1, filename);
+ if (limit != LIMIT_USTAR || strlen(filename) < 100) {
+ /* This is a dir. */
+ failure("Couldn't stat %s (length %d)",
+ name1, strlen(filename));
+ if (assertEqualInt(0, lstat(name1, &st2))) {
+ if (assert(S_ISDIR(st2.st_mode))) {
+ /* TODO: opendir/readdir this
+ * directory and make sure
+ * it's empty.
+ */
+ }
+ }
+ }
+ }
+
+ /* Now make sure nothing is there that shouldn't be. */
+ for (dp = "dflms"; *dp != '\0'; ++dp) {
+ char dir[2];
+ dir[0] = *dp; dir[1] = '\0';
+ d = opendir(dir);
+ failure("Unable to open dir '%s'", dir);
+ if (!assert(d != NULL))
+ continue;
+ while ((de = readdir(d)) != NULL) {
+ p = de->d_name;
+ switch(dp[0]) {
+ case 'l': case 'm':
+ if (limit == LIMIT_USTAR) {
+ failure("strlen(p) = %d", strlen(p));
+ assert(strlen(p) <= 100);
+ }
+ case 'd':
+ if (limit == LIMIT_USTAR) {
+ failure("strlen(p)=%d", strlen(p));
+ assert(strlen(p) < 100);
+ }
+ case 'f': case 's':
+ if (limit == LIMIT_USTAR) {
+ failure("strlen(p)=%d", strlen(p));
+ assert(strlen(p) < 101);
+ }
+ /* Our files have very particular filename patterns. */
+ if (p[0] != '.' || (p[1] != '.' && p[1] != '\0')) {
+ for (i = 0; p[i] != '\0' && i < 200; i++) {
+ failure("i=%d, p[i]='%c' 'a'+(i%%26)='%c'", i, p[i], 'a' + (i % 26));
+ assertEqualInt(p[i], 'a' + (i % 26));
+ }
+ assert(p[i] == '\0');
+ }
+ break;
+ case '.':
+ assert(p[1] == '\0' || (p[1] == '.' && p[2] == '\0'));
+ break;
+ default:
+ failure("File %s shouldn't be here", p);
+ assert(0);
+ }
+ }
+ closedir(d);
+ }
+}
+
+static void
+copy_basic(void)
+{
+ int r;
+
+ assertEqualInt(0, mkdir("plain", 0775));
+ assertEqualInt(0, chdir("plain"));
+
+ /*
+ * Use the tar program to create an archive.
+ */
+ r = systemf("%s cf archive -C ../original f d l m s >pack.out 2>pack.err",
+ testprog);
+ failure("Error invoking \"%s cf\"", testprog);
+ assertEqualInt(r, 0);
+
+ /* Verify that nothing went to stdout or stderr. */
+ assertEmptyFile("pack.err");
+ assertEmptyFile("pack.out");
+
+ /*
+ * Use tar to unpack the archive into another directory.
+ */
+ r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
+ failure("Error invoking %s xf archive", testprog);
+ assertEqualInt(r, 0);
+
+ /* Verify that nothing went to stdout or stderr. */
+ assertEmptyFile("unpack.err");
+ assertEmptyFile("unpack.out");
+
+ verify_tree(LIMIT_NONE);
+ assertEqualInt(0, chdir(".."));
+}
+
+static void
+copy_ustar(void)
+{
+ const char *target = "ustar";
+ int r;
+
+ assertEqualInt(0, mkdir(target, 0775));
+ assertEqualInt(0, chdir(target));
+
+ /*
+ * Use the tar program to create an archive.
+ */
+ r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err",
+ testprog);
+ failure("Error invoking \"%s cf archive --format=ustar\"", testprog);
+ assertEqualInt(r, 0);
+
+ /* Verify that nothing went to stdout. */
+ assertEmptyFile("pack.out");
+ /* Stderr is non-empty, since there are a bunch of files
+ * with filenames too long to archive. */
+
+ /*
+ * Use tar to unpack the archive into another directory.
+ */
+ r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
+ failure("Error invoking %s xf archive", testprog);
+ assertEqualInt(r, 0);
+
+ /* Verify that nothing went to stdout or stderr. */
+ assertEmptyFile("unpack.err");
+ assertEmptyFile("unpack.out");
+
+ chdir("original");
+ verify_tree(LIMIT_USTAR);
+ chdir("../..");
+}
+
+DEFINE_TEST(test_copy)
+{
+ int oldumask;
+
+ oldumask = umask(0);
+
+ create_tree(); /* Create sample files in "original" dir. */
+
+ /* Test simple "tar -c | tar -x" pipeline copy. */
+ copy_basic();
+
+ /* Same, but constrain to ustar format. */
+ copy_ustar();
+
+ umask(oldumask);
+}
--- /dev/null
+++ usr.bin/tar/test/test_stdio.c
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_stdio.c,v 1.2.2.1 2008/08/10 06:53:28 kientzle Exp $");
+
+DEFINE_TEST(test_stdio)
+{
+ int fd;
+ int filelist;
+ int oldumask;
+ int r;
+
+ oldumask = umask(0);
+
+ /*
+ * Create a couple of files on disk.
+ */
+ filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+ /* File */
+ fd = open("f", O_CREAT | O_WRONLY, 0644);
+ assert(fd >= 0);
+ write(fd, "f\n", 2);
+ close(fd);
+ write(filelist, "f\n", 2);
+ /* Link to above file. */
+ assertEqualInt(0, link("f", "l"));
+ write(filelist, "l\n", 2);
+ close(filelist);
+
+ /*
+ * Archive/dearchive with a variety of options, verifying
+ * stdio paths.
+ */
+
+ /* 'cf' should generate no output unless there's an error. */
+ r = systemf("%s cf archive f l >cf.out 2>cf.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("cf.out");
+ assertEmptyFile("cf.err");
+
+ /* 'cvf' should generate file list on stderr, empty stdout. */
+ r = systemf("%s cvf archive f l >cvf.out 2>cvf.err", testprog);
+ assertEqualInt(r, 0);
+ failure("'cv' writes filenames to stderr, nothing to stdout (SUSv2)\n"
+ "Note that GNU tar writes the file list to stdout by default.");
+ assertEmptyFile("cvf.out");
+ /* TODO: Verify cvf.err has file list in SUSv2-prescribed format. */
+
+ /* 'cvf -' should generate file list on stderr, archive on stdout. */
+ r = systemf("%s cvf - f l >cvf-.out 2>cvf-.err", testprog);
+ assertEqualInt(r, 0);
+ failure("cvf - should write archive to stdout");
+ /* TODO: Verify cvf-.out has archive. */
+ failure("cvf - should write file list to stderr (SUSv2)");
+ /* TODO: Verify cvf-.err has verbose file list. */
+
+ /* 'tf' should generate file list on stdout, empty stderr. */
+ r = systemf("%s tf archive >tf.out 2>tf.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("tf.err");
+ failure("'t' mode should write results to stdout");
+ /* TODO: Verify tf.out has file list. */
+
+ /* 'tvf' should generate file list on stdout, empty stderr. */
+ r = systemf("%s tvf archive >tvf.out 2>tvf.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("tvf.err");
+ failure("'tv' mode should write results to stdout");
+ /* TODO: Verify tvf.out has file list. */
+
+ /* 'tvf -' uses stdin, file list on stdout, empty stderr. */
+ r = systemf("%s tvf - < archive >tvf-.out 2>tvf-.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("tvf-.err");
+ /* TODO: Verify tvf-.out has file list. */
+
+ /* Basic 'xf' should generate no output on stdout or stderr. */
+ r = systemf("%s xf archive >xf.out 2>xf.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("xf.err");
+ assertEmptyFile("xf.out");
+
+ /* 'xvf' should generate list on stderr, empty stdout. */
+ r = systemf("%s xvf archive >xvf.out 2>xvf.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("xvf.out");
+ /* TODO: Verify xvf.err */
+
+ /* 'xvOf' should generate list on stderr, file contents on stdout. */
+ r = systemf("%s xvOf archive >xvOf.out 2>xvOf.err", testprog);
+ assertEqualInt(r, 0);
+ /* TODO: Verify xvOf.out */
+ /* TODO: Verify xvf.err */
+
+ /* 'xvf -' should generate list on stderr, empty stdout. */
+ r = systemf("%s xvf - < archive >xvf-.out 2>xvf-.err", testprog);
+ assertEqualInt(r, 0);
+ assertEmptyFile("xvf-.out");
+ /* TODO: Verify xvf-.err */
+
+ umask(oldumask);
+}
--- /dev/null
+++ usr.bin/tar/test/test_strip_components.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_strip_components.c,v 1.2.4.1 2008/12/11 05:56:47 kientzle Exp $");
+
+static int
+touch(const char *fn)
+{
+ int fd = open(fn, O_RDWR | O_CREAT | 0644);
+ failure("Couldn't create file '%s', fd=%d, errno=%d (%s)\n",
+ fn, fd, errno, strerror(errno));
+ if (!assert(fd > 0))
+ return (0); /* Failure. */
+ close(fd);
+ return (1); /* Success */
+}
+
+DEFINE_TEST(test_strip_components)
+{
+ struct stat st;
+
+ assertEqualInt(0, mkdir("d0", 0755));
+ assertEqualInt(0, chdir("d0"));
+ assertEqualInt(0, mkdir("d1", 0755));
+ assertEqualInt(0, mkdir("d1/d2", 0755));
+ assertEqualInt(0, mkdir("d1/d2/d3", 0755));
+ assertEqualInt(1, touch("d1/d2/f1"));
+ assertEqualInt(0, link("d1/d2/f1", "l1"));
+ assertEqualInt(0, link("d1/d2/f1", "d1/l2"));
+ assertEqualInt(0, symlink("d1/d2/f1", "s1"));
+ assertEqualInt(0, symlink("d2/f1", "d1/s2"));
+ assertEqualInt(0, chdir(".."));
+
+ assertEqualInt(0, systemf("%s -cf test.tar d0", testprog));
+
+ assertEqualInt(0, mkdir("target", 0755));
+ assertEqualInt(0, systemf("%s -x -C target --strip-components 2 "
+ "-f test.tar", testprog));
+
+ failure("d0/ is too short and should not get restored");
+ assertEqualInt(-1, lstat("target/d0", &st));
+ failure("d0/d1/ is too short and should not get restored");
+ assertEqualInt(-1, lstat("target/d1", &st));
+ failure("d0/d1/s2 is a symlink to something that won't be extracted");
+ assertEqualInt(-1, stat("target/s2", &st));
+ assertEqualInt(0, lstat("target/s2", &st));
+ failure("d0/d1/d2 should be extracted");
+ assertEqualInt(0, lstat("target/d2", &st));
+
+ /*
+ * This next is a complicated case. d0/l1, d0/d1/l2, and
+ * d0/d1/d2/f1 are all hardlinks to the same file; d0/l1 can't
+ * be extracted with --strip-components=2 and the other two
+ * can. Remember that tar normally stores the first file with
+ * a body and the other as hardlink entries to the first
+ * appearance. So the final result depends on the order in
+ * which these three names get archived. If d0/l1 is first,
+ * none of the three can be restored. If either of the longer
+ * names are first, then the two longer ones can both be
+ * restored.
+ *
+ * The tree-walking code used by bsdtar always visits files
+ * before subdirectories, so bsdtar's behavior is fortunately
+ * deterministic: d0/l1 will always get stored first and the
+ * other two will be stored as hardlinks to d0/l1. Since
+ * d0/l1 can't be extracted, none of these three will be
+ * extracted.
+ *
+ * It may be worth extending this test to force a particular
+ * archiving order so as to exercise both of the cases described
+ * above.
+ *
+ * Of course, this is all totally different for cpio and newc
+ * formats because the hardlink management is different.
+ * TODO: Rename this to test_strip_components_tar and create
+ * parallel tests for cpio and newc formats.
+ */
+ failure("d0/l1 is too short and should not get restored");
+ assertEqualInt(-1, lstat("target/l1", &st));
+ failure("d0/d1/l2 is a hardlink to file whose name was too short");
+ assertEqualInt(-1, lstat("target/l2", &st));
+ failure("d0/d1/d2/f1 is a hardlink to file whose name was too short");
+ assertEqualInt(-1, lstat("target/d2/f1", &st));
+}
--- /dev/null
+++ usr.bin/tar/test/test_symlink_dir.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_symlink_dir.c,v 1.1.4.1 2008/12/11 05:56:47 kientzle Exp $");
+
+/*
+ * tar -x -P should follow existing symlinks for dirs, but not other
+ * content. Plain tar -x should remove symlinks when they're in the
+ * way of a dir extraction.
+ */
+
+static int
+mkfile(const char *name, int mode, const char *contents, ssize_t size)
+{
+ int fd = open(name, O_CREAT | O_WRONLY, mode);
+ if (fd < 0)
+ return (-1);
+ if (size != write(fd, contents, size)) {
+ close(fd);
+ return (-1);
+ }
+ close(fd);
+ return (0);
+}
+
+DEFINE_TEST(test_symlink_dir)
+{
+ struct stat st, st2;
+ int oldumask;
+
+ oldumask = umask(0);
+
+ assertEqualInt(0, mkdir("source", 0755));
+ assertEqualInt(0, mkfile("source/file", 0755, "a", 1));
+ assertEqualInt(0, mkfile("source/file2", 0755, "ab", 2));
+ assertEqualInt(0, mkdir("source/dir", 0755));
+ assertEqualInt(0, mkdir("source/dir/d", 0755));
+ assertEqualInt(0, mkfile("source/dir/f", 0755, "abc", 3));
+ assertEqualInt(0, mkdir("source/dir2", 0755));
+ assertEqualInt(0, mkdir("source/dir2/d2", 0755));
+ assertEqualInt(0, mkfile("source/dir2/f2", 0755, "abcd", 4));
+ assertEqualInt(0, mkdir("source/dir3", 0755));
+ assertEqualInt(0, mkdir("source/dir3/d3", 0755));
+ assertEqualInt(0, mkfile("source/dir3/f3", 0755, "abcde", 5));
+
+ assertEqualInt(0,
+ systemf("%s -cf test.tar -C source dir dir2 dir3 file file2",
+ testprog));
+
+ /*
+ * Extract with -x and without -P.
+ */
+ assertEqualInt(0, mkdir("dest1", 0755));
+ /* "dir" is a symlink to an existing "real_dir" */
+ assertEqualInt(0, mkdir("dest1/real_dir", 0755));
+ assertEqualInt(0, symlink("real_dir", "dest1/dir"));
+ /* "dir2" is a symlink to a non-existing "real_dir2" */
+ assertEqualInt(0, symlink("real_dir2", "dest1/dir2"));
+ /* "dir3" is a symlink to an existing "non_dir3" */
+ assertEqualInt(0, mkfile("dest1/non_dir3", 0755, "abcdef", 6));
+ assertEqualInt(0, symlink("non_dir3", "dest1/dir3"));
+ /* "file" is a symlink to existing "real_file" */
+ assertEqualInt(0, mkfile("dest1/real_file", 0755, "abcdefg", 7));
+ assertEqualInt(0, symlink("real_file", "dest1/file"));
+ /* "file2" is a symlink to non-existing "real_file2" */
+ assertEqualInt(0, symlink("real_file2", "dest1/file2"));
+
+ assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog));
+
+ /* dest1/dir symlink should be removed */
+ assertEqualInt(0, lstat("dest1/dir", &st));
+ failure("symlink to dir was followed when it shouldn't be");
+ assert(S_ISDIR(st.st_mode));
+ /* dest1/dir2 symlink should be removed */
+ assertEqualInt(0, lstat("dest1/dir2", &st));
+ failure("Broken symlink wasn't replaced with dir");
+ assert(S_ISDIR(st.st_mode));
+ /* dest1/dir3 symlink should be removed */
+ assertEqualInt(0, lstat("dest1/dir3", &st));
+ failure("Symlink to non-dir wasn't replaced with dir");
+ assert(S_ISDIR(st.st_mode));
+ /* dest1/file symlink should be removed */
+ assertEqualInt(0, lstat("dest1/file", &st));
+ failure("Symlink to existing file should be removed");
+ assert(S_ISREG(st.st_mode));
+ /* dest1/file2 symlink should be removed */
+ assertEqualInt(0, lstat("dest1/file2", &st));
+ failure("Symlink to non-existing file should be removed");
+ assert(S_ISREG(st.st_mode));
+
+ /*
+ * Extract with both -x and -P
+ */
+ assertEqualInt(0, mkdir("dest2", 0755));
+ /* "dir" is a symlink to existing "real_dir" */
+ assertEqualInt(0, mkdir("dest2/real_dir", 0755));
+ assertEqualInt(0, symlink("real_dir", "dest2/dir"));
+ /* "dir2" is a symlink to a non-existing "real_dir2" */
+ assertEqualInt(0, symlink("real_dir2", "dest2/dir2"));
+ /* "dir3" is a symlink to an existing "non_dir3" */
+ assertEqualInt(0, mkfile("dest2/non_dir3", 0755, "abcdefgh", 8));
+ assertEqualInt(0, symlink("non_dir3", "dest2/dir3"));
+ /* "file" is a symlink to existing "real_file" */
+ assertEqualInt(0, mkfile("dest2/real_file", 0755, "abcdefghi", 9));
+ assertEqualInt(0, symlink("real_file", "dest2/file"));
+ /* "file2" is a symlink to non-existing "real_file2" */
+ assertEqualInt(0, symlink("real_file2", "dest2/file2"));
+
+ assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog));
+
+ /* dest2/dir symlink should be followed */
+ assertEqualInt(0, lstat("dest2/dir", &st));
+ failure("tar -xP removed symlink instead of following it");
+ if (assert(S_ISLNK(st.st_mode))) {
+ /* Only verify what the symlink points to if it
+ * really is a symlink. */
+ failure("The symlink should point to a directory");
+ assertEqualInt(0, stat("dest2/dir", &st));
+ assert(S_ISDIR(st.st_mode));
+ failure("The pre-existing directory should still be there");
+ assertEqualInt(0, lstat("dest2/real_dir", &st2));
+ assert(S_ISDIR(st2.st_mode));
+ assertEqualInt(st.st_dev, st2.st_dev);
+ failure("symlink should still point to the existing directory");
+ assertEqualInt(st.st_ino, st2.st_ino);
+ }
+ /* Contents of 'dir' should be restored */
+ assertEqualInt(0, lstat("dest2/dir/d", &st));
+ assert(S_ISDIR(st.st_mode));
+ assertEqualInt(0, lstat("dest2/dir/f", &st));
+ assert(S_ISREG(st.st_mode));
+ assertEqualInt(3, st.st_size);
+ /* dest2/dir2 symlink should be removed */
+ assertEqualInt(0, lstat("dest2/dir2", &st));
+ failure("Broken symlink wasn't replaced with dir");
+ assert(S_ISDIR(st.st_mode));
+ /* dest2/dir3 symlink should be removed */
+ assertEqualInt(0, lstat("dest2/dir3", &st));
+ failure("Symlink to non-dir wasn't replaced with dir");
+ assert(S_ISDIR(st.st_mode));
+ /* dest2/file symlink should be removed;
+ * even -P shouldn't follow symlinks for files */
+ assertEqualInt(0, lstat("dest2/file", &st));
+ failure("Symlink to existing file should be removed");
+ assert(S_ISREG(st.st_mode));
+ /* dest2/file2 symlink should be removed */
+ assertEqualInt(0, lstat("dest2/file2", &st));
+ failure("Symlink to non-existing file should be removed");
+ assert(S_ISREG(st.st_mode));
+}
--- /dev/null
+++ usr.bin/tar/test/main.c
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Various utility routines useful for test programs.
+ * Each test program is linked against this file.
+ */
+#include "test.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <time.h>
+
+/*
+ * This same file is used pretty much verbatim for all test harnesses.
+ *
+ * The next few lines are the only differences.
+ */
+#define PROGRAM "bsdtar" /* Name of program being tested. */
+#define ENVBASE "BSDTAR" /* Prefix for environment variables. */
+#undef EXTRA_DUMP /* How to dump extra data */
+/* How to generate extra version info. */
+#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "")
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.3.2.3 2008/08/27 04:59:00 kientzle Exp $");
+
+/*
+ * "list.h" is simply created by "grep DEFINE_TEST"; it has
+ * a line like
+ * DEFINE_TEST(test_function)
+ * for each test.
+ * Include it here with a suitable DEFINE_TEST to declare all of the
+ * test functions.
+ */
+#undef DEFINE_TEST
+#define DEFINE_TEST(name) void name(void);
+#include "list.h"
+
+/* Interix doesn't define these in a standard header. */
+#if __INTERIX__
+extern char *optarg;
+extern int optind;
+#endif
+
+/* Enable core dump on failure. */
+static int dump_on_failure = 0;
+/* Default is to remove temp dirs for successful tests. */
+static int keep_temp_files = 0;
+/* Default is to print some basic information about each test. */
+static int quiet_flag = 0;
+/* Default is to summarize repeated failures. */
+static int verbose = 0;
+/* Cumulative count of component failures. */
+static int failures = 0;
+/* Cumulative count of skipped component tests. */
+static int skips = 0;
+/* Cumulative count of assertions. */
+static int assertions = 0;
+
+/* Directory where uuencoded reference files can be found. */
+static char *refdir;
+
+/*
+ * My own implementation of the standard assert() macro emits the
+ * message in the same format as GCC (file:line: message).
+ * It also includes some additional useful information.
+ * This makes it a lot easier to skim through test failures in
+ * Emacs. ;-)
+ *
+ * It also supports a few special features specifically to simplify
+ * test harnesses:
+ * failure(fmt, args) -- Stores a text string that gets
+ * printed if the following assertion fails, good for
+ * explaining subtle tests.
+ */
+static char msg[4096];
+
+/*
+ * For each test source file, we remember how many times each
+ * failure was reported.
+ */
+static const char *failed_filename = NULL;
+static struct line {
+ int line;
+ int count;
+} failed_lines[1000];
+
+/*
+ * Count this failure; return the number of previous failures.
+ */
+static int
+previous_failures(const char *filename, int line)
+{
+ unsigned int i;
+ int count;
+
+ if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
+ memset(failed_lines, 0, sizeof(failed_lines));
+ failed_filename = filename;
+
+ for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
+ if (failed_lines[i].line == line) {
+ count = failed_lines[i].count;
+ failed_lines[i].count++;
+ return (count);
+ }
+ if (failed_lines[i].line == 0) {
+ failed_lines[i].line = line;
+ failed_lines[i].count = 1;
+ return (0);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Copy arguments into file-local variables.
+ */
+static const char *test_filename;
+static int test_line;
+static void *test_extra;
+void test_setup(const char *filename, int line)
+{
+ test_filename = filename;
+ test_line = line;
+}
+
+/*
+ * Inform user that we're skipping a test.
+ */
+void
+test_skipping(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (previous_failures(test_filename, test_line))
+ return;
+
+ va_start(ap, fmt);
+ fprintf(stderr, " *** SKIPPING: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ ++skips;
+}
+
+/* Common handling of failed tests. */
+static void
+report_failure(void *extra)
+{
+ if (msg[0] != '\0') {
+ fprintf(stderr, " Description: %s\n", msg);
+ msg[0] = '\0';
+ }
+
+#ifdef EXTRA_DUMP
+ if (extra != NULL)
+ fprintf(stderr, " detail: %s\n", EXTRA_DUMP(extra));
+#else
+ (void)extra; /* UNUSED */
+#endif
+
+ if (dump_on_failure) {
+ fprintf(stderr,
+ " *** forcing core dump so failure can be debugged ***\n");
+ *(char *)(NULL) = 0;
+ exit(1);
+ }
+}
+
+/*
+ * Summarize repeated failures in the just-completed test file.
+ * The reports above suppress multiple failures from the same source
+ * line; this reports on any tests that did fail multiple times.
+ */
+static int
+summarize_comparator(const void *a0, const void *b0)
+{
+ const struct line *a = a0, *b = b0;
+ if (a->line == 0 && b->line == 0)
+ return (0);
+ if (a->line == 0)
+ return (1);
+ if (b->line == 0)
+ return (-1);
+ return (a->line - b->line);
+}
+
+static void
+summarize(void)
+{
+ unsigned int i;
+
+ qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
+ sizeof(failed_lines[0]), summarize_comparator);
+ for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
+ if (failed_lines[i].line == 0)
+ break;
+ if (failed_lines[i].count > 1)
+ fprintf(stderr, "%s:%d: Failed %d times\n",
+ failed_filename, failed_lines[i].line,
+ failed_lines[i].count);
+ }
+ /* Clear the failure history for the next file. */
+ memset(failed_lines, 0, sizeof(failed_lines));
+}
+
+/* Set up a message to display only after a test fails. */
+void
+failure(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(msg, fmt, ap);
+ va_end(ap);
+}
+
+/* Generic assert() just displays the failed condition. */
+int
+test_assert(const char *file, int line, int value, const char *condition, void *extra)
+{
+ ++assertions;
+ if (value) {
+ msg[0] = '\0';
+ return (value);
+ }
+ failures ++;
+ if (!verbose && previous_failures(file, line))
+ return (value);
+ fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
+ fprintf(stderr, " Condition: %s\n", condition);
+ report_failure(extra);
+ return (value);
+}
+
+/* assertEqualInt() displays the values of the two integers. */
+int
+test_assert_equal_int(const char *file, int line,
+ int v1, const char *e1, int v2, const char *e2, void *extra)
+{
+ ++assertions;
+ if (v1 == v2) {
+ msg[0] = '\0';
+ return (1);
+ }
+ failures ++;
+ if (!verbose && previous_failures(file, line))
+ return (0);
+ fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
+ file, line);
+ fprintf(stderr, " %s=%d\n", e1, v1);
+ fprintf(stderr, " %s=%d\n", e2, v2);
+ report_failure(extra);
+ return (0);
+}
+
+static void strdump(const char *p)
+{
+ if (p == NULL) {
+ fprintf(stderr, "(null)");
+ return;
+ }
+ fprintf(stderr, "\"");
+ while (*p != '\0') {
+ unsigned int c = 0xff & *p++;
+ switch (c) {
+ case '\a': fprintf(stderr, "\a"); break;
+ case '\b': fprintf(stderr, "\b"); break;
+ case '\n': fprintf(stderr, "\n"); break;
+ case '\r': fprintf(stderr, "\r"); break;
+ default:
+ if (c >= 32 && c < 127)
+ fprintf(stderr, "%c", c);
+ else
+ fprintf(stderr, "\\x%02X", c);
+ }
+ }
+ fprintf(stderr, "\"");
+}
+
+/* assertEqualString() displays the values of the two strings. */
+int
+test_assert_equal_string(const char *file, int line,
+ const char *v1, const char *e1,
+ const char *v2, const char *e2,
+ void *extra)
+{
+ ++assertions;
+ if (v1 == NULL || v2 == NULL) {
+ if (v1 == v2) {
+ msg[0] = '\0';
+ return (1);
+ }
+ } else if (strcmp(v1, v2) == 0) {
+ msg[0] = '\0';
+ return (1);
+ }
+ failures ++;
+ if (!verbose && previous_failures(file, line))
+ return (0);
+ fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
+ file, line);
+ fprintf(stderr, " %s = ", e1);
+ strdump(v1);
+ fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1));
+ fprintf(stderr, " %s = ", e2);
+ strdump(v2);
+ fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2));
+ report_failure(extra);
+ return (0);
+}
+
+static void wcsdump(const wchar_t *w)
+{
+ if (w == NULL) {
+ fprintf(stderr, "(null)");
+ return;
+ }
+ fprintf(stderr, "\"");
+ while (*w != L'\0') {
+ unsigned int c = *w++;
+ if (c >= 32 && c < 127)
+ fprintf(stderr, "%c", c);
+ else if (c < 256)
+ fprintf(stderr, "\\x%02X", c);
+ else if (c < 0x10000)
+ fprintf(stderr, "\\u%04X", c);
+ else
+ fprintf(stderr, "\\U%08X", c);
+ }
+ fprintf(stderr, "\"");
+}
+
+/* assertEqualWString() displays the values of the two strings. */
+int
+test_assert_equal_wstring(const char *file, int line,
+ const wchar_t *v1, const char *e1,
+ const wchar_t *v2, const char *e2,
+ void *extra)
+{
+ ++assertions;
+ if (v1 == NULL) {
+ if (v2 == NULL) {
+ msg[0] = '\0';
+ return (1);
+ }
+ } else if (v2 == NULL) {
+ if (v1 == NULL) {
+ msg[0] = '\0';
+ return (1);
+ }
+ } else if (wcscmp(v1, v2) == 0) {
+ msg[0] = '\0';
+ return (1);
+ }
+ failures ++;
+ if (!verbose && previous_failures(file, line))
+ return (0);
+ fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
+ file, line);
+ fprintf(stderr, " %s = ", e1);
+ wcsdump(v1);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %s = ", e2);
+ wcsdump(v2);
+ fprintf(stderr, "\n");
+ report_failure(extra);
+ return (0);
+}
+
+/*
+ * Pretty standard hexdump routine. As a bonus, if ref != NULL, then
+ * any bytes in p that differ from ref will be highlighted with '_'
+ * before and after the hex value.
+ */
+static void
+hexdump(const char *p, const char *ref, size_t l, size_t offset)
+{
+ size_t i, j;
+ char sep;
+
+ for(i=0; i < l; i+=16) {
+ fprintf(stderr, "%04x", i + offset);
+ sep = ' ';
+ for (j = 0; j < 16 && i + j < l; j++) {
+ if (ref != NULL && p[i + j] != ref[i + j])
+ sep = '_';
+ fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]);
+ if (ref != NULL && p[i + j] == ref[i + j])
+ sep = ' ';
+ }
+ for (; j < 16; j++) {
+ fprintf(stderr, "%c ", sep);
+ sep = ' ';
+ }
+ fprintf(stderr, "%c", sep);
+ for (j=0; j < 16 && i + j < l; j++) {
+ int c = p[i + j];
+ if (c >= ' ' && c <= 126)
+ fprintf(stderr, "%c", c);
+ else
+ fprintf(stderr, ".");
+ }
+ fprintf(stderr, "\n");
+ }
+}
+
+/* assertEqualMem() displays the values of the two memory blocks. */
+/* TODO: For long blocks, hexdump the first bytes that actually differ. */
+int
+test_assert_equal_mem(const char *file, int line,
+ const char *v1, const char *e1,
+ const char *v2, const char *e2,
+ size_t l, const char *ld, void *extra)
+{
+ ++assertions;
+ if (v1 == NULL || v2 == NULL) {
+ if (v1 == v2) {
+ msg[0] = '\0';
+ return (1);
+ }
+ } else if (memcmp(v1, v2, l) == 0) {
+ msg[0] = '\0';
+ return (1);
+ }
+ failures ++;
+ if (!verbose && previous_failures(file, line))
+ return (0);
+ fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n",
+ file, line);
+ fprintf(stderr, " size %s = %d\n", ld, (int)l);
+ fprintf(stderr, " Dump of %s\n", e1);
+ hexdump(v1, v2, l < 32 ? l : 32, 0);
+ fprintf(stderr, " Dump of %s\n", e2);
+ hexdump(v2, v1, l < 32 ? l : 32, 0);
+ fprintf(stderr, "\n");
+ report_failure(extra);
+ return (0);
+}
+
+int
+test_assert_empty_file(const char *f1fmt, ...)
+{
+ char buff[1024];
+ char f1[1024];
+ struct stat st;
+ va_list ap;
+ ssize_t s;
+ int fd;
+
+
+ va_start(ap, f1fmt);
+ vsprintf(f1, f1fmt, ap);
+ va_end(ap);
+
+ if (stat(f1, &st) != 0) {
+ fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1);
+ report_failure(NULL);
+ return (0);
+ }
+ if (st.st_size == 0)
+ return (1);
+
+ failures ++;
+ if (!verbose && previous_failures(test_filename, test_line))
+ return (0);
+
+ fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1);
+ fprintf(stderr, " File size: %d\n", (int)st.st_size);
+ fprintf(stderr, " Contents:\n");
+ fd = open(f1, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, " Unable to open %s\n", f1);
+ } else {
+ s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size;
+ s = read(fd, buff, s);
+ hexdump(buff, NULL, s, 0);
+ }
+ report_failure(NULL);
+ return (0);
+}
+
+int
+test_assert_non_empty_file(const char *f1fmt, ...)
+{
+ char f1[1024];
+ struct stat st;
+ va_list ap;
+
+
+ va_start(ap, f1fmt);
+ vsprintf(f1, f1fmt, ap);
+ va_end(ap);
+
+ if (stat(f1, &st) != 0) {
+ fprintf(stderr, "%s:%d: Could not stat: %s\n",
+ test_filename, test_line, f1);
+ report_failure(NULL);
+ return (0);
+ }
+ if (st.st_size != 0)
+ return (1);
+
+ failures ++;
+ if (!verbose && previous_failures(test_filename, test_line))
+ return (0);
+
+ fprintf(stderr, "%s:%d: File empty: %s\n",
+ test_filename, test_line, f1);
+ report_failure(NULL);
+ return (0);
+}
+
+/* assertEqualFile() asserts that two files have the same contents. */
+/* TODO: hexdump the first bytes that actually differ. */
+int
+test_assert_equal_file(const char *f1, const char *f2pattern, ...)
+{
+ char f2[1024];
+ va_list ap;
+ char buff1[1024];
+ char buff2[1024];
+ int fd1, fd2;
+ int n1, n2;
+
+ va_start(ap, f2pattern);
+ vsprintf(f2, f2pattern, ap);
+ va_end(ap);
+
+ fd1 = open(f1, O_RDONLY);
+ fd2 = open(f2, O_RDONLY);
+ for (;;) {
+ n1 = read(fd1, buff1, sizeof(buff1));
+ n2 = read(fd2, buff2, sizeof(buff2));
+ if (n1 != n2)
+ break;
+ if (n1 == 0 && n2 == 0)
+ return (1);
+ if (memcmp(buff1, buff2, n1) != 0)
+ break;
+ }
+ failures ++;
+ if (!verbose && previous_failures(test_filename, test_line))
+ return (0);
+ fprintf(stderr, "%s:%d: Files are not identical\n",
+ test_filename, test_line);
+ fprintf(stderr, " file1=\"%s\"\n", f1);
+ fprintf(stderr, " file2=\"%s\"\n", f2);
+ report_failure(test_extra);
+ return (0);
+}
+
+int
+test_assert_file_exists(const char *fpattern, ...)
+{
+ char f[1024];
+ va_list ap;
+
+ va_start(ap, fpattern);
+ vsprintf(f, fpattern, ap);
+ va_end(ap);
+
+ if (!access(f, F_OK))
+ return (1);
+ if (!previous_failures(test_filename, test_line)) {
+ fprintf(stderr, "%s:%d: File doesn't exist\n",
+ test_filename, test_line);
+ fprintf(stderr, " file=\"%s\"\n", f);
+ report_failure(test_extra);
+ }
+ return (0);
+}
+
+int
+test_assert_file_not_exists(const char *fpattern, ...)
+{
+ char f[1024];
+ va_list ap;
+
+ va_start(ap, fpattern);
+ vsprintf(f, fpattern, ap);
+ va_end(ap);
+
+ if (access(f, F_OK))
+ return (1);
+ if (!previous_failures(test_filename, test_line)) {
+ fprintf(stderr, "%s:%d: File exists and shouldn't\n",
+ test_filename, test_line);
+ fprintf(stderr, " file=\"%s\"\n", f);
+ report_failure(test_extra);
+ }
+ return (0);
+}
+
+/* assertFileContents() asserts the contents of a file. */
+int
+test_assert_file_contents(const void *buff, int s, const char *fpattern, ...)
+{
+ char f[1024];
+ va_list ap;
+ char *contents;
+ int fd;
+ int n;
+
+ va_start(ap, fpattern);
+ vsprintf(f, fpattern, ap);
+ va_end(ap);
+
+ fd = open(f, O_RDONLY);
+ contents = malloc(s * 2);
+ n = read(fd, contents, s * 2);
+ if (n == s && memcmp(buff, contents, s) == 0) {
+ free(contents);
+ return (1);
+ }
+ failures ++;
+ if (!previous_failures(test_filename, test_line)) {
+ fprintf(stderr, "%s:%d: File contents don't match\n",
+ test_filename, test_line);
+ fprintf(stderr, " file=\"%s\"\n", f);
+ if (n > 0)
+ hexdump(contents, buff, n, 0);
+ else {
+ fprintf(stderr, " File empty, contents should be:\n");
+ hexdump(buff, NULL, s, 0);
+ }
+ report_failure(test_extra);
+ }
+ free(contents);
+ return (0);
+}
+
+/*
+ * Call standard system() call, but build up the command line using
+ * sprintf() conventions.
+ */
+int
+systemf(const char *fmt, ...)
+{
+ char buff[8192];
+ va_list ap;
+ int r;
+
+ va_start(ap, fmt);
+ vsprintf(buff, fmt, ap);
+ r = system(buff);
+ va_end(ap);
+ return (r);
+}
+
+/*
+ * Slurp a file into memory for ease of comparison and testing.
+ * Returns size of file in 'sizep' if non-NULL, null-terminates
+ * data in memory for ease of use.
+ */
+char *
+slurpfile(size_t * sizep, const char *fmt, ...)
+{
+ char filename[8192];
+ struct stat st;
+ va_list ap;
+ char *p;
+ ssize_t bytes_read;
+ int fd;
+ int r;
+
+ va_start(ap, fmt);
+ vsprintf(filename, fmt, ap);
+ va_end(ap);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ /* Note: No error; non-existent file is okay here. */
+ return (NULL);
+ }
+ r = fstat(fd, &st);
+ if (r != 0) {
+ fprintf(stderr, "Can't stat file %s\n", filename);
+ close(fd);
+ return (NULL);
+ }
+ p = malloc(st.st_size + 1);
+ if (p == NULL) {
+ fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename);
+ close(fd);
+ return (NULL);
+ }
+ bytes_read = read(fd, p, st.st_size);
+ if (bytes_read < st.st_size) {
+ fprintf(stderr, "Can't read file %s\n", filename);
+ close(fd);
+ free(p);
+ return (NULL);
+ }
+ p[st.st_size] = '\0';
+ if (sizep != NULL)
+ *sizep = (size_t)st.st_size;
+ close(fd);
+ return (p);
+}
+
+/*
+ * "list.h" is automatically generated; it just has a lot of lines like:
+ * DEFINE_TEST(function_name)
+ * It's used above to declare all of the test functions.
+ * We reuse it here to define a list of all tests (functions and names).
+ */
+#undef DEFINE_TEST
+#define DEFINE_TEST(n) { n, #n },
+struct { void (*func)(void); const char *name; } tests[] = {
+ #include "list.h"
+};
+
+/*
+ * Each test is run in a private work dir. Those work dirs
+ * do have consistent and predictable names, in case a group
+ * of tests need to collaborate. However, there is no provision
+ * for requiring that tests run in a certain order.
+ */
+static int test_run(int i, const char *tmpdir)
+{
+ int failures_before = failures;
+
+ if (!quiet_flag) {
+ printf("%d: %s\n", i, tests[i].name);
+ fflush(stdout);
+ }
+
+ /*
+ * Always explicitly chdir() in case the last test moved us to
+ * a strange place.
+ */
+ if (chdir(tmpdir)) {
+ fprintf(stderr,
+ "ERROR: Couldn't chdir to temp dir %s\n",
+ tmpdir);
+ exit(1);
+ }
+ /* Create a temp directory for this specific test. */
+ if (mkdir(tests[i].name, 0755)) {
+ fprintf(stderr,
+ "ERROR: Couldn't create temp dir ``%s''\n",
+ tests[i].name);
+ exit(1);
+ }
+ /* Chdir() to that work directory. */
+ if (chdir(tests[i].name)) {
+ fprintf(stderr,
+ "ERROR: Couldn't chdir to temp dir ``%s''\n",
+ tests[i].name);
+ exit(1);
+ }
+ /* Explicitly reset the locale before each test. */
+ setlocale(LC_ALL, "C");
+ /* Run the actual test. */
+ (*tests[i].func)();
+ /* Summarize the results of this test. */
+ summarize();
+ /* If there were no failures, we can remove the work dir. */
+ if (failures == failures_before) {
+ if (!keep_temp_files && chdir(tmpdir) == 0) {
+ systemf("rm -rf %s", tests[i].name);
+ }
+ }
+ /* Return appropriate status. */
+ return (failures == failures_before ? 0 : 1);
+}
+
+static void usage(const char *program)
+{
+ static const int limit = sizeof(tests) / sizeof(tests[0]);
+ int i;
+
+ printf("Usage: %s [options] <test> <test> ...\n", program);
+ printf("Default is to run all tests.\n");
+ printf("Otherwise, specify the numbers of the tests you wish to run.\n");
+ printf("Options:\n");
+ printf(" -d Dump core after any failure, for debugging.\n");
+ printf(" -k Keep all temp files.\n");
+ printf(" Default: temp files for successful tests deleted.\n");
+#ifdef PROGRAM
+ printf(" -p <path> Path to executable to be tested.\n");
+ printf(" Default: path taken from " ENVBASE " environment variable.\n");
+#endif
+ printf(" -q Quiet.\n");
+ printf(" -r <dir> Path to dir containing reference files.\n");
+ printf(" Default: Current directory.\n");
+ printf(" -v Verbose.\n");
+ printf("Available tests:\n");
+ for (i = 0; i < limit; i++)
+ printf(" %d: %s\n", i, tests[i].name);
+ exit(1);
+}
+
+#define UUDECODE(c) (((c) - 0x20) & 0x3f)
+
+void
+extract_reference_file(const char *name)
+{
+ char buff[1024];
+ FILE *in, *out;
+
+ sprintf(buff, "%s/%s.uu", refdir, name);
+ in = fopen(buff, "r");
+ failure("Couldn't open reference file %s", buff);
+ assert(in != NULL);
+ if (in == NULL)
+ return;
+ /* Read up to and including the 'begin' line. */
+ for (;;) {
+ if (fgets(buff, sizeof(buff), in) == NULL) {
+ /* TODO: This is a failure. */
+ return;
+ }
+ if (memcmp(buff, "begin ", 6) == 0)
+ break;
+ }
+ /* Now, decode the rest and write it. */
+ /* Not a lot of error checking here; the input better be right. */
+ out = fopen(name, "w");
+ while (fgets(buff, sizeof(buff), in) != NULL) {
+ char *p = buff;
+ int bytes;
+
+ if (memcmp(buff, "end", 3) == 0)
+ break;
+
+ bytes = UUDECODE(*p++);
+ while (bytes > 0) {
+ int n = 0;
+ /* Write out 1-3 bytes from that. */
+ if (bytes > 0) {
+ n = UUDECODE(*p++) << 18;
+ n |= UUDECODE(*p++) << 12;
+ fputc(n >> 16, out);
+ --bytes;
+ }
+ if (bytes > 0) {
+ n |= UUDECODE(*p++) << 6;
+ fputc((n >> 8) & 0xFF, out);
+ --bytes;
+ }
+ if (bytes > 0) {
+ n |= UUDECODE(*p++);
+ fputc(n & 0xFF, out);
+ --bytes;
+ }
+ }
+ }
+ fclose(out);
+ fclose(in);
+}
+
+
+int main(int argc, char **argv)
+{
+ static const int limit = sizeof(tests) / sizeof(tests[0]);
+ int i, tests_run = 0, tests_failed = 0, opt;
+ time_t now;
+ char *refdir_alloc = NULL;
+ char *progname, *p;
+ char tmpdir[256];
+ char tmpdir_timestamp[256];
+
+ /*
+ * Name of this program, used to build root of our temp directory
+ * tree.
+ */
+ progname = p = argv[0];
+ while (*p != '\0') {
+ if (*p == '/')
+ progname = p + 1;
+ ++p;
+ }
+
+#ifdef PROGRAM
+ /* Get the target program from environment, if available. */
+ testprog = getenv(ENVBASE);
+#endif
+
+ /* Allow -d to be controlled through the environment. */
+ if (getenv(ENVBASE "_DEBUG") != NULL)
+ dump_on_failure = 1;
+
+ /* Get the directory holding test files from environment. */
+ refdir = getenv(ENVBASE "_TEST_FILES");
+
+ /*
+ * Parse options.
+ */
+ while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) {
+ switch (opt) {
+ case 'd':
+ dump_on_failure = 1;
+ break;
+ case 'k':
+ keep_temp_files = 1;
+ break;
+ case 'p':
+#ifdef PROGRAM
+ testprog = optarg;
+#else
+ usage(progname);
+#endif
+ break;
+ case 'q':
+ quiet_flag++;
+ break;
+ case 'r':
+ refdir = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage(progname);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Sanity-check that our options make sense.
+ */
+#ifdef PROGRAM
+ if (testprog == NULL)
+ usage(progname);
+#endif
+
+ /*
+ * Create a temp directory for the following tests.
+ * Include the time the tests started as part of the name,
+ * to make it easier to track the results of multiple tests.
+ */
+ now = time(NULL);
+ for (i = 0; i < 1000; i++) {
+ strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
+ "%Y-%m-%dT%H.%M.%S",
+ localtime(&now));
+ sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i);
+ if (mkdir(tmpdir,0755) == 0)
+ break;
+ if (errno == EEXIST)
+ continue;
+ fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
+ tmpdir);
+ exit(1);
+ }
+
+ /*
+ * If the user didn't specify a directory for locating
+ * reference files, use the current directory for that.
+ */
+ if (refdir == NULL) {
+ systemf("/bin/pwd > %s/refdir", tmpdir);
+ refdir = refdir_alloc = slurpfile(NULL, "%s/refdir", tmpdir);
+ p = refdir + strlen(refdir);
+ while (p[-1] == '\n') {
+ --p;
+ *p = '\0';
+ }
+ systemf("rm %s/refdir", tmpdir);
+ }
+
+ /*
+ * Banner with basic information.
+ */
+ if (!quiet_flag) {
+ printf("Running tests in: %s\n", tmpdir);
+ printf("Reference files will be read from: %s\n", refdir);
+#ifdef PROGRAM
+ printf("Running tests on: %s\n", testprog);
+#endif
+ printf("Exercising: ");
+ fflush(stdout);
+ printf("%s\n", EXTRA_VERSION);
+ }
+
+ /*
+ * Run some or all of the individual tests.
+ */
+ if (argc == 0) {
+ /* Default: Run all tests. */
+ for (i = 0; i < limit; i++) {
+ if (test_run(i, tmpdir))
+ tests_failed++;
+ tests_run++;
+ }
+ } else {
+ while (*(argv) != NULL) {
+ i = atoi(*argv);
+ if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
+ printf("*** INVALID Test %s\n", *argv);
+ usage(progname);
+ } else {
+ if (test_run(i, tmpdir))
+ tests_failed++;
+ tests_run++;
+ }
+ argv++;
+ }
+ }
+
+ /*
+ * Report summary statistics.
+ */
+ if (!quiet_flag) {
+ printf("\n");
+ printf("%d of %d tests reported failures\n",
+ tests_failed, tests_run);
+ printf(" Total of %d assertions checked.\n", assertions);
+ printf(" Total of %d assertions failed.\n", failures);
+ printf(" Total of %d assertions skipped.\n", skips);
+ }
+
+ free(refdir_alloc);
+
+ /* If the final tmpdir is empty, we can remove it. */
+ /* This should be the usual case when all tests succeed. */
+ rmdir(tmpdir);
+
+ return (tests_failed);
+}
--- /dev/null
+++ usr.bin/tar/test/test_option_q.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_option_q.c,v 1.3.2.1 2008/08/25 02:14:52 kientzle Exp $");
+
+DEFINE_TEST(test_option_q)
+{
+ int fd;
+
+ /*
+ * Create an archive with several different versions of the
+ * same files. By default, the last version will overwrite
+ * any earlier versions. The -q/--fast-read option will
+ * stop early, so we can verify -q/--fast-read by seeing
+ * which version of each file actually ended up being
+ * extracted. This also exercises -r mode, since that's
+ * what we use to build up the test archive.
+ */
+
+ fd = open("foo", O_CREAT | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(4, write(fd, "foo1", 4));
+ close(fd);
+
+ assertEqualInt(0, systemf("%s -cf archive.tar foo", testprog));
+
+ fd = open("foo", O_TRUNC | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(4, write(fd, "foo2", 4));
+ close(fd);
+
+ assertEqualInt(0, systemf("%s -rf archive.tar foo", testprog));
+
+ fd = open("bar", O_CREAT | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(4, write(fd, "bar1", 4));
+ close(fd);
+
+ assertEqualInt(0, systemf("%s -rf archive.tar bar", testprog));
+
+ fd = open("foo", O_TRUNC | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(4, write(fd, "foo3", 4));
+ close(fd);
+
+ assertEqualInt(0, systemf("%s -rf archive.tar foo", testprog));
+
+ fd = open("bar", O_TRUNC | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(4, write(fd, "bar2", 4));
+ close(fd);
+
+ assertEqualInt(0, systemf("%s -rf archive.tar bar", testprog));
+
+ /*
+ * Now, try extracting from the test archive with various
+ * combinations of -q.
+ */
+
+ /* Test 1: -q foo should only extract the first foo. */
+ assertEqualInt(0, mkdir("test1", 0755));
+ assertEqualInt(0, chdir("test1"));
+ assertEqualInt(0,
+ systemf("%s -xf ../archive.tar -q foo >test.out 2>test.err",
+ testprog));
+ assertFileContents("foo1", 4, "foo");
+ assertEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertEqualInt(0, chdir(".."));
+
+ /* Test 2: -q foo bar should extract up to the first bar. */
+ assertEqualInt(0, mkdir("test2", 0755));
+ assertEqualInt(0, chdir("test2"));
+ assertEqualInt(0,
+ systemf("%s -xf ../archive.tar -q foo bar >test.out 2>test.err", testprog));
+ assertFileContents("foo2", 4, "foo");
+ assertFileContents("bar1", 4, "bar");
+ assertEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertEqualInt(0, chdir(".."));
+
+ /* Test 3: Same as test 2, but use --fast-read spelling. */
+ assertEqualInt(0, mkdir("test3", 0755));
+ assertEqualInt(0, chdir("test3"));
+ assertEqualInt(0,
+ systemf("%s -xf ../archive.tar --fast-read foo bar >test.out 2>test.err", testprog));
+ assertFileContents("foo2", 4, "foo");
+ assertFileContents("bar1", 4, "bar");
+ assertEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertEqualInt(0, chdir(".."));
+
+ /* Test 4: Without -q, should extract everything. */
+ assertEqualInt(0, mkdir("test4", 0755));
+ assertEqualInt(0, chdir("test4"));
+ assertEqualInt(0,
+ systemf("%s -xf ../archive.tar foo bar >test.out 2>test.err", testprog));
+ assertFileContents("foo3", 4, "foo");
+ assertFileContents("bar2", 4, "bar");
+ assertEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertEqualInt(0, chdir(".."));
+}
--- /dev/null
+++ usr.bin/tar/test/test_patterns_3.tgz.uu
@@ -0,0 +1,8 @@
+$FreeBSD: src/usr.bin/tar/test/test_patterns_3.tgz.uu,v 1.1.2.1 2008/08/27 04:59:00 kientzle Exp $
+begin 644 test_patterns_3.tgz
+M'XL(`)P/K4@``^W5T0K"(!3&\3V*;^#1H3Z/@T5!8[&,H*=O%J/M9M3(7?U_
+M-T?1BR.?HD[=11_Z7C=QT%49(A*<4V,UP4FNV53?$V/$U3;OLTK,./*5<H7Z
+M6;A=4QS&5M*I6]UW/[;M>65]>2CUUQX+TO/\F_ at H<0<VY!^\)?\]?.(O$OW+
+K3_E[R?F;FO>_BWG^I;Z`#?D'X?T#``````````````!\Y0FE&!YR`"@`````
+`
+end
--- /dev/null
+++ usr.bin/tar/test/test_basic.c
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_basic.c,v 1.2.2.1 2008/08/10 06:53:28 kientzle Exp $");
+
+
+static void
+basic_tar(const char *target, const char *pack_options, const char *unpack_options)
+{
+ struct stat st, st2;
+ char buff[128];
+ int r;
+
+ assertEqualInt(0, mkdir(target, 0775));
+
+ /* Use the tar program to create an archive. */
+ r = systemf("%s cf - %s `cat filelist` >%s/archive 2>%s/pack.err", testprog, pack_options, target, target);
+ failure("Error invoking %s cf -", testprog, pack_options);
+ assertEqualInt(r, 0);
+
+ chdir(target);
+
+ /* Verify that nothing went to stderr. */
+ assertEmptyFile("pack.err");
+
+ /*
+ * Use tar to unpack the archive into another directory.
+ */
+ r = systemf("%s xf archive %s >unpack.out 2>unpack.err", testprog, unpack_options);
+ failure("Error invoking %s xf archive %s", testprog, unpack_options);
+ assertEqualInt(r, 0);
+
+ /* Verify that nothing went to stderr. */
+ assertEmptyFile("unpack.err");
+
+ /*
+ * Verify unpacked files.
+ */
+
+ /* Regular file with 2 links. */
+ r = lstat("file", &st);
+ failure("Failed to stat file %s/file, errno=%d", target, errno);
+ assertEqualInt(r, 0);
+ if (r == 0) {
+ assert(S_ISREG(st.st_mode));
+ assertEqualInt(0644, st.st_mode & 0777);
+ assertEqualInt(10, st.st_size);
+ failure("file %s/file", target);
+ assertEqualInt(2, st.st_nlink);
+ }
+
+ /* Another name for the same file. */
+ r = lstat("linkfile", &st2);
+ failure("Failed to stat file %s/linkfile, errno=%d", target, errno);
+ assertEqualInt(r, 0);
+ if (r == 0) {
+ assert(S_ISREG(st2.st_mode));
+ assertEqualInt(0644, st2.st_mode & 0777);
+ assertEqualInt(10, st2.st_size);
+ failure("file %s/linkfile", target);
+ assertEqualInt(2, st2.st_nlink);
+ /* Verify that the two are really hardlinked. */
+ assertEqualInt(st.st_dev, st2.st_dev);
+ failure("%s/linkfile and %s/file aren't really hardlinks", target, target);
+ assertEqualInt(st.st_ino, st2.st_ino);
+ }
+
+ /* Symlink */
+ r = lstat("symlink", &st);
+ failure("Failed to stat file %s/symlink, errno=%d", target, errno);
+ assertEqualInt(r, 0);
+ if (r == 0) {
+ failure("symlink should be a symlink; actual mode is %o",
+ st.st_mode);
+ assert(S_ISLNK(st.st_mode));
+ if (S_ISLNK(st.st_mode)) {
+ r = readlink("symlink", buff, sizeof(buff));
+ assertEqualInt(r, 4);
+ buff[r] = '\0';
+ assertEqualString(buff, "file");
+ }
+ }
+
+ /* dir */
+ r = lstat("dir", &st);
+ if (r == 0) {
+ assertEqualInt(r, 0);
+ assert(S_ISDIR(st.st_mode));
+ assertEqualInt(0775, st.st_mode & 0777);
+ }
+
+ chdir("..");
+}
+
+DEFINE_TEST(test_basic)
+{
+ int fd;
+ int filelist;
+ int oldumask;
+
+ oldumask = umask(0);
+
+ /*
+ * Create an assortment of files on disk.
+ */
+ filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
+
+ /* File with 10 bytes content. */
+ fd = open("file", O_CREAT | O_WRONLY, 0644);
+ assert(fd >= 0);
+ assertEqualInt(10, write(fd, "123456789", 10));
+ close(fd);
+ write(filelist, "file\n", 5);
+
+ /* hardlink to above file. */
+ assertEqualInt(0, link("file", "linkfile"));
+ write(filelist, "linkfile\n", 9);
+
+ /* Symlink to above file. */
+ assertEqualInt(0, symlink("file", "symlink"));
+ write(filelist, "symlink\n", 8);
+
+ /* Directory. */
+ assertEqualInt(0, mkdir("dir", 0775));
+ write(filelist, "dir\n", 4);
+ /* All done. */
+ close(filelist);
+
+ /* Archive/dearchive with a variety of options. */
+ basic_tar("copy", "", "");
+ /* tar doesn't handle cpio symlinks correctly */
+ /* basic_tar("copy_odc", "--format=odc", ""); */
+ basic_tar("copy_ustar", "--format=ustar", "");
+
+ umask(oldumask);
+}
--- /dev/null
+++ usr.bin/tar/test/Makefile
@@ -0,0 +1,65 @@
+# $FreeBSD: src/usr.bin/tar/test/Makefile,v 1.2.2.4 2008/12/11 05:56:47 kientzle Exp $
+
+# Where to find the tar sources (for the internal unit tests)
+TAR_SRCDIR=${.CURDIR}/..
+.PATH: ${TAR_SRCDIR}
+
+# Some tar sources are pulled in for white-box tests
+TAR_SRCS= \
+ ../getdate.y
+
+TESTS= \
+ test_0.c \
+ test_basic.c \
+ test_copy.c \
+ test_getdate.c \
+ test_help.c \
+ test_option_T.c \
+ test_option_q.c \
+ test_patterns.c \
+ test_stdio.c \
+ test_strip_components.c \
+ test_symlink_dir.c \
+ test_version.c
+
+# Build the test program
+SRCS= ${TAR_SRCS} \
+ ${TESTS} \
+ list.h \
+ main.c
+
+CLEANFILES+= list.h
+
+NO_MAN=yes
+
+PROG=bsdtar_test
+DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
+CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
+CFLAGS+= -I..
+LDADD= -larchive -lz -lbz2
+CFLAGS+= -static -g -O2 -Wall
+CFLAGS+= -I${.OBJDIR}
+CFLAGS+= -I${TAR_SRCDIR}
+
+# Uncomment to link against dmalloc
+#LDADD+= -L/usr/local/lib -ldmalloc
+#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
+WARNS=6
+
+check test: bsdtar_test
+ ./bsdtar_test -p ${.OBJDIR}/../bsdtar -r ${.CURDIR}
+
+list.h: ${TESTS} Makefile
+ (cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h
+
+clean:
+ rm -f *.out
+ rm -f *.o
+ rm -f *.core
+ rm -f *~
+ rm -f list.h
+ rm -f archive.h ../archive.h
+ -chmod -R +w /tmp/bsdtar_test.*
+ rm -rf /tmp/bsdtar_test.*
+
+.include <bsd.prog.mk>
--- /dev/null
+++ usr.bin/tar/test/test_0.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_0.c,v 1.2.2.1 2008/08/10 06:53:28 kientzle Exp $");
+
+/*
+ * This first test does basic sanity checks on the environment. For
+ * most of these, we just exit on failure.
+ */
+
+DEFINE_TEST(test_0)
+{
+ struct stat st;
+
+ failure("File %s does not exist?!", testprog);
+ if (!assertEqualInt(0, stat(testprog, &st)))
+ exit(1);
+
+ failure("%s is not executable?!", testprog);
+ if (!assert((st.st_mode & 0111) != 0))
+ exit(1);
+
+ /*
+ * Try to succesfully run the program; this requires that
+ * we know some option that will succeed.
+ */
+ if (0 == systemf("%s --version >/dev/null", testprog)) {
+ /* This worked. */
+ } else if (0 == systemf("%s -W version >/dev/null", testprog)) {
+ /* This worked. */
+ } else {
+ failure("Unable to successfully run any of the following:\n"
+ " * %s --version\n"
+ " * %s -W version\n",
+ testprog, testprog);
+ assert(0);
+ }
+
+ /* TODO: Ensure that our reference files are available. */
+}
--- /dev/null
+++ usr.bin/tar/test/test.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2003-2006 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/usr.bin/tar/test/test.h,v 1.3.2.3 2008/08/27 04:59:00 kientzle Exp $
+ */
+
+/* Every test program should #include "test.h" as the first thing. */
+
+/*
+ * The goal of this file (and the matching test.c) is to
+ * simplify the very repetitive test-*.c test programs.
+ */
+#if defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "../../config.h"
+#elif defined(__FreeBSD__)
+/* Building as part of FreeBSD system requires a pre-built config.h. */
+#include "../config_freebsd.h"
+#elif defined(_WIN32)
+/* Win32 can't run the 'configure' script. */
+#include "../config_windows.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no pre-built configuration in test.h.
+#endif
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <wchar.h>
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
+/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
+#ifdef __FreeBSD__
+#include <sys/cdefs.h> /* For __FBSDID */
+#else
+#define __FBSDID(a) /* null */
+#endif
+
+/*
+ * Redefine DEFINE_TEST for use in defining the test functions.
+ */
+#undef DEFINE_TEST
+#define DEFINE_TEST(name) void name(void); void name(void)
+
+/* An implementation of the standard assert() macro */
+#define assert(e) test_assert(__FILE__, __LINE__, (e), #e, NULL)
+
+/* Assert two integers are the same. Reports value of each one if not. */
+#define assertEqualInt(v1,v2) \
+ test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+
+/* Assert two strings are the same. Reports value of each one if not. */
+#define assertEqualString(v1,v2) \
+ test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+/* As above, but v1 and v2 are wchar_t * */
+#define assertEqualWString(v1,v2) \
+ test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+/* As above, but raw blocks of bytes. */
+#define assertEqualMem(v1, v2, l) \
+ test_assert_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
+/* Assert two files are the same; allow printf-style expansion of second name.
+ * See below for comments about variable arguments here...
+ */
+#define assertEqualFile \
+ test_setup(__FILE__, __LINE__);test_assert_equal_file
+/* Assert that a file is empty; supports printf-style arguments. */
+#define assertEmptyFile \
+ test_setup(__FILE__, __LINE__);test_assert_empty_file
+/* Assert that a file is not empty; supports printf-style arguments. */
+#define assertNonEmptyFile \
+ test_setup(__FILE__, __LINE__);test_assert_non_empty_file
+/* Assert that a file exists; supports printf-style arguments. */
+#define assertFileExists \
+ test_setup(__FILE__, __LINE__);test_assert_file_exists
+/* Assert that a file exists; supports printf-style arguments. */
+#define assertFileNotExists \
+ test_setup(__FILE__, __LINE__);test_assert_file_not_exists
+/* Assert that file contents match a string; supports printf-style arguments. */
+#define assertFileContents \
+ test_setup(__FILE__, __LINE__);test_assert_file_contents
+
+/*
+ * This would be simple with C99 variadic macros, but I don't want to
+ * require that. Instead, I insert a function call before each
+ * skipping() call to pass the file and line information down. Crude,
+ * but effective.
+ */
+#define skipping \
+ test_setup(__FILE__, __LINE__);test_skipping
+
+/* Function declarations. These are defined in test_utility.c. */
+void failure(const char *fmt, ...);
+void test_setup(const char *, int);
+void test_skipping(const char *fmt, ...);
+int test_assert(const char *, int, int, const char *, void *);
+int test_assert_empty_file(const char *, ...);
+int test_assert_non_empty_file(const char *, ...);
+int test_assert_equal_file(const char *, const char *, ...);
+int test_assert_equal_int(const char *, int, int, const char *, int, const char *, void *);
+int test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *);
+int test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
+int test_assert_equal_mem(const char *, int, const char *, const char *, const char *, const char *, size_t, const char *, void *);
+int test_assert_file_contents(const void *, int, const char *, ...);
+int test_assert_file_exists(const char *, ...);
+int test_assert_file_not_exists(const char *, ...);
+
+/* Like sprintf, then system() */
+int systemf(const char * fmt, ...);
+
+/* Suck file into string allocated via malloc(). Call free() when done. */
+/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
+char *slurpfile(size_t *, const char *fmt, ...);
+
+/* Extracts named reference file to the current directory. */
+void extract_reference_file(const char *);
+
+/*
+ * Special interfaces for program test harness.
+ */
+
+/* Pathname of exe to be tested. */
+char *testprog;
--- /dev/null
+++ usr.bin/tar/test/test_getdate.c
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_getdate.c,v 1.2.2.1 2008/08/10 06:53:28 kientzle Exp $");
+
+/*
+ * Verify that the getdate() function works.
+ */
+
+time_t get_date(const char *);
+
+DEFINE_TEST(test_getdate)
+{
+ assertEqualInt(0, get_date("Jan 1, 1970 UTC"));
+ /* TODO: Lots more tests here. */
+}
--- /dev/null
+++ usr.bin/tar/test/test_patterns_2.tgz.uu
@@ -0,0 +1,9 @@
+$FreeBSD: src/usr.bin/tar/test/test_patterns_2.tgz.uu,v 1.1.2.1 2008/08/27 04:59:00 kientzle Exp $
+begin 644 test_patterns_2.tgz
+M'XL(`,P5I4@``^W3T0J",!3&<1]E;[!SYC:?Q\`H2`PS at IZ^F5AV(PFMJ__O
+MYB@;>.:W8X?V;/==9XM\1*0*P:2J59"QCN8ZO:A*4*<NAFA$G?=:F)"QIY?K
+M9:C[U,IP;%?WW0Y-<UI9_SR4^6F/&=DY_UW=Y[H#V_(O4_ZE$T?^_[#(_Y[K
+M&^E_1.^WS'^9'@HCN1I:(O_W_&>Z`]OR?\Y_C!7Y`P``````````````?.,!
+(*>E$>P`H````
+`
+end
--- /dev/null
+++ usr.bin/tar/test/test_patterns.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_patterns.c,v 1.1.2.3 2008/08/27 04:59:00 kientzle Exp $");
+
+DEFINE_TEST(test_patterns)
+{
+ int fd, r;
+ const char *reffile2 = "test_patterns_2.tgz";
+ const char *reffile3 = "test_patterns_3.tgz";
+ const char *p;
+
+ /*
+ * Test basic command-line pattern handling.
+ */
+
+ /*
+ * Test 1: Files on the command line that don't get matched
+ * didn't produce an error.
+ *
+ * John Baldwin reported this problem in PR bin/121598
+ */
+ fd = open("foo", O_CREAT | O_WRONLY, 0644);
+ assert(fd >= 0);
+ close(fd);
+ r = systemf("%s zcfv tar1.tgz foo > tar1a.out 2> tar1a.err", testprog);
+ assertEqualInt(r, 0);
+ r = systemf("%s zxfv tar1.tgz foo bar > tar1b.out 2> tar1b.err", testprog);
+ failure("tar should return non-zero because a file was given on the command line that's not in the archive");
+ assert(r != 0);
+
+ /*
+ * Test 2: Check basic matching of full paths that start with /
+ */
+ extract_reference_file(reffile2);
+
+ r = systemf("%s tf %s /tmp/foo/bar > tar2a.out 2> tar2a.err",
+ testprog, reffile2);
+ assertEqualInt(r, 0);
+ p = "/tmp/foo/bar/\n/tmp/foo/bar/baz\n";
+ assertFileContents(p, strlen(p), "tar2a.out");
+ assertEmptyFile("tar2a.err");
+
+ /*
+ * Test 3 archive has some entries starting with '/' and some not.
+ */
+ extract_reference_file(reffile3);
+
+ /* Test 3a: Pattern tmp/foo/bar should not match /tmp/foo/bar */
+ r = systemf("%s xf %s tmp/foo/bar > tar3a.out 2> tar3a.err",
+ testprog, reffile3);
+ assert(r != 0);
+ assertEmptyFile("tar3a.out");
+
+ /* Test 3b: Pattern /tmp/foo/baz should not match tmp/foo/baz */
+ assertNonEmptyFile("tar3a.err");
+ /* Again, with the '/' */
+ r = systemf("%s xf %s /tmp/foo/baz > tar3b.out 2> tar3b.err",
+ testprog, reffile3);
+ assert(r != 0);
+ assertEmptyFile("tar3b.out");
+ assertNonEmptyFile("tar3b.err");
+
+ /* Test 3c: ./tmp/foo/bar should not match /tmp/foo/bar */
+ r = systemf("%s xf %s ./tmp/foo/bar > tar3c.out 2> tar3c.err",
+ testprog, reffile3);
+ assert(r != 0);
+ assertEmptyFile("tar3c.out");
+ assertNonEmptyFile("tar3c.err");
+
+ /* Test 3d: ./tmp/foo/baz should match tmp/foo/baz */
+ r = systemf("%s xf %s ./tmp/foo/baz > tar3d.out 2> tar3d.err",
+ testprog, reffile3);
+ assertEqualInt(r, 0);
+ assertEmptyFile("tar3d.out");
+ assertEmptyFile("tar3d.err");
+ assertEqualInt(0, access("tmp/foo/baz/bar", F_OK));
+}
--- /dev/null
+++ usr.bin/tar/test/test_help.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_help.c,v 1.2.2.1 2008/08/10 06:53:28 kientzle Exp $");
+
+/*
+ * Test that "--help", "-h", and "-W help" options all work and
+ * generate reasonable output.
+ */
+
+static int
+in_first_line(const char *p, const char *substring)
+{
+ size_t l = strlen(substring);
+
+ while (*p != '\0' && *p != '\n') {
+ if (memcmp(p, substring, l) == 0)
+ return (1);
+ ++p;
+ }
+ return (0);
+}
+
+DEFINE_TEST(test_help)
+{
+ int r;
+ char *p;
+ size_t plen;
+
+ /* Exercise --help option. */
+ r = systemf("%s --help >help.stdout 2>help.stderr", testprog);
+ failure("--help should generate nothing to stderr.");
+ assertEmptyFile("help.stderr");
+ /* Help message should start with name of program. */
+ p = slurpfile(&plen, "help.stdout");
+ failure("Help output should be long enough.");
+ assert(plen >= 6);
+ failure("First line of help output should contain 'bsdtar': %s", p);
+ assert(in_first_line(p, "bsdtar"));
+ /*
+ * TODO: Extend this check to further verify that --help output
+ * looks approximately right.
+ */
+ free(p);
+
+ /* -h option should generate the same output. */
+ r = systemf("%s -h >h.stdout 2>h.stderr", testprog);
+ failure("-h should generate nothing to stderr.");
+ assertEmptyFile("h.stderr");
+ failure("stdout should be same for -h and --help");
+ assertEqualFile("h.stdout", "help.stdout");
+
+ /* -W help should be another synonym. */
+ r = systemf("%s -W help >Whelp.stdout 2>Whelp.stderr", testprog);
+ failure("-W help should generate nothing to stderr.");
+ assertEmptyFile("Whelp.stderr");
+ failure("stdout should be same for -W help and --help");
+ assertEqualFile("Whelp.stdout", "help.stdout");
+}
--- /dev/null
+++ usr.bin/tar/test/test_option_T.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2003-2008 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_option_T.c,v 1.2.2.3 2008/08/25 02:01:47 kientzle Exp $");
+
+static int
+touch(const char *fn)
+{
+ int fd = open(fn, O_RDWR | O_CREAT);
+ failure("Couldn't create file '%s', fd=%d, errno=%d (%s)\n",
+ fn, fd, errno, strerror(errno));
+ if (!assert(fd > 0))
+ return (0); /* Failure. */
+ close(fd);
+ return (1); /* Success */
+}
+
+DEFINE_TEST(test_option_T)
+{
+ FILE *f;
+ int r;
+
+ /* Create a simple dir heirarchy; bail if anything fails. */
+ if (!assertEqualInt(0, mkdir("d1", 0755))) return;
+ if (!assertEqualInt(0, mkdir("d1/d2", 0755))) return;
+ if (!touch("d1/f1")) return;
+ if (!touch("d1/f2")) return;
+ if (!touch("d1/d2/f3")) return;
+ if (!touch("d1/d2/f4")) return;
+ if (!touch("d1/d2/f5")) return;
+
+ /* Populate a file list */
+ f = fopen("filelist", "w+");
+ if (!assert(f != NULL))
+ return;
+ fprintf(f, "d1/f1\n");
+ fprintf(f, "d1/d2/f4\n");
+ fclose(f);
+
+ /* Populate a second file list */
+ f = fopen("filelist2", "w+");
+ if (!assert(f != NULL))
+ return;
+ fprintf(f, "d1/d2/f3\n");
+ fprintf(f, "d1/d2/f5\n");
+ fclose(f);
+
+ /* Use -c -T to archive up the files. */
+ r = systemf("%s -c -f test1.tar -T filelist > test1.out 2> test1.err",
+ testprog);
+ failure("Failure here probably means that tar can't archive zero-length files without reading them");
+ assert(r == 0);
+ assertEmptyFile("test1.out");
+ assertEmptyFile("test1.err");
+
+ /* Use -x -T to dearchive the files */
+ if (!assertEqualInt(0, mkdir("test1", 0755))) return;
+ systemf("%s -x -f test1.tar -T filelist -C test1"
+ " > test1b.out 2> test1b.err", testprog);
+ assertEmptyFile("test1b.out");
+ assertEmptyFile("test1b.err");
+
+ /* Verify the files were extracted. */
+ assertFileExists("test1/d1/f1");
+ assertFileNotExists("test1/d1/f2");
+ assertFileNotExists("test1/d1/d2/f3");
+ assertFileExists("test1/d1/d2/f4");
+ assertFileNotExists("test1/d1/d2/f5");
+
+ /* Use -r -T to add more files to the archive. */
+ systemf("%s -r -f test1.tar -T filelist2 > test2.out 2> test2.err",
+ testprog);
+ assertEmptyFile("test2.out");
+ assertEmptyFile("test2.err");
+
+ /* Use -x without -T to dearchive the files (ensure -r worked) */
+ if (!assertEqualInt(0, mkdir("test3", 0755))) return;
+ systemf("%s -x -f test1.tar -C test3"
+ " > test3.out 2> test3.err", testprog);
+ assertEmptyFile("test3.out");
+ assertEmptyFile("test3.err");
+ /* Verify the files were extracted.*/
+ assertFileExists("test3/d1/f1");
+ assertFileNotExists("test3/d1/f2");
+ assertFileExists("test3/d1/d2/f3");
+ assertFileExists("test3/d1/d2/f4");
+ assertFileExists("test3/d1/d2/f5");
+
+ /* Use -x -T to dearchive the files (verify -x -T together) */
+ if (!assertEqualInt(0, mkdir("test2", 0755))) return;
+ systemf("%s -x -f test1.tar -T filelist -C test2"
+ " > test2b.out 2> test2b.err", testprog);
+ assertEmptyFile("test2b.out");
+ assertEmptyFile("test2b.err");
+ /* Verify the files were extracted.*/
+ assertFileExists("test2/d1/f1");
+ assertFileNotExists("test2/d1/f2");
+ assertFileNotExists("test2/d1/d2/f3");
+ assertFileExists("test2/d1/d2/f4");
+ assertFileNotExists("test2/d1/d2/f5");
+
+ assertEqualInt(0, mkdir("test4", 0755));
+ assertEqualInt(0, mkdir("test4_out", 0755));
+ assertEqualInt(0, mkdir("test4_out2", 0755));
+ assertEqualInt(0, mkdir("test4/d1", 0755));
+ assertEqualInt(1, touch("test4/d1/foo"));
+
+ systemf("%s -cf - -s /foo/bar/ test4/d1/foo | %s -xf - -C test4_out",
+ testprog, testprog);
+ assertEmptyFile("test4_out/test4/d1/bar");
+ systemf("%s -cf - -s /d1/d2/ test4/d1/foo | %s -xf - -C test4_out",
+ testprog, testprog);
+ assertEmptyFile("test4_out/test4/d2/foo");
+ systemf("%s -cf - -s ,test4/d1/foo,, test4/d1/foo | %s -tvf - > test4.lst",
+ testprog, testprog);
+ assertEmptyFile("test4.lst");
+ systemf("%s -cf - test4/d1/foo | %s -xf - -s /foo/bar/ -C test4_out2",
+ testprog, testprog);
+ assertEmptyFile("test4_out2/test4/d1/bar");
+
+ /* TODO: Include some use of -C directory-changing within the filelist. */
+ /* I'm pretty sure -C within the filelist is broken on extract. */
+}
More information about the Midnightbsd-cvs
mailing list