summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2010-02-06 20:27:36 +0000
committerkientzle <kientzle@FreeBSD.org>2010-02-06 20:27:36 +0000
commit4392f2bf7231595a13a6a6ea7c119d7cec24eb4f (patch)
tree2c8ff1e028595860f8bc68cbc836e432609f4a4b /usr.bin/tar
parent4b236d0b57e31e32debed1ab6ecbc3ecdee0b0c5 (diff)
downloadFreeBSD-src-4392f2bf7231595a13a6a6ea7c119d7cec24eb4f.zip
FreeBSD-src-4392f2bf7231595a13a6a6ea7c119d7cec24eb4f.tar.gz
Refactor the siginfo/sigusr1 handling. The read/write reporting
is sufficiently different that it was simpler to just put separate reporting functions into read.c and write.c rather than try to have a single all-purpose reporting function. Switch to a custom function for converting int64_t to a string; in the portable version, this saves a lot of configuration headaches trying to decipher the platform printf().
Diffstat (limited to 'usr.bin/tar')
-rw-r--r--usr.bin/tar/Makefile1
-rw-r--r--usr.bin/tar/bsdtar.c52
-rw-r--r--usr.bin/tar/bsdtar.h7
-rw-r--r--usr.bin/tar/read.c53
-rw-r--r--usr.bin/tar/siginfo.c152
-rw-r--r--usr.bin/tar/util.c22
-rw-r--r--usr.bin/tar/write.c61
7 files changed, 146 insertions, 202 deletions
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile
index 7f19b7e..b2eb4c2 100644
--- a/usr.bin/tar/Makefile
+++ b/usr.bin/tar/Makefile
@@ -9,7 +9,6 @@ SRCS= bsdtar.c \
getdate.c \
matching.c \
read.c \
- siginfo.c \
subst.c \
tree.c \
util.c \
diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c
index 80d4ded..b070d8e 100644
--- a/usr.bin/tar/bsdtar.c
+++ b/usr.bin/tar/bsdtar.c
@@ -47,6 +47,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -82,7 +85,32 @@ __FBSDID("$FreeBSD$");
#define _PATH_DEFTAPE "/dev/tape"
#endif
-/* External function to parse a date/time string (from getdate.y) */
+#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1))
+static volatile int siginfo_occurred;
+
+static void
+siginfo_handler(int sig)
+{
+ (void)sig; /* UNUSED */
+ siginfo_occurred = 1;
+}
+
+int
+need_report(void)
+{
+ int r = siginfo_occurred;
+ siginfo_occurred = 0;
+ return (r);
+}
+#else
+int
+need_report(void)
+{
+ return (0);
+}
+#endif
+
+/* External function to parse a date/time string */
time_t get_date(time_t, const char *);
static void long_help(void);
@@ -114,11 +142,23 @@ main(int argc, char **argv)
memset(bsdtar, 0, sizeof(*bsdtar));
bsdtar->fd = -1; /* Mark as "unused" */
option_o = 0;
-#if defined(_WIN32) && !defined(__CYGWIN__)
- /* Make sure open() function will be used with a binary mode. */
- /* on cygwin, we need something similar, but instead link against */
- /* a special startup object, binmode.o */
- _set_fmode(_O_BINARY);
+
+#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1))
+ { /* Catch SIGINFO and SIGUSR1, if they exist. */
+ struct sigaction sa;
+ sa.sa_handler = siginfo_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+#ifdef SIGINFO
+ if (sigaction(SIGINFO, &sa, NULL))
+ bsdtar_errc(1, errno, "sigaction(SIGINFO) failed");
+#endif
+#ifdef SIGUSR1
+ /* ... and treat SIGUSR1 the same way as SIGINFO. */
+ if (sigaction(SIGUSR1, &sa, NULL))
+ bsdtar_errc(1, errno, "sigaction(SIGUSR1) failed");
+#endif
+ }
#endif
/* Need bsdtar_progname before calling bsdtar_warnc. */
diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h
index 2d70b7e..2e5972b 100644
--- a/usr.bin/tar/bsdtar.h
+++ b/usr.bin/tar/bsdtar.h
@@ -142,16 +142,13 @@ int exclude_from_file(struct bsdtar *, const char *pathname);
int excluded(struct bsdtar *, const char *pathname);
int include(struct bsdtar *, const char *pattern);
int include_from_file(struct bsdtar *, const char *pathname);
+int need_report(void);
int pathcmp(const char *a, const char *b);
int process_lines(struct bsdtar *bsdtar, const char *pathname,
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 *);
+const char *tar_i64toa(int64_t);
void tar_mode_c(struct bsdtar *bsdtar);
void tar_mode_r(struct bsdtar *bsdtar);
void tar_mode_t(struct bsdtar *bsdtar);
diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c
index ec80db8..16334d1 100644
--- a/usr.bin/tar/read.c
+++ b/usr.bin/tar/read.c
@@ -70,6 +70,12 @@ __FBSDID("$FreeBSD$");
#include "bsdtar.h"
#include "err.h"
+struct progress_data {
+ struct bsdtar *bsdtar;
+ struct archive *archive;
+ struct archive_entry *entry;
+};
+
static void list_item_verbose(struct bsdtar *, FILE *,
struct archive_entry *);
static void read_archive(struct bsdtar *bsdtar, char mode);
@@ -84,22 +90,40 @@ tar_mode_t(struct bsdtar *bsdtar)
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)
+progress_func(void *cookie)
{
- struct bsdtar * bsdtar = cookie;
-
- siginfo_printinfo(bsdtar, 0);
+ struct progress_data *progress_data = cookie;
+ struct bsdtar *bsdtar = progress_data->bsdtar;
+ struct archive *a = progress_data->archive;
+ struct archive_entry *entry = progress_data->entry;
+ uint64_t comp, uncomp;
+
+ if (!need_report())
+ return;
+
+ if (bsdtar->verbose)
+ fprintf(stderr, "\n");
+ if (a != NULL) {
+ comp = archive_position_compressed(a);
+ uncomp = archive_position_uncompressed(a);
+ fprintf(stderr,
+ "In: %s bytes, compression %d%%;",
+ tar_i64toa(comp), (int)((uncomp - comp) * 100 / uncomp));
+ fprintf(stderr, " Out: %d files, %s bytes\n",
+ archive_file_count(a), tar_i64toa(uncomp));
+ }
+ if (entry != NULL) {
+ safe_fprintf(stderr, "Current: %s",
+ archive_entry_pathname(entry));
+ fprintf(stderr, " (%s bytes)\n",
+ tar_i64toa(archive_entry_size(entry)));
+ }
}
/*
@@ -108,6 +132,7 @@ progress_func(void * cookie)
static void
read_archive(struct bsdtar *bsdtar, char mode)
{
+ struct progress_data progress_data;
FILE *out;
struct archive *a;
struct archive_entry *entry;
@@ -140,8 +165,10 @@ read_archive(struct bsdtar *bsdtar, char mode)
if (mode == 'x') {
/* Set an extract callback so that we can handle SIGINFO. */
+ progress_data.bsdtar = bsdtar;
+ progress_data.archive = a;
archive_read_extract_set_progress_callback(a, progress_func,
- bsdtar);
+ &progress_data);
}
if (mode == 'x' && bsdtar->option_chroot) {
@@ -161,6 +188,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
break;
r = archive_read_next_header(a, &entry);
+ progress_data.entry = entry;
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
@@ -268,10 +296,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
fflush(stderr);
}
- /* Tell the SIGINFO-handler code what we're doing. */
- siginfo_setinfo(bsdtar, "extracting",
- archive_entry_pathname(entry), 0);
- siginfo_printinfo(bsdtar, 0);
+ // TODO siginfo_printinfo(bsdtar, 0);
if (bsdtar->option_stdout)
r = archive_read_data_into_fd(a, 1);
diff --git a/usr.bin/tar/siginfo.c b/usr.bin/tar/siginfo.c
deleted file mode 100644
index b6b64f1..0000000
--- a/usr.bin/tar/siginfo.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*-
- * 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$");
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "bsdtar.h"
-#include "err.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(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
-#ifdef SIGUSR1
- /* ... and treat SIGUSR1 the same way as SIGINFO. */
- bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler);
-#endif
-}
-
-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(1, errno, "Cannot strdup");
- if ((bsdtar->siginfo->path = strdup(path)) == NULL)
- bsdtar_errc(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
-#ifdef SIGUSR1
- /* And the old SIGUSR1 handler, too. */
- signal(SIGUSR1, bsdtar->siginfo->sigusr1_old);
-#endif
-
- /* Free strings. */
- free(bsdtar->siginfo->path);
- free(bsdtar->siginfo->oper);
-
- /* Free internal data structure. */
- free(bsdtar->siginfo);
-}
diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c
index 82a018f..b3df5f0 100644
--- a/usr.bin/tar/util.c
+++ b/usr.bin/tar/util.c
@@ -559,6 +559,28 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
}
/*
+ * It would be nice to just use printf() for formatting large numbers,
+ * but the compatibility problems are quite a headache. Hence the
+ * following simple utility function.
+ */
+const char *
+tar_i64toa(int64_t n0)
+{
+ static char buff[24];
+ int64_t n = n0 < 0 ? -n0 : n0;
+ char *p = buff + sizeof(buff);
+
+ *--p = '\0';
+ do {
+ *--p = '0' + (int)(n % 10);
+ n /= 10;
+ } while (n > 0);
+ if (n0 < 0)
+ *--p = '-';
+ return p;
+}
+
+/*
* Like strcmp(), but try to be a little more aware of the fact that
* we're comparing two paths. Right now, it just handles leading
* "./" and trailing '/' specially, so that "a/b/" == "./a/b"
diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c
index 78e555c..f236876 100644
--- a/usr.bin/tar/write.c
+++ b/usr.bin/tar/write.c
@@ -127,10 +127,12 @@ static void archive_names_from_file(struct bsdtar *bsdtar,
struct archive *a);
static int archive_names_from_file_helper(struct bsdtar *bsdtar,
const char *line);
-static int copy_file_data(struct bsdtar *bsdtar,
- struct archive *a, struct archive *ina);
+static int copy_file_data(struct bsdtar *, struct archive *a,
+ struct archive *ina, struct archive_entry *);
static int new_enough(struct bsdtar *, const char *path,
const struct stat *);
+static void report_write(struct bsdtar *, struct archive *,
+ struct archive_entry *, int64_t progress);
static void test_for_append(struct bsdtar *);
static void write_archive(struct archive *, struct bsdtar *);
static void write_entry_backend(struct bsdtar *, struct archive *,
@@ -414,9 +416,6 @@ 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(1, 0, "cannot allocate memory");
@@ -488,14 +487,11 @@ cleanup:
bsdtar->diskreader = NULL;
if (bsdtar->option_totals) {
- fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n",
- (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a));
+ fprintf(stderr, "Total bytes written: %s\n",
+ tar_i64toa(archive_position_compressed(a)));
}
archive_write_finish(a);
-
- /* Restore old SIGINFO + SIGUSR1 handlers. */
- siginfo_done(bsdtar);
}
/*
@@ -591,10 +587,8 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
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);
+ if (need_report())
+ report_write(bsdtar, a, in_entry, 0);
e = archive_write_header(a, in_entry);
if (e != ARCHIVE_OK) {
@@ -611,7 +605,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *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))
+ else if (copy_file_data(bsdtar, a, ina, in_entry))
exit(1);
}
@@ -625,7 +619,8 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
/* Helper function to copy data between archives. */
static int
-copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
+copy_file_data(struct bsdtar *bsdtar, struct archive *a,
+ struct archive *ina, struct archive_entry *entry)
{
ssize_t bytes_read;
ssize_t bytes_written;
@@ -633,7 +628,8 @@ copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
bytes_read = archive_read_data(ina, bsdtar->buff, FILEDATABUFLEN);
while (bytes_read > 0) {
- siginfo_printinfo(bsdtar, progress);
+ if (need_report())
+ report_write(bsdtar, a, entry, progress);
bytes_written = archive_write_data(a, bsdtar->buff,
bytes_read);
@@ -839,14 +835,8 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
if (!S_ISREG(st->st_mode))
archive_entry_set_size(entry, 0);
- /* Record what we're doing, for SIGINFO / SIGUSR1. */
- siginfo_setinfo(bsdtar, "adding",
- archive_entry_pathname(entry), archive_entry_size(entry));
archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry);
- /* Handle SIGINFO / SIGUSR1 request if one was made. */
- siginfo_printinfo(bsdtar, 0);
-
while (entry != NULL) {
write_entry_backend(bsdtar, a, entry);
archive_entry_free(entry);
@@ -916,6 +906,28 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a,
close(fd);
}
+static void
+report_write(struct bsdtar *bsdtar, struct archive *a,
+ struct archive_entry *entry, int64_t progress)
+{
+ uint64_t comp, uncomp;
+ if (bsdtar->verbose)
+ fprintf(stderr, "\n");
+ comp = archive_position_compressed(a);
+ uncomp = archive_position_uncompressed(a);
+ fprintf(stderr, "In: %d files, %s bytes;",
+ archive_file_count(a), tar_i64toa(uncomp));
+ fprintf(stderr,
+ " Out: %s bytes, compression %d%%\n",
+ tar_i64toa(comp), (int)((uncomp - comp) * 100 / uncomp));
+ /* Can't have two calls to tar_i64toa() pending, so split the output. */
+ safe_fprintf(stderr, "Current: %s (%s",
+ archive_entry_pathname(entry),
+ tar_i64toa(progress));
+ fprintf(stderr, "/%s bytes)\n",
+ tar_i64toa(archive_entry_size(entry)));
+}
+
/* Helper function to copy file to archive. */
static int
@@ -928,7 +940,8 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a,
bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
while (bytes_read > 0) {
- siginfo_printinfo(bsdtar, progress);
+ if (need_report())
+ report_write(bsdtar, a, entry, progress);
bytes_written = archive_write_data(a, bsdtar->buff,
bytes_read);
OpenPOWER on IntegriCloud