From owner-svn-src-stable@FreeBSD.ORG Fri Nov 28 20:08:48 2008 Return-Path: Delivered-To: svn-src-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 76D461065674; Fri, 28 Nov 2008 20:08:48 +0000 (UTC) (envelope-from kientzle@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 5F7AF8FC16; Fri, 28 Nov 2008 20:08:48 +0000 (UTC) (envelope-from kientzle@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id mASK8m8E028605; Fri, 28 Nov 2008 20:08:48 GMT (envelope-from kientzle@svn.freebsd.org) Received: (from kientzle@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id mASK8l6H028595; Fri, 28 Nov 2008 20:08:47 GMT (envelope-from kientzle@svn.freebsd.org) Message-Id: <200811282008.mASK8l6H028595@svn.freebsd.org> From: Tim Kientzle Date: Fri, 28 Nov 2008 20:08:47 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org X-SVN-Group: stable-7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r185408 - in stable/7/lib/libarchive: . test X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 28 Nov 2008 20:08:48 -0000 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%8T$@H````2````!0`5`&9I;&4R550)``.L6T]&K%M/1E5X!`#H`^@#RTC- -MR%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`0`Z`/H`\M(S```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 ***