Author: kientzle
Date: Fri Nov 28 20:09:48 2008
New Revision: 185409
URL: http://svn.freebsd.org/changeset/base/185409

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:
  releng/7.1/lib/libarchive/   (props changed)
  releng/7.1/lib/libarchive/archive_entry.c
  releng/7.1/lib/libarchive/archive_entry.h
  releng/7.1/lib/libarchive/archive_entry_private.h
  releng/7.1/lib/libarchive/archive_read_support_format_zip.c
  releng/7.1/lib/libarchive/archive_write_disk.c
  releng/7.1/lib/libarchive/test/test_entry.c
  releng/7.1/lib/libarchive/test/test_read_format_zip.c
  releng/7.1/lib/libarchive/test/test_read_format_zip.zip.uu
  releng/7.1/lib/libarchive/test/test_write_disk.c
  releng/7.1/lib/libarchive/test/test_write_disk_hardlink.c

Modified: releng/7.1/lib/libarchive/archive_entry.c
==============================================================================
--- releng/7.1/lib/libarchive/archive_entry.c   Fri Nov 28 20:08:47 2008        
(r185408)
+++ releng/7.1/lib/libarchive/archive_entry.c   Fri Nov 28 20:09:48 2008        
(r185409)
@@ -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: releng/7.1/lib/libarchive/archive_entry.h
==============================================================================
--- releng/7.1/lib/libarchive/archive_entry.h   Fri Nov 28 20:08:47 2008        
(r185408)
+++ releng/7.1/lib/libarchive/archive_entry.h   Fri Nov 28 20:09:48 2008        
(r185409)
@@ -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: releng/7.1/lib/libarchive/archive_entry_private.h
==============================================================================
--- releng/7.1/lib/libarchive/archive_entry_private.h   Fri Nov 28 20:08:47 
2008        (r185408)
+++ releng/7.1/lib/libarchive/archive_entry_private.h   Fri Nov 28 20:09:48 
2008        (r185409)
@@ -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: releng/7.1/lib/libarchive/archive_read_support_format_zip.c
==============================================================================
--- releng/7.1/lib/libarchive/archive_read_support_format_zip.c Fri Nov 28 
20:08:47 2008        (r185408)
+++ releng/7.1/lib/libarchive/archive_read_support_format_zip.c Fri Nov 28 
20:09:48 2008        (r185409)
@@ -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: releng/7.1/lib/libarchive/archive_write_disk.c
==============================================================================
--- releng/7.1/lib/libarchive/archive_write_disk.c      Fri Nov 28 20:08:47 
2008        (r185408)
+++ releng/7.1/lib/libarchive/archive_write_disk.c      Fri Nov 28 20:09:48 
2008        (r185409)
@@ -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: releng/7.1/lib/libarchive/test/test_entry.c
==============================================================================
--- releng/7.1/lib/libarchive/test/test_entry.c Fri Nov 28 20:08:47 2008        
(r185408)
+++ releng/7.1/lib/libarchive/test/test_entry.c Fri Nov 28 20:09:48 2008        
(r185409)
@@ -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: releng/7.1/lib/libarchive/test/test_read_format_zip.c
==============================================================================
--- releng/7.1/lib/libarchive/test/test_read_format_zip.c       Fri Nov 28 
20:08:47 2008        (r185408)
+++ releng/7.1/lib/libarchive/test/test_read_format_zip.c       Fri Nov 28 
20:09:48 2008        (r185409)
@@ -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: releng/7.1/lib/libarchive/test/test_read_format_zip.zip.uu
==============================================================================
--- releng/7.1/lib/libarchive/test/test_read_format_zip.zip.uu  Fri Nov 28 
20:08:47 2008        (r185408)
+++ releng/7.1/lib/libarchive/test/test_read_format_zip.zip.uu  Fri Nov 28 
20:09:48 2008        (r185409)
@@ -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/[EMAIL PROTECTED]<GGRD`B`5!+`P04````"`!::K,V
-M>[EMAIL PROTECTED]&9I;&4R550)``.L6T]&K%M/[EMAIL PROTECTED]
-MR<GGRD`B`5!+`0(7`PH``````%EFLS8````````````````$``T`````````
-M$`#M00````!D:7(O550%``,954]&[EMAIL PROTECTED](7`Q0````(`&]FLS8Z-V8]
-M"@```!(````%``T```````$```"[EMAIL PROTECTED]<```!F:6QE,554!0`#055/1E5X``!0
-M2P$"%P,4````"`!::K,V>[EMAIL PROTECTED](%Y````
-H9FEL93)55`4``ZQ;3T95>[EMAIL PROTECTED],`OP```+L`````````
+M4$L#!`H`"````%EFLS8````````````````$`!4`9&ER+U54"0`#&55/1M19
+M_4A5>`0`Z`/[EMAIL PROTECTED],[EMAIL PROTECTED];V:S-CHW9CT*
[EMAIL PROTECTED]:6QE,554"0`#055/1L!9_4A5>`0`Z`/H`\M(S<G)Y\I`
+M(@%02P,$%``([EMAIL PROTECTED]@[EMAIL PROTECTED]:6QE,E54"0`#K%M/
+M1L!9_4A5>`0`Z`/H`\M(S<G)Y\I`(@%02P<([EMAIL PROTECTED]<#
+M"@`(````66:[EMAIL PROTECTED]&1I<B]5
+M5`4``QE53T95>```4$L!`A<#%``([EMAIL PROTECTED];V:[EMAIL PROTECTED]
+M`````0```.V!1P```&9I;&4Q550%``-!54]&[EMAIL PROTECTED](7`Q0`"``(`%IJ
+MLS8Z-V8]"@```!([EMAIL PROTECTED]:6QE,E54!0`#K%M/
+;1E5X``!02P4&``````,``P"_````VP``````
 `
 end

Modified: releng/7.1/lib/libarchive/test/test_write_disk.c
==============================================================================
--- releng/7.1/lib/libarchive/test/test_write_disk.c    Fri Nov 28 20:08:47 
2008        (r185408)
+++ releng/7.1/lib/libarchive/test/test_write_disk.c    Fri Nov 28 20:09:48 
2008        (r185409)
@@ -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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to