svn commit: r185408 - in stable/7/lib/libarchive: . test
Tim Kientzle
kientzle at FreeBSD.org
Fri Nov 28 12:08:48 PST 2008
Author: kientzle
Date: Fri Nov 28 20:08:47 2008
New Revision: 185408
URL: http://svn.freebsd.org/changeset/base/185408
Log:
MFC 182590,182778,182779,184109: Support for certain
archive_entry attributes to be explicitly "unset".
This fixes a number of issues:
* Zip archives with "length at end" can now be
extracted correctly, since the Zip extractor
can simply mark the size as "unknown."
* Extract-to-disk can now accurately handle
cases with partial time information (e.g.,
mtime is known but not atime)
* We get more accurate handling of different
hardlink extraction cases; sometimes a hardlink
entry has definitive size information and sometimes
not.
Approved by: re
Modified:
stable/7/lib/libarchive/ (props changed)
stable/7/lib/libarchive/archive_entry.c
stable/7/lib/libarchive/archive_entry.h
stable/7/lib/libarchive/archive_entry_private.h
stable/7/lib/libarchive/archive_read_support_format_zip.c
stable/7/lib/libarchive/archive_write_disk.c
stable/7/lib/libarchive/test/test_entry.c
stable/7/lib/libarchive/test/test_read_format_zip.c
stable/7/lib/libarchive/test/test_read_format_zip.zip.uu
stable/7/lib/libarchive/test/test_write_disk.c
stable/7/lib/libarchive/test/test_write_disk_hardlink.c
Modified: stable/7/lib/libarchive/archive_entry.c
==============================================================================
--- stable/7/lib/libarchive/archive_entry.c Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/archive_entry.c Fri Nov 28 20:08:47 2008 (r185408)
@@ -395,8 +395,7 @@ archive_entry_clone(struct archive_entry
aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
- entry2->ae_hardlinkset = entry->ae_hardlinkset;
- entry2->ae_symlinkset = entry->ae_symlinkset;
+ entry2->ae_set = entry->ae_set;
aes_copy(&entry2->ae_uname, &entry->ae_uname);
/* Copy ACL data over. */
@@ -455,12 +454,24 @@ archive_entry_atime_nsec(struct archive_
return (entry->ae_stat.aest_atime_nsec);
}
+int
+archive_entry_atime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_ATIME);
+}
+
time_t
archive_entry_ctime(struct archive_entry *entry)
{
return (entry->ae_stat.aest_ctime);
}
+int
+archive_entry_ctime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_CTIME);
+}
+
long
archive_entry_ctime_nsec(struct archive_entry *entry)
{
@@ -562,17 +573,17 @@ archive_entry_gname_w(struct archive_ent
const char *
archive_entry_hardlink(struct archive_entry *entry)
{
- if (!entry->ae_hardlinkset)
- return (NULL);
- return (aes_get_mbs(&entry->ae_hardlink));
+ if (entry->ae_set & AE_SET_HARDLINK)
+ return (aes_get_mbs(&entry->ae_hardlink));
+ return (NULL);
}
const wchar_t *
archive_entry_hardlink_w(struct archive_entry *entry)
{
- if (!entry->ae_hardlinkset)
- return (NULL);
- return (aes_get_wcs(&entry->ae_hardlink));
+ if (entry->ae_set & AE_SET_HARDLINK)
+ return (aes_get_wcs(&entry->ae_hardlink));
+ return (NULL);
}
ino_t
@@ -599,6 +610,12 @@ archive_entry_mtime_nsec(struct archive_
return (entry->ae_stat.aest_mtime_nsec);
}
+int
+archive_entry_mtime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_MTIME);
+}
+
unsigned int
archive_entry_nlink(struct archive_entry *entry)
{
@@ -651,6 +668,12 @@ archive_entry_size(struct archive_entry
return (entry->ae_stat.aest_size);
}
+int
+archive_entry_size_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_SIZE);
+}
+
const char *
archive_entry_sourcepath(struct archive_entry *entry)
{
@@ -660,17 +683,17 @@ archive_entry_sourcepath(struct archive_
const char *
archive_entry_symlink(struct archive_entry *entry)
{
- if (!entry->ae_symlinkset)
- return (NULL);
- return (aes_get_mbs(&entry->ae_symlink));
+ if (entry->ae_set & AE_SET_SYMLINK)
+ return (aes_get_mbs(&entry->ae_symlink));
+ return (NULL);
}
const wchar_t *
archive_entry_symlink_w(struct archive_entry *entry)
{
- if (!entry->ae_symlinkset)
- return (NULL);
- return (aes_get_wcs(&entry->ae_symlink));
+ if (entry->ae_set & AE_SET_SYMLINK)
+ return (aes_get_wcs(&entry->ae_symlink));
+ return (NULL);
}
uid_t
@@ -773,7 +796,9 @@ archive_entry_set_hardlink(struct archiv
{
aes_set_mbs(&entry->ae_hardlink, target);
if (target != NULL)
- entry->ae_hardlinkset = 1;
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
}
void
@@ -781,7 +806,9 @@ archive_entry_copy_hardlink(struct archi
{
aes_copy_mbs(&entry->ae_hardlink, target);
if (target != NULL)
- entry->ae_hardlinkset = 1;
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
}
void
@@ -789,26 +816,44 @@ archive_entry_copy_hardlink_w(struct arc
{
aes_copy_wcs(&entry->ae_hardlink, target);
if (target != NULL)
- entry->ae_hardlinkset = 1;
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
}
void
archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
{
entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_ATIME;
entry->ae_stat.aest_atime = t;
entry->ae_stat.aest_atime_nsec = ns;
}
void
+archive_entry_unset_atime(struct archive_entry *entry)
+{
+ archive_entry_set_atime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_ATIME;
+}
+
+void
archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
{
entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_CTIME;
entry->ae_stat.aest_ctime = t;
entry->ae_stat.aest_ctime_nsec = ns;
}
void
+archive_entry_unset_ctime(struct archive_entry *entry)
+{
+ archive_entry_set_ctime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_CTIME;
+}
+
+void
archive_entry_set_dev(struct archive_entry *entry, dev_t d)
{
entry->stat_valid = 0;
@@ -836,7 +881,7 @@ archive_entry_set_devminor(struct archiv
void
archive_entry_set_link(struct archive_entry *entry, const char *target)
{
- if (entry->ae_symlinkset)
+ if (entry->ae_set & AE_SET_SYMLINK)
aes_set_mbs(&entry->ae_symlink, target);
else
aes_set_mbs(&entry->ae_hardlink, target);
@@ -846,7 +891,7 @@ archive_entry_set_link(struct archive_en
void
archive_entry_copy_link(struct archive_entry *entry, const char *target)
{
- if (entry->ae_symlinkset)
+ if (entry->ae_set & AE_SET_SYMLINK)
aes_copy_mbs(&entry->ae_symlink, target);
else
aes_copy_mbs(&entry->ae_hardlink, target);
@@ -856,7 +901,7 @@ archive_entry_copy_link(struct archive_e
void
archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
{
- if (entry->ae_symlinkset)
+ if (entry->ae_set & AE_SET_SYMLINK)
aes_copy_wcs(&entry->ae_symlink, target);
else
aes_copy_wcs(&entry->ae_hardlink, target);
@@ -865,7 +910,7 @@ archive_entry_copy_link_w(struct archive
int
archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
{
- if (entry->ae_symlinkset)
+ if (entry->ae_set & AE_SET_SYMLINK)
return (aes_update_utf8(&entry->ae_symlink, target));
else
return (aes_update_utf8(&entry->ae_hardlink, target));
@@ -882,11 +927,19 @@ void
archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
{
entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_MTIME;
entry->ae_stat.aest_mtime = m;
entry->ae_stat.aest_mtime_nsec = ns;
}
void
+archive_entry_unset_mtime(struct archive_entry *entry)
+{
+ archive_entry_set_mtime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_MTIME;
+}
+
+void
archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
{
entry->stat_valid = 0;
@@ -954,6 +1007,14 @@ archive_entry_set_size(struct archive_en
{
entry->stat_valid = 0;
entry->ae_stat.aest_size = s;
+ entry->ae_set |= AE_SET_SIZE;
+}
+
+void
+archive_entry_unset_size(struct archive_entry *entry)
+{
+ archive_entry_set_size(entry, 0);
+ entry->ae_set &= ~AE_SET_SIZE;
}
void
@@ -967,7 +1028,9 @@ archive_entry_set_symlink(struct archive
{
aes_set_mbs(&entry->ae_symlink, linkname);
if (linkname != NULL)
- entry->ae_symlinkset = 1;
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
}
void
@@ -975,7 +1038,9 @@ archive_entry_copy_symlink(struct archiv
{
aes_copy_mbs(&entry->ae_symlink, linkname);
if (linkname != NULL)
- entry->ae_symlinkset = 1;
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
}
void
@@ -983,7 +1048,9 @@ archive_entry_copy_symlink_w(struct arch
{
aes_copy_wcs(&entry->ae_symlink, linkname);
if (linkname != NULL)
- entry->ae_symlinkset = 1;
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
}
void
Modified: stable/7/lib/libarchive/archive_entry.h
==============================================================================
--- stable/7/lib/libarchive/archive_entry.h Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/archive_entry.h Fri Nov 28 20:08:47 2008 (r185408)
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2008 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -152,12 +152,29 @@ __LA_DECL struct archive_entry *archive_
/*
* Retrieve fields from an archive_entry.
+ *
+ * There are a number of implicit conversions among these fields. For
+ * example, if a regular string field is set and you read the _w wide
+ * character field, the entry will implicitly convert narrow-to-wide
+ * using the current locale. Similarly, dev values are automatically
+ * updated when you write devmajor or devminor and vice versa.
+ *
+ * In addition, fields can be "set" or "unset." Unset string fields
+ * return NULL, non-string fields have _is_set() functions to test
+ * whether they've been set. You can "unset" a string field by
+ * assigning NULL; non-string fields have _unset() functions to
+ * unset them.
+ *
+ * Note: There is one ambiguity in the above; string fields will
+ * also return NULL when implicit character set conversions fail.
+ * This is usually what you want.
*/
-
__LA_DECL time_t archive_entry_atime(struct archive_entry *);
__LA_DECL long archive_entry_atime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_atime_is_set(struct archive_entry *);
__LA_DECL time_t archive_entry_ctime(struct archive_entry *);
__LA_DECL long archive_entry_ctime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_ctime_is_set(struct archive_entry *);
__LA_DECL dev_t archive_entry_dev(struct archive_entry *);
__LA_DECL dev_t archive_entry_devmajor(struct archive_entry *);
__LA_DECL dev_t archive_entry_devminor(struct archive_entry *);
@@ -175,6 +192,7 @@ __LA_DECL __LA_INO_T archive_entry_ino(
__LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *);
__LA_DECL time_t archive_entry_mtime(struct archive_entry *);
__LA_DECL long archive_entry_mtime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_mtime_is_set(struct archive_entry *);
__LA_DECL unsigned int archive_entry_nlink(struct archive_entry *);
__LA_DECL const char *archive_entry_pathname(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *);
@@ -183,6 +201,7 @@ __LA_DECL dev_t archive_entry_rdevmajo
__LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *);
__LA_DECL const char *archive_entry_sourcepath(struct archive_entry *);
__LA_DECL int64_t archive_entry_size(struct archive_entry *);
+__LA_DECL int archive_entry_size_is_set(struct archive_entry *);
__LA_DECL const char *archive_entry_strmode(struct archive_entry *);
__LA_DECL const char *archive_entry_symlink(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
@@ -195,10 +214,16 @@ __LA_DECL const wchar_t *archive_entry_u
*
* Note that string 'set' functions do not copy the string, only the pointer.
* In contrast, 'copy' functions do copy the object pointed to.
+ *
+ * Note: As of libarchive 2.4, 'set' functions do copy the string and
+ * are therefore exact synonyms for the 'copy' versions. The 'copy'
+ * names will be retired in libarchive 3.0.
*/
__LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_atime(struct archive_entry *);
__LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_ctime(struct archive_entry *);
__LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t);
__LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t);
__LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t);
@@ -226,6 +251,7 @@ __LA_DECL void archive_entry_copy_link_w
__LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *);
__LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
__LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_mtime(struct archive_entry *);
__LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int);
__LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *);
__LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *);
@@ -236,6 +262,7 @@ __LA_DECL void archive_entry_set_rdev(st
__LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
__LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
__LA_DECL void archive_entry_set_size(struct archive_entry *, int64_t);
+__LA_DECL void archive_entry_unset_size(struct archive_entry *);
__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *);
__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
@@ -257,6 +284,7 @@ __LA_DECL int archive_entry_update_uname
__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+
/*
* ACL routines. This used to simply store and return text-format ACL
* strings, but that proved insufficient for a number of reasons:
Modified: stable/7/lib/libarchive/archive_entry_private.h
==============================================================================
--- stable/7/lib/libarchive/archive_entry_private.h Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/archive_entry_private.h Fri Nov 28 20:08:47 2008 (r185408)
@@ -136,6 +136,14 @@ struct archive_entry {
dev_t aest_rdevminor;
} ae_stat;
+ int ae_set; /* bitmap of fields that are currently set */
+#define AE_SET_HARDLINK 1
+#define AE_SET_SYMLINK 2
+#define AE_SET_ATIME 4
+#define AE_SET_CTIME 8
+#define AE_SET_MTIME 16
+#define AE_SET_SIZE 64
+
/*
* Use aes here so that we get transparent mbs<->wcs conversions.
*/
@@ -147,8 +155,6 @@ struct archive_entry {
struct aes ae_pathname; /* Name of entry */
struct aes ae_symlink; /* symlink contents */
struct aes ae_uname; /* Name of owner */
- unsigned char ae_hardlinkset;
- unsigned char ae_symlinkset;
/* Not used within libarchive; useful for some clients. */
struct aes ae_sourcepath; /* Path this entry is sourced from. */
Modified: stable/7/lib/libarchive/archive_read_support_format_zip.c
==============================================================================
--- stable/7/lib/libarchive/archive_read_support_format_zip.c Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/archive_read_support_format_zip.c Fri Nov 28 20:08:47 2008 (r185408)
@@ -444,7 +444,9 @@ zip_read_file_header(struct archive_read
archive_entry_set_mtime(entry, zip->mtime, 0);
archive_entry_set_ctime(entry, zip->ctime, 0);
archive_entry_set_atime(entry, zip->atime, 0);
- archive_entry_set_size(entry, zip->uncompressed_size);
+ /* Set the size only if it's meaningful. */
+ if (0 == (zip->flags & ZIP_LENGTH_AT_END))
+ archive_entry_set_size(entry, zip->uncompressed_size);
zip->entry_bytes_remaining = zip->compressed_size;
zip->entry_offset = 0;
@@ -573,12 +575,16 @@ archive_read_format_zip_read_data(struct
}
break;
}
+ if (r != ARCHIVE_OK)
+ return (r);
/* Update checksum */
- if (r == ARCHIVE_OK && *size) {
+ if (*size)
zip->entry_crc32 =
crc32(zip->entry_crc32, *buff, *size);
- }
- return (r);
+ /* Return EOF immediately if this is a non-regular file. */
+ if (AE_IFREG != (zip->mode & AE_IFMT))
+ return (ARCHIVE_EOF);
+ return (ARCHIVE_OK);
}
/*
Modified: stable/7/lib/libarchive/archive_write_disk.c
==============================================================================
--- stable/7/lib/libarchive/archive_write_disk.c Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/archive_write_disk.c Fri Nov 28 20:08:47 2008 (r185408)
@@ -176,7 +176,7 @@ struct archive_write_disk {
int fd;
/* Current offset for writing data to the file. */
off_t offset;
- /* Maximum size of file. */
+ /* Maximum size of file, -1 if unknown. */
off_t filesize;
/* Dir we were in before this restore; only for deep paths. */
int restore_pwd;
@@ -231,7 +231,8 @@ static int set_time(struct archive_write
static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
static gid_t trivial_lookup_gid(void *, const char *, gid_t);
static uid_t trivial_lookup_uid(void *, const char *, uid_t);
-
+static ssize_t write_data_block(struct archive_write_disk *,
+ const char *, size_t, off_t);
static struct archive_vtable *archive_write_disk_vtable(void);
@@ -337,7 +338,10 @@ _archive_write_header(struct archive *_a
a->offset = 0;
a->uid = a->user_uid;
a->mode = archive_entry_mode(a->entry);
- a->filesize = archive_entry_size(a->entry);
+ if (archive_entry_size_is_set(a->entry))
+ a->filesize = archive_entry_size(a->entry);
+ else
+ a->filesize = -1;
archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
a->name = a->_name_data.s;
archive_clear_error(&a->archive);
@@ -439,15 +443,25 @@ _archive_write_header(struct archive *_a
fe->mode = a->mode;
}
- if (a->deferred & TODO_TIMES) {
+ if ((a->deferred & TODO_TIMES)
+ && (archive_entry_mtime_is_set(entry)
+ || archive_entry_atime_is_set(entry))) {
fe = current_fixup(a, archive_entry_pathname(entry));
fe->fixup |= TODO_TIMES;
- fe->mtime = archive_entry_mtime(entry);
- fe->mtime_nanos = archive_entry_mtime_nsec(entry);
- fe->atime = archive_entry_atime(entry);
- fe->atime_nanos = archive_entry_atime_nsec(entry);
- if (fe->atime == 0 && fe->atime_nanos == 0)
+ if (archive_entry_mtime_is_set(entry)) {
+ fe->mtime = archive_entry_mtime(entry);
+ fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+ } else {
+ fe->mtime = a->start_time;
+ fe->mtime_nanos = 0;
+ }
+ if (archive_entry_atime_is_set(entry)) {
+ fe->atime = archive_entry_atime(entry);
+ fe->atime_nanos = archive_entry_atime_nsec(entry);
+ } else {
fe->atime = a->start_time;
+ fe->atime_nanos = 0;
+ }
}
if (a->deferred & TODO_FFLAGS) {
@@ -486,89 +500,113 @@ archive_write_disk_set_skip_file(struct
}
static ssize_t
-_archive_write_data_block(struct archive *_a,
- const void *buff, size_t size, off_t offset)
+write_data_block(struct archive_write_disk *a,
+ const char *buff, size_t size, off_t offset)
{
- struct archive_write_disk *a = (struct archive_write_disk *)_a;
ssize_t bytes_written = 0;
- ssize_t block_size, bytes_to_write;
- int r = ARCHIVE_OK;
+ ssize_t block_size = 0, bytes_to_write;
+ int r;
- __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
- ARCHIVE_STATE_DATA, "archive_write_disk_block");
- if (a->fd < 0) {
- archive_set_error(&a->archive, 0, "File not open");
+ if (a->filesize == 0 || a->fd < 0) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
return (ARCHIVE_WARN);
}
- archive_clear_error(&a->archive);
if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
return (r);
block_size = a->pst->st_blksize;
- } else
- block_size = -1;
-
- if ((off_t)(offset + size) > a->filesize) {
- size = (size_t)(a->filesize - a->offset);
- archive_set_error(&a->archive, 0,
- "Write request too large");
- r = ARCHIVE_WARN;
}
+ if (a->filesize >= 0 && (off_t)(offset + size) > a->filesize)
+ size = (size_t)(a->filesize - offset);
+
/* Write the data. */
while (size > 0) {
- if (block_size != -1) {
- const char *buf;
-
- for (buf = buff; size; ++buf, --size, ++offset) {
- if (*buf != '\0')
+ if (block_size == 0) {
+ bytes_to_write = size;
+ } else {
+ /* We're sparsifying the file. */
+ const char *p, *end;
+ off_t block_end;
+
+ /* Skip leading zero bytes. */
+ for (p = buff, end = buff + size; p < end; ++p) {
+ if (*p != '\0')
break;
}
+ offset += p - buff;
+ size -= p - buff;
+ buff = p;
if (size == 0)
break;
- bytes_to_write = block_size - offset % block_size;
- buff = buf;
- } else
+
+ /* Calculate next block boundary after offset. */
+ block_end
+ = (offset / block_size) * block_size + block_size;
+
+ /* If the adjusted write would cross block boundary,
+ * truncate it to the block boundary. */
bytes_to_write = size;
+ if (offset + bytes_to_write > block_end)
+ bytes_to_write = block_end - offset;
+ }
+
/* Seek if necessary to the specified offset. */
if (offset != a->last_offset) {
if (lseek(a->fd, offset, SEEK_SET) < 0) {
- archive_set_error(&a->archive, errno, "Seek failed");
+ archive_set_error(&a->archive, errno,
+ "Seek failed");
return (ARCHIVE_FATAL);
}
}
- bytes_written = write(a->fd, buff, size);
+ bytes_written = write(a->fd, buff, bytes_to_write);
if (bytes_written < 0) {
archive_set_error(&a->archive, errno, "Write failed");
return (ARCHIVE_WARN);
}
- buff = (const char *)buff + bytes_written;
+ buff += bytes_written;
size -= bytes_written;
offset += bytes_written;
a->archive.file_position += bytes_written;
a->archive.raw_position += bytes_written;
a->last_offset = a->offset = offset;
}
- a->offset = offset;
- return (r);
+ return (bytes_written);
+}
+
+static ssize_t
+_archive_write_data_block(struct archive *_a,
+ const void *buff, size_t size, off_t offset)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ ssize_t r;
+
+ __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_disk_block");
+
+ r = write_data_block(a, buff, size, offset);
+
+ if (r < 0)
+ return (r);
+ if ((size_t)r < size) {
+ archive_set_error(&a->archive, 0,
+ "Write request too large");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
}
static ssize_t
_archive_write_data(struct archive *_a, const void *buff, size_t size)
{
struct archive_write_disk *a = (struct archive_write_disk *)_a;
- int r;
__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
ARCHIVE_STATE_DATA, "archive_write_data");
- if (a->fd < 0)
- return (ARCHIVE_OK);
- r = _archive_write_data_block(_a, buff, size, a->offset);
- if (r < ARCHIVE_OK)
- return (r);
- return size;
+ return (write_data_block(a, buff, size, a->offset));
}
static int
@@ -584,7 +622,15 @@ _archive_write_finish_entry(struct archi
return (ARCHIVE_OK);
archive_clear_error(&a->archive);
- if (a->last_offset != a->filesize && a->fd >= 0) {
+ /* Pad or truncate file to the right size. */
+ if (a->fd < 0) {
+ /* There's no file. */
+ } else if (a->filesize < 0) {
+ /* File size is unknown, so we can't set the size. */
+ } else if (a->last_offset == a->filesize) {
+ /* Last write ended at exactly the filesize; we're done. */
+ /* Hopefully, this is the common case. */
+ } else {
if (ftruncate(a->fd, a->filesize) == -1 &&
a->filesize == 0) {
archive_set_error(&a->archive, errno,
@@ -601,7 +647,8 @@ _archive_write_finish_entry(struct archi
if (a->st.st_size != a->filesize) {
const char nul = '\0';
if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) {
- archive_set_error(&a->archive, errno, "Seek failed");
+ archive_set_error(&a->archive, errno,
+ "Seek failed");
return (ARCHIVE_FATAL);
}
if (write(a->fd, &nul, 1) < 0) {
@@ -609,6 +656,7 @@ _archive_write_finish_entry(struct archi
"Write to restore size failed");
return (ARCHIVE_FATAL);
}
+ a->pst = NULL;
}
}
@@ -975,7 +1023,7 @@ create_filesystem_object(struct archive_
* If the hardlink does carry data, let the last
* archive entry decide ownership.
*/
- if (r == 0 && a->filesize == 0) {
+ if (r == 0 && a->filesize <= 0) {
a->todo = 0;
a->deferred = 0;
} if (r == 0 && a->filesize > 0) {
@@ -1635,18 +1683,31 @@ set_time(struct archive_write_disk *a)
{
struct timeval times[2];
- times[1].tv_sec = archive_entry_mtime(a->entry);
- times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
+ /* If no time was provided, we're done. */
+ if (!archive_entry_atime_is_set(a->entry)
+ && !archive_entry_mtime_is_set(a->entry))
+ return (ARCHIVE_OK);
- times[0].tv_sec = archive_entry_atime(a->entry);
- times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
+ /* We know at least one is set, so... */
+ if (archive_entry_mtime_is_set(a->entry)) {
+ times[1].tv_sec = archive_entry_mtime(a->entry);
+ times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
+ } else {
+ times[1].tv_sec = a->start_time;
+ times[1].tv_usec = 0;
+ }
/* If no atime was specified, use start time instead. */
/* In theory, it would be marginally more correct to use
* time(NULL) here, but that would cost us an extra syscall
* for little gain. */
- if (times[0].tv_sec == 0 && times[0].tv_usec == 0)
+ if (archive_entry_atime_is_set(a->entry)) {
+ times[0].tv_sec = archive_entry_atime(a->entry);
+ times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
+ } else {
times[0].tv_sec = a->start_time;
+ times[0].tv_usec = 0;
+ }
#ifdef HAVE_FUTIMES
if (a->fd >= 0 && futimes(a->fd, times) == 0) {
@@ -1684,10 +1745,24 @@ set_time(struct archive_write_disk *a)
{
struct utimbuf times;
- times.modtime = archive_entry_mtime(a->entry);
- times.actime = archive_entry_atime(a->entry);
- if (times.actime == 0)
+ /* If no time was provided, we're done. */
+ if (!archive_entry_atime_is_set(a->entry)
+ && !archive_entry_mtime_is_set(a->entry))
+ return (ARCHIVE_OK);
+
+ /* We know at least one is set, so... */
+ /* Set mtime from mtime if set, else start time. */
+ if (archive_entry_mtime_is_set(a->entry))
+ times.modtime = archive_entry_mtime(a->entry);
+ else
+ times.modtime = a->start_time;
+
+ /* Set atime from provided atime, else mtime. */
+ if (archive_entry_atime_is_set(a->entry))
+ times.actime = archive_entry_atime(a->entry);
+ else
times.actime = a->start_time;
+
if (!S_ISLNK(a->mode) && utime(a->name, ×) != 0) {
archive_set_error(&a->archive, errno,
"Can't update time for %s", a->name);
Modified: stable/7/lib/libarchive/test/test_entry.c
==============================================================================
--- stable/7/lib/libarchive/test/test_entry.c Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/test/test_entry.c Fri Nov 28 20:08:47 2008 (r185408)
@@ -69,14 +69,25 @@ DEFINE_TEST(test_entry)
* The following tests are ordered alphabetically by the
* name of the field.
*/
+
/* atime */
archive_entry_set_atime(e, 13579, 24680);
assertEqualInt(archive_entry_atime(e), 13579);
assertEqualInt(archive_entry_atime_nsec(e), 24680);
+ archive_entry_unset_atime(e);
+ assertEqualInt(archive_entry_atime(e), 0);
+ assertEqualInt(archive_entry_atime_nsec(e), 0);
+ assert(!archive_entry_atime_is_set(e));
+
/* ctime */
archive_entry_set_ctime(e, 13580, 24681);
assertEqualInt(archive_entry_ctime(e), 13580);
assertEqualInt(archive_entry_ctime_nsec(e), 24681);
+ archive_entry_unset_ctime(e);
+ assertEqualInt(archive_entry_ctime(e), 0);
+ assertEqualInt(archive_entry_ctime_nsec(e), 0);
+ assert(!archive_entry_ctime_is_set(e));
+
#if ARCHIVE_VERSION_STAMP >= 1009000
/* dev */
archive_entry_set_dev(e, 235);
@@ -85,6 +96,7 @@ DEFINE_TEST(test_entry)
skipping("archive_entry_dev()");
#endif
/* devmajor/devminor are tested specially below. */
+
#if ARCHIVE_VERSION_STAMP >= 1009000
/* filetype */
archive_entry_set_filetype(e, AE_IFREG);
@@ -92,10 +104,13 @@ DEFINE_TEST(test_entry)
#else
skipping("archive_entry_filetype()");
#endif
+
/* fflags are tested specially below */
+
/* gid */
archive_entry_set_gid(e, 204);
assertEqualInt(archive_entry_gid(e), 204);
+
/* gname */
archive_entry_set_gname(e, "group");
assertEqualString(archive_entry_gname(e), "group");
@@ -104,6 +119,7 @@ DEFINE_TEST(test_entry)
assertEqualWString(archive_entry_gname_w(e), L"wgroup");
memset(wbuff, 0, sizeof(wbuff));
assertEqualWString(archive_entry_gname_w(e), L"wgroup");
+
/* hardlink */
archive_entry_set_hardlink(e, "hardlinkname");
assertEqualString(archive_entry_hardlink(e), "hardlinkname");
@@ -158,10 +174,16 @@ DEFINE_TEST(test_entry)
/* mode */
archive_entry_set_mode(e, 0123456);
assertEqualInt(archive_entry_mode(e), 0123456);
+
/* mtime */
archive_entry_set_mtime(e, 13581, 24682);
assertEqualInt(archive_entry_mtime(e), 13581);
assertEqualInt(archive_entry_mtime_nsec(e), 24682);
+ archive_entry_unset_mtime(e);
+ assertEqualInt(archive_entry_mtime(e), 0);
+ assertEqualInt(archive_entry_mtime_nsec(e), 0);
+ assert(!archive_entry_mtime_is_set(e));
+
#if ARCHIVE_VERSION_STAMP >= 1009000
/* nlink */
archive_entry_set_nlink(e, 736);
@@ -169,6 +191,7 @@ DEFINE_TEST(test_entry)
#else
skipping("archive_entry_nlink()");
#endif
+
/* pathname */
archive_entry_set_pathname(e, "path");
assertEqualString(archive_entry_pathname(e), "path");
@@ -184,6 +207,7 @@ DEFINE_TEST(test_entry)
assertEqualWString(archive_entry_pathname_w(e), L"wpath");
memset(wbuff, 0, sizeof(wbuff));
assertEqualWString(archive_entry_pathname_w(e), L"wpath");
+
#if ARCHIVE_VERSION_STAMP >= 1009000
/* rdev */
archive_entry_set_rdev(e, 532);
@@ -192,9 +216,14 @@ DEFINE_TEST(test_entry)
skipping("archive_entry_rdev()");
#endif
/* rdevmajor/rdevminor are tested specially below. */
+
/* size */
archive_entry_set_size(e, 987654321);
assertEqualInt(archive_entry_size(e), 987654321);
+ archive_entry_unset_size(e);
+ assertEqualInt(archive_entry_size(e), 0);
+ assert(!archive_entry_size_is_set(e));
+
/* symlink */
archive_entry_set_symlink(e, "symlinkname");
assertEqualString(archive_entry_symlink(e), "symlinkname");
@@ -207,9 +236,11 @@ DEFINE_TEST(test_entry)
#endif
archive_entry_copy_symlink_w(e, L"wsymlink");
assertEqualWString(archive_entry_symlink_w(e), L"wsymlink");
+
/* uid */
archive_entry_set_uid(e, 83);
assertEqualInt(archive_entry_uid(e), 83);
+
/* uname */
archive_entry_set_uname(e, "user");
assertEqualString(archive_entry_uname(e), "user");
Modified: stable/7/lib/libarchive/test/test_read_format_zip.c
==============================================================================
--- stable/7/lib/libarchive/test/test_read_format_zip.c Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/test/test_read_format_zip.c Fri Nov 28 20:08:47 2008 (r185408)
@@ -25,6 +25,12 @@
#include "test.h"
__FBSDID("$FreeBSD$");
+/*
+ * The reference file for this has been manually tweaked so that:
+ * * file2 has length-at-end but file1 does not
+ * * file2 has an invalid CRC
+ */
+
DEFINE_TEST(test_read_format_zip)
{
const char *refname = "test_read_format_zip.zip";
@@ -57,7 +63,8 @@ DEFINE_TEST(test_read_format_zip)
assertA(0 == archive_read_next_header(a, &ae));
assertEqualString("file2", archive_entry_pathname(ae));
assertEqualInt(1179605932, archive_entry_mtime(ae));
- assertEqualInt(18, archive_entry_size(ae));
+ failure("file2 has length-at-end, so we shouldn't see a valid size");
+ assertEqualInt(0, archive_entry_size_is_set(ae));
failure("file2 has a bad CRC, so reading to end should fail");
assertEqualInt(ARCHIVE_WARN, archive_read_data(a, buff, 19));
assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
Modified: stable/7/lib/libarchive/test/test_read_format_zip.zip.uu
==============================================================================
--- stable/7/lib/libarchive/test/test_read_format_zip.zip.uu Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/test/test_read_format_zip.zip.uu Fri Nov 28 20:08:47 2008 (r185408)
@@ -1,13 +1,14 @@
$FreeBSD$
begin 644 test_read_format_zip.zip
-M4$L#!`H``````%EFLS8````````````````$`!4`9&ER+U54"0`#&55/1AE5
-M3T95>`0`Z`/H`U!+`P04````"`!O9K,V.C=F/0H````2````!0`5`&9I;&4Q
-M550)``-!54]&K%M/1E5X!`#H`^@#RTC-R<GGRD`B`5!+`P04````"`!::K,V
-M>%8T$@H````2````!0`5`&9I;&4R550)``.L6T]&K%M/1E5X!`#H`^@#RTC-
-MR<GGRD`B`5!+`0(7`PH``````%EFLS8````````````````$``T`````````
-M$`#M00````!D:7(O550%``,954]&57@``%!+`0(7`Q0````(`&]FLS8Z-V8]
-M"@```!(````%``T```````$```"D at 3<```!F:6QE,554!0`#055/1E5X``!0
-M2P$"%P,4````"`!::K,V>%8T$@H````2````!0`-```````!````I(%Y````
-H9FEL93)55`4``ZQ;3T95>```4$L%!@`````#``,`OP```+L`````````
+M4$L#!`H`"````%EFLS8````````````````$`!4`9&ER+U54"0`#&55/1M19
+M_4A5>`0`Z`/H`U!+!P@```````````````!02P,$%`````@`;V:S-CHW9CT*
+M````$@````4`%0!F:6QE,554"0`#055/1L!9_4A5>`0`Z`/H`\M(S<G)Y\I`
+M(@%02P,$%``(``@`6FJS-@``````````$@````4`%0!F:6QE,E54"0`#K%M/
+M1L!9_4A5>`0`Z`/H`\M(S<G)Y\I`(@%02P<(.C=F$@H````2````4$L!`A<#
+M"@`(````66:S-@````````````````0`#0`````````0`.U!`````&1I<B]5
+M5`4``QE53T95>```4$L!`A<#%``(``@`;V:S-CHW9CT*````$@````4`#0``
+M`````0```.V!1P```&9I;&4Q550%``-!54]&57@``%!+`0(7`Q0`"``(`%IJ
+MLS8Z-V8]"@```!(````%``T```````$```#M at 8D```!F:6QE,E54!0`#K%M/
+;1E5X``!02P4&``````,``P"_````VP``````
`
end
Modified: stable/7/lib/libarchive/test/test_write_disk.c
==============================================================================
--- stable/7/lib/libarchive/test/test_write_disk.c Fri Nov 28 19:58:31 2008 (r185407)
+++ stable/7/lib/libarchive/test/test_write_disk.c Fri Nov 28 20:08:47 2008 (r185408)
@@ -84,7 +84,7 @@ static void create_reg_file(struct archi
* the entry being a maximum size.
*/
archive_entry_set_size(ae, sizeof(data));
- archive_entry_set_mtime(ae, 123456789, 0);
+ archive_entry_set_mtime(ae, 123456789, 0);
assertEqualIntA(ad, 0, archive_write_header(ad, ae));
assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
@@ -152,6 +152,61 @@ static void create_reg_file2(struct arch
free(compare);
free(data);
}
+
+static void create_reg_file3(struct archive_entry *ae, const char *msg)
+{
+ static const char data[]="abcdefghijklmnopqrstuvwxyz";
+ struct archive *ad;
+ struct stat st;
+
+ /* Write the entry to disk. */
+ assert((ad = archive_write_disk_new()) != NULL);
+ failure("%s", msg);
+ /* Set the size smaller than the data and verify the truncation. */
+ archive_entry_set_size(ae, 5);
+ assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+ assertEqualInt(5, archive_write_data(ad, data, sizeof(data)));
+ assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+ archive_write_finish(ad);
+#else
+ assertEqualInt(0, archive_write_finish(ad));
+#endif
+ /* Test the entry on disk. */
+ assert(0 == stat(archive_entry_pathname(ae), &st));
+ failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+ st.st_mode, archive_entry_mode(ae));
+ assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
+ assertEqualInt(st.st_size, 5);
+}
+
+
+static void create_reg_file4(struct archive_entry *ae, const char *msg)
+{
+ static const char data[]="abcdefghijklmnopqrstuvwxyz";
+ struct archive *ad;
+ struct stat st;
+
+ /* Write the entry to disk. */
+ assert((ad = archive_write_disk_new()) != NULL);
+ /* Leave the size unset. The data should not be truncated. */
+ assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+ assertEqualInt(ARCHIVE_OK,
+ archive_write_data_block(ad, data, sizeof(data), 0));
+ assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_VERSION_NUMBER < 2000000
+ archive_write_finish(ad);
+#else
+ assertEqualInt(0, archive_write_finish(ad));
+#endif
+ /* Test the entry on disk. */
+ assert(0 == stat(archive_entry_pathname(ae), &st));
+ failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+ st.st_mode, archive_entry_mode(ae));
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-all
mailing list