Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 28 Nov 2008 20:08:47 +0000 (UTC)
From:      Tim Kientzle <kientzle@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r185408 - in stable/7/lib/libarchive: . test
Message-ID:  <200811282008.mASK8l6H028595@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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, &times) != 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@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@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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200811282008.mASK8l6H028595>