summaryrefslogtreecommitdiff
path: root/tar/write.c
diff options
context:
space:
mode:
Diffstat (limited to 'tar/write.c')
-rw-r--r--tar/write.c650
1 files changed, 217 insertions, 433 deletions
diff --git a/tar/write.c b/tar/write.c
index 8c102d0ec13ba..273fdda3bd9f2 100644
--- a/tar/write.c
+++ b/tar/write.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,9 +30,6 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
@@ -56,20 +54,6 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
-#ifdef HAVE_LINUX_FS_H
-#include <linux/fs.h> /* for Linux file flags */
-#endif
-/*
- * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
- * As the include guards don't agree, the order of include is important.
- */
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h> /* for Linux file flags */
-#endif
-#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
-/* This header exists but is broken on Cygwin. */
-#include <ext2fs/ext2_fs.h>
-#endif
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
@@ -93,7 +77,6 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle
#include "bsdtar.h"
#include "err.h"
#include "line_reader.h"
-#include "tree.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -110,28 +93,27 @@ struct archive_dir {
struct archive_dir_entry *head, *tail;
};
-static void add_dir_list(struct bsdtar *bsdtar, const char *path,
- time_t mtime_sec, int mtime_nsec);
static int append_archive(struct bsdtar *, struct archive *,
struct archive *ina);
static int append_archive_filename(struct bsdtar *,
struct archive *, const char *fname);
static void archive_names_from_file(struct bsdtar *bsdtar,
struct archive *a);
-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 int copy_file_data_block(struct bsdtar *,
+ struct archive *a, struct archive *,
+ struct archive_entry *);
+static void excluded_callback(struct archive *, void *,
+ struct archive_entry *);
static void report_write(struct bsdtar *, struct archive *,
struct archive_entry *, int64_t progress);
static void test_for_append(struct bsdtar *);
+static int metadata_filter(struct archive *, void *,
+ struct archive_entry *);
static void write_archive(struct archive *, struct bsdtar *);
static void write_entry(struct bsdtar *, struct archive *,
struct archive_entry *);
static void write_file(struct bsdtar *, struct archive *,
struct archive_entry *);
-static int write_file_data(struct bsdtar *, struct archive *,
- struct archive_entry *, int fd, size_t align);
static void write_hierarchy(struct bsdtar *, struct archive *,
const char *);
@@ -360,9 +342,11 @@ tar_mode_u(struct bsdtar *bsdtar)
lafe_errc(1, 0,
"Cannot append to compressed archive.");
}
- add_dir_list(bsdtar, archive_entry_pathname(entry),
- archive_entry_mtime(entry),
- archive_entry_mtime_nsec(entry));
+ if (archive_match_exclude_entry(bsdtar->matching,
+ ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER |
+ ARCHIVE_MATCH_EQUAL, entry) != ARCHIVE_OK)
+ lafe_errc(1, 0, "Error : %s",
+ archive_error_string(bsdtar->matching));
/* Record the last format determination we see */
format = archive_format(a);
/* Keep going until we hit end-of-archive */
@@ -426,8 +410,30 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
lafe_errc(1, 0, "cannot create link resolver");
archive_entry_linkresolver_set_strategy(bsdtar->resolver,
archive_format(a));
+
+ /* Create a read_disk object. */
if ((bsdtar->diskreader = archive_read_disk_new()) == NULL)
lafe_errc(1, 0, "Cannot create read_disk object");
+ /* Tell the read_disk how handle symlink. */
+ switch (bsdtar->symlink_mode) {
+ case 'H':
+ archive_read_disk_set_symlink_hybrid(bsdtar->diskreader);
+ break;
+ case 'L':
+ archive_read_disk_set_symlink_logical(bsdtar->diskreader);
+ break;
+ default:
+ archive_read_disk_set_symlink_physical(bsdtar->diskreader);
+ break;
+ }
+ /* Register entry filters. */
+ archive_read_disk_set_matching(bsdtar->diskreader,
+ bsdtar->matching, excluded_callback, bsdtar);
+ archive_read_disk_set_metadata_filter_callback(
+ bsdtar->diskreader, metadata_filter, bsdtar);
+ /* Set the behavior of archive_read_disk. */
+ archive_read_disk_set_behavior(bsdtar->diskreader,
+ bsdtar->readdisk_flags);
archive_read_disk_set_standard_lookup(bsdtar->diskreader);
if (bsdtar->names_from_file != NULL)
@@ -467,11 +473,57 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
bsdtar->argv++;
}
+ archive_read_disk_set_matching(bsdtar->diskreader, NULL, NULL, NULL);
+ archive_read_disk_set_metadata_filter_callback(
+ bsdtar->diskreader, NULL, NULL);
entry = NULL;
archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
while (entry != NULL) {
+ int r;
+ struct archive_entry *entry2;
+ struct archive *disk = bsdtar->diskreader;
+
+ /*
+ * This tricky code here is to correctly read the cotents
+ * of the entry because the disk reader bsdtar->diskreader
+ * is pointing at does not have any information about the
+ * entry by this time and using archive_read_data_block()
+ * with the disk reader consequently must fail. And we
+ * have to re-open the entry to read the contents.
+ */
+ /* TODO: Work with -C option as well. */
+ r = archive_read_disk_open(disk,
+ archive_entry_sourcepath(entry));
+ if (r != ARCHIVE_OK) {
+ lafe_warnc(archive_errno(disk),
+ "%s", archive_error_string(disk));
+ bsdtar->return_value = 1;
+ archive_entry_free(entry);
+ continue;
+ }
+
+ /*
+ * Invoke archive_read_next_header2() to work
+ * archive_read_data_block(), which is called via write_file(),
+ * without failure.
+ */
+ entry2 = archive_entry_new();
+ r = archive_read_next_header2(disk, entry2);
+ archive_entry_free(entry2);
+ if (r != ARCHIVE_OK) {
+ lafe_warnc(archive_errno(disk),
+ "%s", archive_error_string(disk));
+ if (r == ARCHIVE_FATAL)
+ bsdtar->return_value = 1;
+ else
+ archive_read_close(disk);
+ archive_entry_free(entry);
+ continue;
+ }
+
write_file(bsdtar, a, entry);
archive_entry_free(entry);
+ archive_read_close(disk);
entry = NULL;
archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
}
@@ -584,10 +636,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
int e;
while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) {
- if (!new_enough(bsdtar, archive_entry_pathname(in_entry),
- archive_entry_stat(in_entry)))
- continue;
- if (lafe_excluded(bsdtar->matching, archive_entry_pathname(in_entry)))
+ if (archive_match_excluded(bsdtar->matching, in_entry))
continue;
if (bsdtar->option_interactive &&
!yes("copy '%s'", archive_entry_pathname(in_entry)))
@@ -613,7 +662,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, in_entry))
+ else if (copy_file_data_block(bsdtar, a, ina, in_entry))
exit(1);
}
@@ -624,204 +673,168 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
return (e == ARCHIVE_EOF ? ARCHIVE_OK : e);
}
-/* Helper function to copy data between archives. */
+/* Helper function to copy file to archive. */
static int
-copy_file_data(struct bsdtar *bsdtar, struct archive *a,
- struct archive *ina, struct archive_entry *entry)
+copy_file_data_block(struct bsdtar *bsdtar, struct archive *a,
+ struct archive *in_a, struct archive_entry *entry)
{
- ssize_t bytes_read;
+ size_t bytes_read;
ssize_t bytes_written;
- int64_t progress = 0;
+ int64_t offset, progress = 0;
+ char *null_buff = NULL;
+ const void *buff;
+ int r;
- bytes_read = archive_read_data(ina, bsdtar->buff, bsdtar->buff_size);
- while (bytes_read > 0) {
+ while ((r = archive_read_data_block(in_a, &buff,
+ &bytes_read, &offset)) == ARCHIVE_OK) {
if (need_report())
report_write(bsdtar, a, entry, progress);
- bytes_written = archive_write_data(a, bsdtar->buff,
- bytes_read);
- if (bytes_written < bytes_read) {
+ if (offset > progress) {
+ int64_t sparse = offset - progress;
+ size_t ns;
+
+ if (null_buff == NULL) {
+ null_buff = bsdtar->buff;
+ memset(null_buff, 0, bsdtar->buff_size);
+ }
+
+ while (sparse > 0) {
+ if (sparse > (int64_t)bsdtar->buff_size)
+ ns = bsdtar->buff_size;
+ else
+ ns = (size_t)sparse;
+ bytes_written =
+ archive_write_data(a, null_buff, ns);
+ if (bytes_written < 0) {
+ /* Write failed; this is bad */
+ lafe_warnc(0, "%s",
+ archive_error_string(a));
+ return (-1);
+ }
+ if ((size_t)bytes_written < ns) {
+ /* Write was truncated; warn but
+ * continue. */
+ lafe_warnc(0,
+ "%s: Truncated write; file may "
+ "have grown while being archived.",
+ archive_entry_pathname(entry));
+ return (0);
+ }
+ progress += bytes_written;
+ sparse -= bytes_written;
+ }
+ }
+
+ bytes_written = archive_write_data(a, buff, bytes_read);
+ if (bytes_written < 0) {
+ /* Write failed; this is bad */
lafe_warnc(0, "%s", archive_error_string(a));
return (-1);
}
+ if ((size_t)bytes_written < bytes_read) {
+ /* Write was truncated; warn but continue. */
+ lafe_warnc(0,
+ "%s: Truncated write; file may have grown "
+ "while being archived.",
+ archive_entry_pathname(entry));
+ return (0);
+ }
progress += bytes_written;
- bytes_read = archive_read_data(ina, bsdtar->buff, bsdtar->buff_size);
}
-
+ if (r < ARCHIVE_WARN) {
+ lafe_warnc(archive_errno(a), "%s", archive_error_string(a));
+ return (-1);
+ }
return (0);
}
+static void
+excluded_callback(struct archive *a, void *_data, struct archive_entry *entry)
+{
+ struct bsdtar *bsdtar = (struct bsdtar *)_data;
+
+ if (bsdtar->option_no_subdirs)
+ return;
+ if (!archive_read_disk_can_descend(a))
+ return;
+ if (bsdtar->option_interactive &&
+ !yes("add '%s'", archive_entry_pathname(entry)))
+ return;
+ archive_read_disk_descend(a);
+}
+
+static int
+metadata_filter(struct archive *a, void *_data, struct archive_entry *entry)
+{
+ struct bsdtar *bsdtar = (struct bsdtar *)_data;
+
+ /* XXX TODO: check whether this filesystem is
+ * synthetic and/or local. Add a new
+ * --local-only option to skip non-local
+ * filesystems. Skip synthetic filesystems
+ * regardless.
+ *
+ * The results should be cached, since
+ * tree.c doesn't usually visit a directory
+ * and the directory contents together. A simple
+ * move-to-front list should perform quite well.
+ *
+ * Use archive_read_disk_current_filesystem_is_remote().
+ */
+
+ /*
+ * If the user vetoes this file/directory, skip it.
+ * We want this to be fairly late; if some other
+ * check would veto this file, we shouldn't bother
+ * the user with it.
+ */
+ if (bsdtar->option_interactive &&
+ !yes("add '%s'", archive_entry_pathname(entry)))
+ return (0);
+
+ /* Note: if user vetoes, we won't descend. */
+ if (!bsdtar->option_no_subdirs && archive_read_disk_can_descend(a))
+ archive_read_disk_descend(a);
+
+ return (1);
+}
+
/*
* Add the file or dir hierarchy named by 'path' to the archive
*/
static void
write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
{
+ struct archive *disk = bsdtar->diskreader;
struct archive_entry *entry = NULL, *spare_entry = NULL;
- struct tree *tree;
- char symlink_mode = bsdtar->symlink_mode;
- dev_t first_dev = 0;
- int dev_recorded = 0;
- int tree_ret;
-
- tree = tree_open(path);
+ int r;
- if (!tree) {
- lafe_warnc(errno, "%s: Cannot open", path);
+ r = archive_read_disk_open(disk, path);
+ if (r != ARCHIVE_OK) {
+ lafe_warnc(archive_errno(disk),
+ "%s", archive_error_string(disk));
bsdtar->return_value = 1;
return;
}
+ bsdtar->first_fs = -1;
- while ((tree_ret = tree_next(tree)) != 0) {
- int r;
- const char *name = tree_current_path(tree);
- const struct stat *st = NULL; /* info to use for this entry */
- const struct stat *lst = NULL; /* lstat() information */
- int descend;
-
- if (tree_ret == TREE_ERROR_FATAL)
- lafe_errc(1, tree_errno(tree),
- "%s: Unable to continue traversing directory tree",
- name);
- if (tree_ret == TREE_ERROR_DIR) {
- lafe_warnc(errno,
- "%s: Couldn't visit directory", name);
- bsdtar->return_value = 1;
- }
- if (tree_ret != TREE_REGULAR)
- continue;
-
- /*
- * If this file/dir is excluded by a filename
- * pattern, skip it.
- */
- if (lafe_excluded(bsdtar->matching, name))
- continue;
-
- /*
- * Get lstat() info from the tree library.
- */
- lst = tree_current_lstat(tree);
- if (lst == NULL) {
- /* Couldn't lstat(); must not exist. */
- lafe_warnc(errno, "%s: Cannot stat", name);
- /* Return error if files disappear during traverse. */
- bsdtar->return_value = 1;
- continue;
- }
-
- /*
- * Distinguish 'L'/'P'/'H' symlink following.
- */
- switch(symlink_mode) {
- case 'H':
- /* 'H': After the first item, rest like 'P'. */
- symlink_mode = 'P';
- /* 'H': First item (from command line) like 'L'. */
- /* FALLTHROUGH */
- case 'L':
- /* 'L': Do descend through a symlink to dir. */
- descend = tree_current_is_dir(tree);
- /* 'L': Follow symlinks to files. */
- archive_read_disk_set_symlink_logical(bsdtar->diskreader);
- /* 'L': Archive symlinks as targets, if we can. */
- st = tree_current_stat(tree);
- if (st != NULL)
- break;
- /* If stat fails, we have a broken symlink;
- * in that case, don't follow the link. */
- /* FALLTHROUGH */
- default:
- /* 'P': Don't descend through a symlink to dir. */
- descend = tree_current_is_physical_dir(tree);
- /* 'P': Don't follow symlinks to files. */
- archive_read_disk_set_symlink_physical(bsdtar->diskreader);
- /* 'P': Archive symlinks as symlinks. */
- st = lst;
+ for (;;) {
+ archive_entry_free(entry);
+ entry = archive_entry_new();
+ r = archive_read_next_header2(disk, entry);
+ if (r == ARCHIVE_EOF)
break;
- }
-
- if (bsdtar->option_no_subdirs)
- descend = 0;
-
- /*
- * Are we about to cross to a new filesystem?
- */
- if (!dev_recorded) {
- /* This is the initial file system. */
- first_dev = lst->st_dev;
- dev_recorded = 1;
- } else if (lst->st_dev == first_dev) {
- /* The starting file system is always acceptable. */
- } else if (descend == 0) {
- /* We're not descending, so no need to check. */
- } else if (bsdtar->option_dont_traverse_mounts) {
- descend = 0;
- } else {
- /* We're prepared to cross a mount point. */
-
- /* XXX TODO: check whether this filesystem is
- * synthetic and/or local. Add a new
- * --local-only option to skip non-local
- * filesystems. Skip synthetic filesystems
- * regardless.
- *
- * The results should be cached, since
- * tree.c doesn't usually visit a directory
- * and the directory contents together. A simple
- * move-to-front list should perform quite well.
- *
- * This is going to be heavily OS dependent:
- * FreeBSD's statfs() in conjunction with getvfsbyname()
- * provides all of this; NetBSD's statvfs() does
- * most of it; other systems will vary.
- */
- }
-
- /*
- * In -u mode, check that the file is newer than what's
- * already in the archive; in all modes, obey --newerXXX flags.
- */
- if (!new_enough(bsdtar, name, st)) {
- if (!descend)
- continue;
- if (bsdtar->option_interactive &&
- !yes("add '%s'", name))
+ else if (r != ARCHIVE_OK) {
+ lafe_warnc(archive_errno(disk),
+ "%s", archive_error_string(disk));
+ if (r == ARCHIVE_FATAL) {
+ bsdtar->return_value = 1;
+ return;
+ } else if (r < ARCHIVE_WARN)
continue;
- tree_descend(tree);
- continue;
}
- archive_entry_free(entry);
- entry = archive_entry_new();
-
- archive_entry_set_pathname(entry, name);
- archive_entry_copy_sourcepath(entry,
- tree_current_access_path(tree));
-
- /* Populate the archive_entry with metadata from the disk. */
- /* XXX TODO: Arrange to open a regular file before
- * calling this so we can pass in an fd and shorten
- * the race to query metadata. The linkify dance
- * makes this more complex than it might sound. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
- /* TODO: tree.c uses stat(), which is badly broken
- * on Windows. To fix this, we should
- * deprecate tree_current_stat() and provide a new
- * call tree_populate_entry(t, entry). This call
- * would use stat() internally on POSIX and
- * GetInfoByFileHandle() internally on Windows.
- * This would be another step towards a tree-walker
- * that can be integrated deep into libarchive.
- * For now, just set st to NULL on Windows;
- * archive_read_disk_entry_from_file() should
- * be smart enough to use platform-appropriate
- * ways to probe file information.
- */
- st = NULL;
-#endif
- r = archive_read_disk_entry_from_file(bsdtar->diskreader,
- entry, -1, st);
if (bsdtar->uid >= 0) {
archive_entry_set_uid(entry, bsdtar->uid);
if (!bsdtar->uname)
@@ -840,68 +853,6 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
archive_entry_set_uname(entry, bsdtar->uname);
if (bsdtar->gname)
archive_entry_set_gname(entry, bsdtar->gname);
- if (r != ARCHIVE_OK)
- lafe_warnc(archive_errno(bsdtar->diskreader),
- "%s", archive_error_string(bsdtar->diskreader));
- if (r < ARCHIVE_WARN)
- continue;
-
- /* XXX TODO: Just use flag data from entry; avoid the
- * duplicate check here. */
-
- /*
- * If this file/dir is flagged "nodump" and we're
- * honoring such flags, skip this file/dir.
- */
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
- /* BSD systems store flags in struct stat */
- if (bsdtar->option_honor_nodump &&
- (lst->st_flags & UF_NODUMP))
- continue;
-#endif
-
-#if defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
- /* Linux uses ioctl to read flags. */
- if (bsdtar->option_honor_nodump) {
- int fd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY);
- if (fd >= 0) {
- unsigned long fflags;
- int r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags);
- close(fd);
- if (r >= 0 && (fflags & EXT2_NODUMP_FL))
- continue;
- }
- }
-#endif
-
-#ifdef __APPLE__
- if (bsdtar->enable_copyfile) {
- /* If we're using copyfile(), ignore "._XXX" files. */
- const char *bname = strrchr(name, '/');
- if (bname == NULL)
- bname = name;
- else
- ++bname;
- if (bname[0] == '.' && bname[1] == '_')
- continue;
- } else {
- /* If not, drop the copyfile() data. */
- archive_entry_copy_mac_metadata(entry, NULL, 0);
- }
-#endif
-
- /*
- * If the user vetoes this file/directory, skip it.
- * We want this to be fairly late; if some other
- * check would veto this file, we shouldn't bother
- * the user with it.
- */
- if (bsdtar->option_interactive &&
- !yes("add '%s'", name))
- continue;
-
- if (descend)
- tree_descend(tree);
/*
* Rewrite the pathname to be archived. If rewrite
@@ -933,7 +884,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
fprintf(stderr, "\n");
}
archive_entry_free(entry);
- tree_close(tree);
+ archive_read_close(disk);
}
/*
@@ -954,29 +905,7 @@ static void
write_entry(struct bsdtar *bsdtar, struct archive *a,
struct archive_entry *entry)
{
- int fd = -1;
int e;
- size_t align = 4096;
-
- if (archive_entry_size(entry) > 0) {
- const char *pathname = archive_entry_sourcepath(entry);
- /* TODO: Use O_DIRECT here and set 'align' to the
- * actual filesystem block size. As of July 2010, new
- * directory-traversal code is going in that will make
- * it much easier to track filesystem properties like
- * this during the traversal. */
- fd = open(pathname, O_RDONLY | O_BINARY);
- align = 4096;
- if (fd == -1) {
- bsdtar->return_value = 1;
- if (!bsdtar->verbose)
- lafe_warnc(errno,
- "%s: could not open file", pathname);
- else
- fprintf(stderr, ": %s", strerror(errno));
- return;
- }
- }
e = archive_write_header(a, entry);
if (e != ARCHIVE_OK) {
@@ -997,17 +926,10 @@ write_entry(struct bsdtar *bsdtar, struct archive *a,
* 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, align))
+ if (e >= ARCHIVE_WARN && archive_entry_size(entry) > 0) {
+ if (copy_file_data_block(bsdtar, a, bsdtar->diskreader, entry))
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);
}
static void
@@ -1038,144 +960,6 @@ report_write(struct bsdtar *bsdtar, struct archive *a,
tar_i64toa(archive_entry_size(entry)));
}
-
-/* Helper function to copy file to archive. */
-static int
-write_file_data(struct bsdtar *bsdtar, struct archive *a,
- struct archive_entry *entry, int fd, size_t align)
-{
- ssize_t bytes_read;
- ssize_t bytes_written;
- int64_t progress = 0;
- size_t buff_size;
- char *buff = bsdtar->buff;
-
- /* Round 'buff' up to the next multiple of 'align' and reduce
- * 'buff_size' accordingly. */
- buff = (char *)((((uintptr_t)buff + align - 1) / align) * align);
- buff_size = bsdtar->buff + bsdtar->buff_size - buff;
- buff_size = (buff_size / align) * align;
-
- bytes_read = read(fd, buff, buff_size);
- while (bytes_read > 0) {
- if (need_report())
- report_write(bsdtar, a, entry, progress);
-
- bytes_written = archive_write_data(a, buff, bytes_read);
- if (bytes_written < 0) {
- /* Write failed; this is bad */
- lafe_warnc(0, "%s", archive_error_string(a));
- return (-1);
- }
- if (bytes_written < bytes_read) {
- /* Write was truncated; warn but continue. */
- lafe_warnc(0,
- "%s: Truncated write; file may have grown while being archived.",
- archive_entry_pathname(entry));
- return (0);
- }
- progress += bytes_written;
- bytes_read = read(fd, buff, buff_size);
- }
- if (bytes_read < 0) {
- lafe_warnc(errno,
- "%s: Read error",
- archive_entry_pathname(entry));
- bsdtar->return_value = 1;
- }
- return 0;
-}
-
-/*
- * Test if the specified file is new enough to include in the archive.
- */
-static int
-new_enough(struct bsdtar *bsdtar, const char *path, const struct stat *st)
-{
- struct archive_dir_entry *p;
-
- /*
- * If this file/dir is excluded by a time comparison, skip it.
- */
- if (bsdtar->newer_ctime_filter) {
- if (st->st_ctime < bsdtar->newer_ctime_sec)
- return (0); /* Too old, skip it. */
- if (st->st_ctime == bsdtar->newer_ctime_sec
- && ARCHIVE_STAT_CTIME_NANOS(st)
- <= bsdtar->newer_ctime_nsec)
- return (0); /* Too old, skip it. */
- }
- if (bsdtar->newer_mtime_filter) {
- if (st->st_mtime < bsdtar->newer_mtime_sec)
- return (0); /* Too old, skip it. */
- if (st->st_mtime == bsdtar->newer_mtime_sec
- && ARCHIVE_STAT_MTIME_NANOS(st)
- <= bsdtar->newer_mtime_nsec)
- return (0); /* Too old, skip it. */
- }
-
- /*
- * In -u mode, we only write an entry if it's newer than
- * what was already in the archive.
- */
- if (bsdtar->archive_dir != NULL &&
- bsdtar->archive_dir->head != NULL) {
- for (p = bsdtar->archive_dir->head; p != NULL; p = p->next) {
- if (pathcmp(path, p->name)==0)
- return (p->mtime_sec < st->st_mtime ||
- (p->mtime_sec == st->st_mtime &&
- p->mtime_nsec
- < ARCHIVE_STAT_MTIME_NANOS(st)));
- }
- }
-
- /* If the file wasn't rejected, include it. */
- return (1);
-}
-
-/*
- * Add an entry to the dir list for 'u' mode.
- *
- * XXX TODO: Make this fast.
- */
-static void
-add_dir_list(struct bsdtar *bsdtar, const char *path,
- time_t mtime_sec, int mtime_nsec)
-{
- struct archive_dir_entry *p;
-
- /*
- * Search entire list to see if this file has appeared before.
- * If it has, override the timestamp data.
- */
- p = bsdtar->archive_dir->head;
- while (p != NULL) {
- if (strcmp(path, p->name)==0) {
- p->mtime_sec = mtime_sec;
- p->mtime_nsec = mtime_nsec;
- return;
- }
- p = p->next;
- }
-
- p = malloc(sizeof(*p));
- if (p == NULL)
- lafe_errc(1, ENOMEM, "Can't read archive directory");
-
- p->name = strdup(path);
- if (p->name == NULL)
- lafe_errc(1, ENOMEM, "Can't read archive directory");
- p->mtime_sec = mtime_sec;
- p->mtime_nsec = mtime_nsec;
- p->next = NULL;
- if (bsdtar->archive_dir->tail == NULL) {
- bsdtar->archive_dir->head = bsdtar->archive_dir->tail = p;
- } else {
- bsdtar->archive_dir->tail->next = p;
- bsdtar->archive_dir->tail = p;
- }
-}
-
static void
test_for_append(struct bsdtar *bsdtar)
{