Index: stable/11/contrib/libarchive/COPYING =================================================================== --- stable/11/contrib/libarchive/COPYING (revision 368706) +++ stable/11/contrib/libarchive/COPYING (revision 368707) @@ -1,66 +1,65 @@ The libarchive distribution as a whole is Copyright by Tim Kientzle and is subject to the copyright notice reproduced at the bottom of this file. Each individual file in this distribution should have a clear copyright/licensing statement at the beginning of the file. If any do not, please let me know and I will rectify it. The following is intended to summarize the copyright status of the individual files; the actual statements in the files are controlling. * Except as listed below, all C sources (including .c and .h files) and documentation files are subject to the copyright notice reproduced at the bottom of this file. * The following source files are also subject in whole or in part to a 3-clause UC Regents copyright; please read the individual source files for details: - libarchive/archive_entry.c libarchive/archive_read_support_filter_compress.c libarchive/archive_write_add_filter_compress.c libarchive/mtree.5 * The following source files are in the public domain: libarchive/archive_getdate.c * The following source files are triple-licensed with the ability to choose from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses: libarchive/archive_blake2.h libarchive/archive_blake2_impl.h libarchive/archive_blake2s_ref.c libarchive/archive_blake2sp_ref.c * The build files---including Makefiles, configure scripts, and auxiliary scripts used as part of the compile process---have widely varying licensing terms. Please check individual files before distributing them to see if those restrictions apply to you. I intend for all new source code to use the license below and hope over time to replace code with other licenses with new implementations that do use the license below. The varying licensing of the build scripts seems to be an unavoidable mess. Copyright (c) 2003-2018 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer in this position and unchanged. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Index: stable/11/contrib/libarchive/NEWS =================================================================== --- stable/11/contrib/libarchive/NEWS (revision 368706) +++ stable/11/contrib/libarchive/NEWS (revision 368707) @@ -1,733 +1,737 @@ +Dec 01, 2020: libarchive 3.5.0 released + +Oct 14, 2020: Support for system extended attributes + May 20, 2020: libarchive 3.4.3 released Apr 30, 2020: Support for pzstd compressed files Apr 16, 2020: Support for RHT.security.selinux tar extended attribute Feb 11, 2020: libarchive 3.4.2 released Jan 23, 2020: Important fixes for writing XAR archives Jan 20, 2020: New tar option: --safe-writes (atomical file extraction) Jan 03, 2020: Support mbed TLS (PolarSSL) as optional crypto provider Dec 30, 2019: libarchive 3.4.1 released Dec 11, 2019: New pax write option "xattrhdr" Nov 17, 2019: Unicode filename support for reading lha/lzh archives Jun 11, 2019: libarchive 3.4.0 released May 18, 2019: Fixes for reading Android APK and JAR archives Apr 16, 2019: Support for non-recursive list and extract Apr 14, 2019: New tar option: --exclude-vcs Mar 27, 2019: Support for file and directory symlinks on Windows Mar 12, 2019: Important fixes for storing file attributes and flags Jan 20, 2019: Support for xz, lzma, ppmd8 and bzip2 decompression in ZIP files Oct 06, 2018: RAR 5.0 reader Sep 03, 2018: libarchive 3.3.3 released Jul 19, 2018: Avoid super-linear slowdown on malformed mtree files Jan 27, 2018: Many fixes for building with Visual Studio Oct 19, 2017: NO_OVERWRITE doesn't change existing directory attributes Aug 12, 2017: New support for Zstandard read and write filters Jul 09, 2017: libarchive 3.3.2 released Mar 16, 2017: NFSv4 ACL support for Linux (librichacl) Feb 26, 2017: libarchive 3.3.1 released Security & Feature release Feb 19, 2017: libarchive 3.3.0 released Security & Feature release Jan 29, 2017: Limited NFSv4 ACL support for Mac OS (Darwin) Jan 10, 2017: POSIX.1e and NFSv4 ACL support for Solaris and derivates Dec 27, 2016: NFSv4 ACL read and write support for pax Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w() Nov, 2016: libarchive is now being tested by the OSS-Fuzz project Oct 26, 2016: Remove liblzmadec support Oct 23, 2016: libarchive 3.2.2 released Security release Jun 20, 2016: libarchive 3.2.1 released This fixes a handful of security and other critical issues with 3.2.0 May 01, 2016: libarchive 3.2.0 released Apr 09, 2016: libarchive 3.1.901a released Another test release in preparation for 3.2.0 Feb 13, 2016: libarchive 3.1.900a released This is a test release in preparation for 3.2.0 Oct 21, 2015: Preliminary port to OSF Apr 11, 2015: libarchive's issue tracker is now hosted at GitHub. https://github.com/libarchive/libarchive/issues Early 2015: Many fixes to crash and overflow bugs thanks to Hanno Boeck Oct 13, 2014: Zip encryption and decryption support Aug 13, 2014: Add support for lz4 compression. Jun 10, 2014: Add warc format support May 3, 2014: Add experimental Zip streaming extension Apr 6, 2014: Add bsdcat command-line tool Jan 12, 2014: Add Zip64 support Dec 1, 2013: Rewrite Zip write logic Jul 1, 2013: Add ability to detect encrypted entries for many formats (This does not add the ability to *decrypt* those entries, however) Feb 23, 2013: "raw" write support added Feb 09, 2013: libarchive 3.1.2 released Jan 28, 2013: libarchive's new website moved to http://www.libarchive.org. Jan 13, 2013: libarchive 3.1.1 released Jan 13, 2013: libarchive 3.1.0 released Dec 07, 2012: Implement functions to manually set the format and filters used. Nov 11, 2012: Add support for __MACOSX directory in Zip archives, which resource forks are stored in. Oct 20, 2012: Add support for writing v7 tar format. Oct 09, 2012: Add support for grzip compression. Oct 07, 2012: Introduce b64encode filter. Oct 07, 2012: Introduce uuencode filter. Oct 06, 2012: Add support for lzop. Sep 27, 2012: Implement function used to seek within data blocks. (Currently only supported for uncompressed RAR archives). Apr 22, 2012: Add basic archive read and write filter support for lrzip. Mar 27, 2012: libarchive 3.0.4 released Feb 05, 2012: libarchive development now hosted at GitHub. http://libarchive.github.com/ Feb 05, 2012: libarchive's issue tracker remains at Google Code. http://code.google.com/p/libarchive/issues/list Feb 05, 2012: libarchive's mailing lists remain at Google Groups. Dec 24, 2011: libarchive 3.0.2 released Dec 23, 2011: Various fixes merged from FreeBSD Dec 23, 2011: Symlink support in Zip reader and writer Dec 23, 2011: Robustness fixes to 7Zip reader Nov 27, 2011: libarchive 3.0.1b released Nov 26, 2011: 7Zip reader Nov 26, 2011: Small fixes to ISO and Zip to improve robustness with corrupted input Nov 24, 2011: Improve streaming Zip reader's support for uncompressed entries Nov 20, 2011: New seeking Zip reader supports SFX Zip archives Nov 20, 2011: Build fixes on Windows Nov 13, 2011: libarchive 3.0.0a released Nov 06, 2011: Update shared-library version calculations for libarchive 3.x Sep 04, 2011: Fix tar -s; follow GNU tar for controlling hardlink/symlink substitutions Aug 18, 2011: Fix reading ISO images built by NetBSD's mkisofs Aug 15, 2011: Old archive_read_support_compression_XXX functions are deprecated and will disappear in libarchive 4.0. Jun 26, 2011: RAR reader Jun 16, 2011: Add tar:compat-2x option to emulate broken libarchive 2.x handling of pax UTF-8 headers Apr 25, 2011: Refactor read_open() into a collection of single-item setters; support the old interfaces as wrappers Apr 12, 2011: Split disk writer into separate POSIX and Windows implementations Apr 10, 2011: Improvements to character translations on Windows. Mar 30, 2011: More work to return errors instead of calling abort() Mar 23, 2011: Add charset option to many writers to control MBCS filenames Mar 17, 2011: Overhauled support for per-format extension options Mar 17, 2011: Track character set used for mbcs strings, support translating to/from user-specified locale Mar 09, 2011: Recognize mtree files without requiring a signature Mar 06, 2011: Use iconv to convert to/from Unicode instead of making bad assumptions about the C90 character set translation functions Feb 17, 2011: Fixes for AIX, TRU64, and other platforms Dec 22, 2010: CAB reader Dec 20, 2010: LHA/LZH reader Jul 03, 2010: minitar example demonstrates archive_read_disk directory traversal Jun 29, 2010: Many improvements to ISO reader compatibility Jun 26, 2010: Use larger buffers when copy files into an archive Jun 18, 2010: Reimplement Mac OS extensions in libarchive Jun 09, 2010: archive_read_disk now supports traversals May 28, 2010: XAR writer May 16, 2010: Fix ^T handling; don't exit on interrupted reads and writes May 09, 2010: Improved detection of platform-specific crypto support May 04, 2010: lzip read and write filters May 01, 2010: New options: tar --gid --gname --uid --uname Apr 28, 2010: Use Red-black tree for ISO reader/writer to improve performance Apr 17, 2010: Minimal writer for legacy GNU tar format Mar 12, 2010: Don't dereference symlinks on Linux when reading ACLs. Mar 06, 2010: Fix build when an older libarchive is already installed Feb 28, 2010: Relax handling of state failures; misuse by clients now generally results in a sticky ARCHIVE_FATAL rather than a visit to abort() Feb 25, 2010: ISO writer Feb 21, 2010: Split many man pages into smaller chunks. Feb 21, 2010: Performance: Cheat on block sizes when reading archives from disk. Feb 21, 2010: Use int64_t instead of off_t, dev_t, ino_t, uid_t, and gid_t Feb 20, 2010: Document new ACL functions. Feb 19, 2010: Support multiple write filters Feb 07, 2010: Remove some legacy libarchive 1.x APIs Feb 04, 2010: Read afio headers Feb 02, 2010: Archive sparse files compatibly with GNU tar Feb 01, 2010: Integrate Apple extensions for Mac OS extended attributes into bsdtar Jan 31, 2010: Support cpio -V Feb 04, 2010: libarchive 2.8.0 released Jan 17, 2010: Fix error handling for 'echo nonexistent | cpio -o' Jan 17, 2010: Don't use futimes() on Cygwin Jan 02, 2010: libarchive 2.7.902a released (test release for 2.8) Jan 02, 2010: Fix tar/test/test_windows on MinGW Jan 02, 2010: Fix memory leaks in libarchive tests Jan 01, 2010: Fix memory leak when filter startup fails Dec 27, 2009: libarchive 2.7.901a released (test release for 2.8) Aug 04, 2009: libarchive 2.7.1 released Jul 20, 2009: Suppress bogus warning about unxz Jul 19, 2009: Support Cygwin 1.7 Jun 11, 2009: Support lzma/xz files compressed with larger buffer sizes. May 24, 2009: Handle gzip files signed with OpenBSD "gzsig" program. May 07, 2009: Avoid false failures when reading from pipe. Apr 16, 2009: libarchive 2.7.0 released Apr 10, 2009: libarchive 2.6.992a released Apr 09, 2009: Fix SIGPIPE issue building with MSVC. Apr 09, 2009: Fix several minor memory leaks in libarchive and libarchive_test Apr 08, 2009: libarchive 2.6.991a released Apr 07, 2009: Additional tests added to bsdcpio_test Apr 01, 2009: libarchive 2.6.990a released Apr 01, 2009: Use command-line gunzip, bunzip2, unxz, unlzma for decompression if the library is built without suitable libraries. The setup functions return ARCHIVE_WARN in this case so clients can adapt if necessary. Apr 01, 2009: Use getpw*_r and getgr*_r functions for thread-safety. Mar 24, 2009: Add archive_read_next_header2(), which is up to 25% more efficient for some clients; from Brian Harring. Mar 22, 2009: PDF versions of manpages are now included in the distribution. Mar, 2009: Major work to improve Cygwin build by Charles Wilson. Feb/Mar, 2009: Major work on cmake build support, mostly by Michihiro NAKAJIMA. Feb/Mar, 2009: Major work on Visual Studio support by Michihiro NAKAJIMA. All tests now pass. Feb 25, 2009: Fix Debian Bug #516577 Feb 21, 2009: Yacc is no longer needed to build; date parser rewritten in C. Jan/Feb, 2009: Mtree work by Michihiro. Feb, 2009: Joliet support by Andreas Henriksson. Jan/Feb, 2009: New options framework by Michihiro. Feb, 2009: High-res timestamps on Tru64, AIX, and GNU Hurd, by Björn Jacke. Jan 18, 2009: Extended attributes work on FreeBSD and Linux now with pax format. Jan 07, 2009: New archive_read_disk_entry_from_file() knows about ACLs, extended attributes, etc so that bsdtar and bsdcpio don't require such system-specific knowledge. Jan 03, 2009: Read filter system extensively refactored. In particular, read filter pipelines are now built out automatically and individual filters should be much easier to implement. Documentation on the Googlecode Wiki explains how to implement new filters. Dec 28, 2008: Many Windows/Visual Studio fixes from Michihiro NAKAJIMA. Dec 28, 2008: Main libarchive development moved from FreeBSD Perforce server to Google Code. This should make it easier for more people to participate in libarchive development. Dec 28, 2008: libarchive 2.6.0 released Dec 25, 2008: libarchive 2.5.905a released Dec 10, 2008: libarchive 2.5.904a released Dec 04, 2008: libarchive 2.5.903a released Nov 09, 2008: libarchive 2.5.902a released Nov 08, 2008: libarchive 2.5.901a released Nov 08, 2008: Start of pre-release testing for libarchive 2.6 Nov 07, 2008: Read filter refactor: The decompression routines just consume and produce arbitrarily-sized blocks. The reblocking from read_support_compression_none() has been pulled into the read core. Also, the decompression bid now makes multiple passes and stacks read filters. Oct 21, 2008: bsdcpio: New command-line parser. Oct 19, 2008: Internal read_ahead change: short reads are now an error Oct 06, 2008: bsdtar: option parser no longer uses getopt_long(), gives consistent option parsing on all platforms. Sep 19, 2008: Jaakko Heinonen: shar utility built on libarchive Sep 17, 2008: Pedro Giffuni: birthtime support Sep 17, 2008: Miklos Vajna: lzma reader and test. Note: I still have some concerns about the auto-detection (LZMA file format doesn't support auto-detection well), so this is not yet enabled under archive_read_support_compression_all(). For now, you must call archive_read_support_compression_lzma() if you want LZMA read support. Sep 11, 2008: Ivailo Petrov: Many fixes to Windows build, new solution files Jul 26, 2008: archive_entry now tracks which values have not been set. This helps zip extraction (file size is often "unknown") and time restores (tar usually doesn't know atime). Jul 26, 2008: Joerg Sonnenberger: Performance improvements to shar writer Jul 25, 2008: Joerg Sonnenberger: mtree write support Jul 02, 2008: libarchive 2.5.5 released Jul 02, 2008: libarchive 2.5.5b released Jul 01, 2008: bsdcpio is being used by enough people, we can call it 1.0.0 now Jun 20, 2008: bsdcpio: If a -l link fails with EXDEV, copy the file instead Jun 19, 2008: bsdcpio: additional long options for better GNU cpio compat Jun 15, 2008: Many small portability and bugfixes since 2.5.4b. May 25, 2008: libarchive 2.5.4b released May 21, 2008: Joerg Sonnenberger: fix bsdtar hardlink handling for newc format May 21, 2008: More progress on Windows building. Thanks to "Scott" for the Windows makefiles, thanks to Kees Zeelenberg for code contributions. May 21, 2008: Fix a number of non-exploitable integer and buffer overflows, thanks to David Remahl at Apple for pointing these out. May 21, 2008: Colin Percival: SIGINFO or SIGUSR1 to bsdtar prints progress info May 16, 2008: bsdtar's test harness no longer depends on file ordering. This was causing spurious test failures on a lot of systems. Thanks to Bernhard R. Link for the diagnosis. May 14, 2008: Joerg Sonnenberger: -s substitution support for bsdtar May 13, 2008: Joerg Sonnenberger: Many mtree improvements May 11, 2008: Joerg Sonnenberger: fix hardlink extraction when hardlinks have different permissions from original file April 30, 2008: Primary libarchive work has been moved into the FreeBSD project's Perforce repository: http://perforce.freebsd.org/ The libarchive project can be browsed at //depot/user/kientzle/libarchive-portable Direct link: http://preview.tinyurl.com/46mdgr May 04, 2008: libarchive 2.5.3b released * libarchive: Several fixes to link resolver to address bsdcpio crashes * bsdcpio: -p hardlink handling fixes * tar/pax: Ensure ustar dirnames end in '/'; be more careful about measuring filenames when deciding what pathname fields to use * libarchive: Mark which entry strings are set; be accurate about distinguishing empty strings ("") from unset ones (NULL) * tar: Don't crash reading entries with empty filenames * libarchive_test, bsdtar_test, bsdcpio_test: Better defaults: run all tests, delete temp dirs, summarize repeated failures * -no-undefined to libtool for Cygwin * libarchive_test: Skip large file tests on systems with 32-bit off_t * iso9660: Don't bother trying to find the body of an empty file; this works around strange behavior from some ISO9660 writers * tar: allow -r -T to be used together * tar: allow --format with -r or -u * libarchive: Don't build archive.h May 04, 2008: Simplified building: archive.h is no longer constructed This may require additional #if conditionals on some platforms. Mar 30, 2008: libarchive 2.5.1b released Mar 15, 2008: libarchive 2.5.0b released Mar 15, 2008: bsdcpio now seems to correctly write hardlinks into newc, ustar, and old cpio archives. Just a little more testing before bsdcpio 1.0 becomes a reality. Mar 15, 2008: I think the new linkify() interface is finally handling all known hardlink strategies. Mar 15, 2008: Mtree read fixes from Joerg Sonnenberger. Mar 15, 2008: Many new bsdtar and bsdcpio options from Joerg Sonnenberger. Mar 15, 2008: test harnesses no longer require uudecode; they now have built-in decoding logic that decodes the reference files as they are needed. Mar 14, 2008: libarchive 2.4.14 released; identical to 2.4.13 except for a point fix for gname/uname mixup in pax format that was introduced with the UTF-8 fixes. Feb 26, 2008: libarchive 2.4.13 released Feb 25, 2008: Handle path, linkname, gname, or uname that can't be converted to/from UTF-8. Implement "hdrcharset" attribute from SUS-2008. Feb 25, 2008: Fix name clash on NetBSD. Feb 18, 2008: Fix writing empty 'ar' archives, per Kai Wang Feb 18, 2008: [bsdtar] Permit appending on block devices. Feb 09, 2008: New "linkify" resolver to help with newc hardlink writing; bsdcpio still needs to be converted to use this. Feb 02, 2008: Windows compatibility fixes from Ivailo Petrov, Kees Zeelenberg Jan 30, 2008: Ignore hardlink size for non-POSIX tar archives. Jan 22, 2008: libarchive 2.4.12 released Jan 22, 2008: Fix bad padding when writing symlinks to newc cpio archives. Jan 22, 2008: Verify bsdcpio_test by getting it to work against GNU cpio 2.9. bsdcpio_test complains about missing options (-y and -z), format of informational messages (--version, --help), and a minor formatting issue in odc format output. After this update, bsdcpio_test uncovered several more cosmetic issues in bsdcpio, all now fixed. Jan 22, 2008: Experimental support for self-extracting Zip archives. Jan 22, 2008: Extend hardlink restore strategy to work correctly with hardlinks extracted from newc cpio files. (Which store the body only with the last occurrence of a link.) Dec 30, 2007: libarchive 2.4.11 released Dec 30, 2007: Fixed a compile error in bsdcpio on some systems. Dec 29, 2007: libarchive 2.4.10 released Dec 29, 2007: bsdcpio 0.9.0 is ready for wider use. Dec 29, 2007: Completed initial test harness for bsdcpio. Dec 22, 2007: libarchive 2.4.9 released Dec 22, 2007: Implement the remaining options for bsdcpio: -a, -q, -L, -f, pattern selection for -i and -it. Dec 13, 2007: libarchive 2.4.8 released Dec 13, 2007: gzip and bzip2 compression now handle zero-byte writes correctly, Thanks to Damien Golding for bringing this to my attention. Dec 12, 2007: libarchive 2.4.7 released Dec 10, 2007: libarchive 2.4.6 released Dec 09, 2007: tar/test/test_copy.c verifies "tar -c | tar -x" copy pipeline Dec 07, 2007: Fix a couple of minor memory leaks. Dec 04, 2007: libarchive 2.4.5 released Dec 04, 2007: Fix cpio/test/test_write_odc by setting the umask first. Dec 03, 2007: libarchive 2.4.4 released Dec 03, 2007: New configure options --disable-xattr and --disable-acl, thanks to Samuli Suominen. Dec 03, 2007: libarchive 2.4.3 released Dec 03, 2007: Thanks to Lapo Luchini for sending me a ZIP file that libarchive couldn't handle. Fixed a bug in handling of "length at end" flags in ZIP files. Dec 03, 2007: Fixed bsdcpio -help, bsdtar -help tests. Dec 02, 2007: First cut at real bsdtar test harness. Dec 02, 2007: libarchive 2.4.2 released Dec 02, 2007: libarchive 2.4.1 released Dec 02, 2007: Minor fixes, rough cut of mdoc-to-man conversion for man pages. Oct 30, 2007: libarchive 2.4.0 released Oct 30, 2007: Minor compile fix thanks to Joerg Schilling. Oct 30, 2007: Only run the format auction once at the beginning of the archive. This is simpler and supports better error recovery. Oct 29, 2007: Test support for very large entries in tar archives: libarchive_test now exercises entries from 2GB up to 1TB. Oct 27, 2007: libarchive 2.3.5 released Oct 27, 2007: Correct some unnecessary internal data copying in the "compression none" reader and writer; this reduces user time by up to 2/3 in some tests. (Thanks to Jan Psota for publishing his performance test results to GNU tar's bug-tar mailing list; those results pointed me towards this problem.) Oct 27, 2007: Fix for skipping archive entries that are exactly a multiple of 4G on 32-bit platforms. Oct 25, 2007: Fix for reading very large (>8G) tar archives; this was broken when I put in support for new GNU tar sparse formats. Oct 20, 2007: Initial work on new pattern-matching code for cpio; I hope this eventually replaces the code currently in bsdtar. Oct 08, 2007: libarchive 2.3.4 released Oct 05, 2007: Continuing work on bsdcpio test suite. Oct 05, 2007: New cpio.5 manpage, updates to "History" of bsdcpio.1 and bsdtar.1 manpages. Oct 05, 2007: Fix zip reader to immediately return EOF if you try to read body of non-regular file. In particular, this fixes bsdtar extraction of zip archives. Sep 30, 2007: libarchive 2.3.3 released Sep 26, 2007: Rework Makefile.am so that the enable/disable options actually do the right things. Sep 26, 2007: cpio-odc and cpio-newc archives no longer write bodies for non-regular files. Sep 26, 2007: Test harness for bsdcpio is in place, needs more tests written. This is much nicer than the ragtag collection of test scripts that bsdtar has. Sep 20, 2007: libarchive 2.3.2 released Sep 20, 2007: libarchive 2.3.1 broke bsdtar because the archive_write_data() fix was implemented incorrectly. Sep 16, 2007: libarchive 2.3.1 released Sep 16, 2007: Many fixes to bsdcpio 0.3: handle hardlinks with -p, recognize block size on writing, fix a couple of segfaults. Sep 16, 2007: Fixed return value from archive_write_data() when used with archive_write_disk() to match the documentation and other instances of this same function. Sep 15, 2007: Add archive_entry_link_resolver, archive_entry_strmode Sep 11, 2007: libarchive 2.2.8 released Sep 09, 2007: bsdcpio 0.2 supports most (not yet all) of the old POSIX spec. Sep 01, 2007: libarchive 2.2.7 released Aug 31, 2007: Support for reading mtree files, including an mtree.5 manpage (A little experimental still.) Aug 18, 2007: Read gtar 1.17 --posix --sparse entries. Aug 13, 2007: Refined suid/sgid restore handling; it is no longer an error if suid/sgid bits are dropped when you request perm restore but don't request owner restore. Aug 06, 2007: Use --enable-bsdcpio if you want to try bsdcpio Aug 05, 2007: libarchive 2.2.6 released Aug 05, 2007: New configure option --disable-bsdtar, thanks to Joerg Sonnenberger. Aug 05, 2007: Several bug fixes from FreeBSD CVS repo. Jul 13, 2007: libarchive 2.2.5 released Jul 12, 2007: libarchive 2.2.4 released Jul 12, 2007: Thanks to Colin Percival's help in diagnosing and fixing several critical security bugs. Details available at http://security.freebsd.org/advisories/FreeBSD-SA-07:05.libarchive.asc May 26, 2007: libarchive 2.2.3 released May 26, 2007: Fix memory leaks in ZIP reader and shar writer, add some missing system headers to archive_entry.h, dead code cleanup from Colin Percival, more tests for gzip/bzip2, fix an EOF anomaly in bzip2 decompression. May 12, 2007: libarchive 2.2.2 released May 12, 2007: Fix archive_write_disk permission restore by cloning entry passed into write_header so that permission info is still available at finish_entry time. (archive_read_extract() worked okay because it held onto the passed-in entry, but direct consumers of archive_write_disk would break). This required fixing archive_entry_clone(), which now works and has a reasonably complete test case. May 10, 2007: Skeletal cpio implementation. May 06, 2007: libarchive 2.2.1 released May 06, 2007: Flesh out a lot more of test_entry.c so as to catch problems such as the device node breakage before releasing . May 05, 2007: Fix a bad bug introduced in 2.1.9 that broke device node entries in tar archives. May 03, 2007: Move 'struct stat' out of archive_entry core as well. This removes some portability headaches and fixes a bunch of corner cases that arise when manipulating archives on dissimilar systems. Apr 30, 2007: libarchive 2.1.10 released Apr 31, 2007: Minor code cleanup. Apr 24, 2007: libarchive 2.1.9 released Apr 24, 2007: Fix some recently-introduced problems with libraries (Just let automake handle it and it all works much better.) Finish isolating major()/minor()/makedev() in archive_entry.c. Apr 23, 2007: libarchive 2.1.8 released Apr 23, 2007: Minor fixes found from building on MacOS X Apr 22, 2007: libarchive 2.1.7 released Apr 22, 2007: Eliminated all uses of 'struct stat' from the format readers/writers. This should improve portability; 'struct stat' is now only used in archive_entry and in code that actually touches the disk. Apr 17, 2007: libarchive 2.1.6 released Libarchive now compiles and passes all tests on Interix. Apr 16, 2007: libarchive 2.1.5 released Apr 15, 2007: libarchive 2.1b2 released Apr 15, 2007: New libarchive_internals.3 documentation of internal APIs. Not complete, but should prove helpful. Apr 15, 2007: Experimental "read_compress_program" and "write_compress_program" for using libarchive with external compression. Not yet well tested, and likely has portability issues. Feedback appreciated. Apr 14, 2007: libarchive 2.0.31 released Apr 14, 2007: More fixes for Interix, more 'ar' work Apr 14, 2007: libarchive 2.0.30 released Apr 13, 2007: libarchive now enforces trailing '/' on dirs written to tar archives Apr 11, 2007: libarchive 2.0.29 released Apr 11, 2007: Make it easier to statically configure for different platforms. Apr 11, 2007: Updated config.guess, config.sub, libtool Apr 06, 2007: libarchive 2.0.28 released Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang. Apr 01, 2007: libarchive 2.0.27 released Mar 31, 2007: Several minor fixes from Colin Percival and Joerg Sonnenberger. Mar 12, 2007: libarchive 2.0.25 released Mar 12, 2007: Fix broken --unlink flag. Mar 11, 2007: libarchive 2.0.24 released Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry that refers to a non-existent user or group to not be restored correctly. The fix both makes the parser more tolerant (so that archives created with the buggy ACLs can be read now) and corrects the ACL formatter. Mar 10, 2007: More work on test portability to Linux. Mar 10, 2007: libarchive 2.0.22 released Mar 10, 2007: Header cleanups; added linux/fs.h, removed some unnecessary headers, added #include guards in bsdtar. If you see any obvious compile failures from this, let me know. Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough to enable as part of "make check", but getting better. Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when a header write fails in a way that only affects this item. Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN. Mar 07, 2007: libarchive 2.0.21 released Mar 07, 2007: Add some ACL tests (only for the system-independent portion of the ACL support for now). Mar 07, 2007: tar's ability to read ACLs off disk got turned off for FreeBSD; re-enable it. (ACL restores and libarchive support for storing/reading ACLs from pax archives was unaffected.) Mar 02, 2007: libarchive 2.0.20 released Mar 2, 2007: It's not perfect, but it's pretty good. Libarchive 2.0 is officially out of beta. Feb 28, 2007: libarchive 2.0b17 released Feb 27, 2007: Make the GID restore checks more robust by checking whether the current user has too few or too many privileges. Feb 26, 2007: libarchive 2.0b15 released Feb 26, 2007: Don't lose symlinks when extracting from ISOs. Thanks to Diego "Flameeyes" Pettenò for telling me about the broken testcase on Gentoo that (finally!) led me to the cause of this long-standing bug. Feb 26, 2007: libarchive 2.0b14 released Feb 26, 2007: Fix a broken test on platforms that lack lchmod(). Feb 25, 2007: libarchive 2.0b13 released Feb 25, 2007: Empty archives were being written as empty files, without a proper end-of-archive marker. Fixed. Feb 23, 2007: libarchive 2.0b12 released Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT and _EXTRACT_SECURE_SYMLINK. These checks used to be in bsdtar, but they belong down in libarchive where they can be used by other tools and where they can be better optimized. Feb 11, 2007: libarchive 2.0b11 released Feb 10, 2007: Fixed a bunch of errors in libarchive's handling of EXTRACT_PERM and EXTRACT_OWNER, especially relating to SUID and SGID bits. Jan 31, 2007: libarchive 2.0b9 released Jan 31, 2007: Added read support for "empty" archives as a distinct archive format. Bsdtar uses this to handle, e.g., "touch foo.tar; tar -rf foo.tar" Jan 22, 2007: libarchive 2.0b6 released Jan 22, 2007: archive_write_disk API is now in place. It provides a finer-grained interface than archive_read_extract. In particular, you can use it to create objects on disk without having an archive around (just feed it archive_entry objects describing what you want to create), you can override the uname/gname-to-uid/gid lookups (minitar uses this to avoid getpwXXX() and getgrXXX() bloat). Jan 09, 2007: libarchive 2.0a3 released Jan 9, 2007: archive_extract is now much better; it handles the most common cases with a minimal number of system calls. Some features still need a lot of testing, especially corner cases involving objects that already exist on disk. I expect the next round of API overhaul will simplify building test cases. Jan 9, 2007: a number of fixes thanks to Colin Percival, especially corrections to the skip() framework and handling of large files. Jan 9, 2007: Fixes for large ISOs. The code should correctly handle very large ISOs with entries up to 4G. Thanks to Robert Sciuk for pointing out these issues. Sep 05, 2006: libarchive 1.3.1 released Sep 5, 2006: Bump version to 1.3 for new I/O wrappers. Sep 4, 2006: New memory and FILE read/write wrappers. Sep 4, 2006: libarchive test harness is now minimally functional; it's located a few minor bugs in error-handling logic Aug 17, 2006: libarchive 1.2.54 released Aug 17, 2006: Outline ABI changes for libarchive 2.0; these are protected behind #ifdef's until I think I've found everything that needs to change. Aug 17, 2006: Fix error-handling in archive_read/write_close() They weren't returning any errors before. Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set Fixes a bug adding files when writing archive to pipe or when using archive_write_open() directly. Jul 2006: New "skip" handling improves performance extracting single files from large uncompressed archives. Mar 21, 2006: 1.2.52 released Mar 21, 2006: Fix -p on platforms that don't have platform-specific extended attribute code. Mar 20, 2006: Add NEWS file; fill in some older history from other files. I'll try to keep this file up-to-date from now on. OLDER NEWS SUMMARIES Mar 19, 2006: libarchive 1.2.51 released Mar 18, 2006: Many fixes to extended attribute support, including a redesign of the storage format to simplify debugging. Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth spending much time on. Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support for extended attributes (Currently Linux-only.). Mar 11, 2006: Reorganized distribution package: There is now one tar.gz file that builds both libarchive and bsdtar. Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write Pax attribute entry names. Nov 7, 2005: Experimental 'tp' format support in libarchive. Feedback appreciated; this is not enabled by archive_read_support_format_all() yet as I'm not quite content with the format detection heuristics. Nov 7, 2005: Some more portability improvements thanks to Darin Broady, minor bugfixes. Oct 12, 2005: Use GNU libtool to build shared libraries on many systems. Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs. Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows: http://gnuwin32.sourceforge.net/ Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson. -L/-h fix from Jaakko Heinonen. Mar 12, 2005: archive_read_extract can now handle very long pathnames (I've tested with pathnames up to 1MB). Mar 12, 2005: Marcus Geiger has written an article about libarchive http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/ including examples of using it from Objective-C. His MoinX http://moinx.antbear.org/ desktop Wiki uses libarchive for archiving and restoring Wiki pages. Jan 22, 2005: Preliminary ZIP extraction support, new directory-walking code for bsdtar. Jan 16, 2005: ISO9660 extraction code added; manpage corrections. May 22, 2004: Many gtar-compatible long options have been added; almost all FreeBSD ports extract correctly with bsdtar. May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar, and pdtar archives. Index: stable/11/contrib/libarchive/libarchive/archive.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive.h (revision 368707) @@ -1,1197 +1,1204 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_H_INCLUDED #define ARCHIVE_H_INCLUDED /* * The version number is expressed as a single integer that makes it * easy to compare versions at build time: for version a.b.c, the * version number is printf("%d%03d%03d",a,b,c). For example, if you * know your application requires version 2.12.108 or later, you can * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3004003 +#define ARCHIVE_VERSION_NUMBER 3005000 #include #include /* for wchar_t */ #include /* For FILE * */ #include /* For time_t */ /* * Note: archive.h is for use outside of libarchive; the configuration * headers (config.h, archive_platform.h, etc.) are purely internal. * Do NOT use HAVE_XXX configuration macros to control the behavior of * this header! If you must conditionalize, use predefined compiler and/or * platform macros. */ #if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 # include #elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H) # include #endif /* Get appropriate definitions of 64-bit integer */ #if !defined(__LA_INT64_T_DEFINED) /* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_INT64_T la_int64_t # endif #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; # else # include /* ssize_t */ # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; # else typedef int64_t la_int64_t; # endif # endif #endif /* The la_ssize_t should match the type used in 'struct stat' */ #if !defined(__LA_SSIZE_T_DEFINED) /* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_SSIZE_T la_ssize_t # endif #define __LA_SSIZE_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) typedef ssize_t la_ssize_t; # elif defined(_WIN64) typedef __int64 la_ssize_t; # else typedef long la_ssize_t; # endif # else # include /* ssize_t */ typedef ssize_t la_ssize_t; # endif #endif /* Large file support for Android */ #ifdef __ANDROID__ #include "android_lf.h" #endif /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ #if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries or non-Windows needs no special declaration. */ # define __LA_DECL #endif #if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__) #define __LA_PRINTF(fmtarg, firstvararg) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) #else #define __LA_PRINTF(fmtarg, firstvararg) /* nothing */ #endif #if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 # define __LA_DEPRECATED __attribute__((deprecated)) #else # define __LA_DEPRECATED #endif #ifdef __cplusplus extern "C" { #endif /* * The version number is provided as both a macro and a function. * The macro identifies the installed header; the function identifies * the library version (which may not be the same if you're using a * dynamically-linked version of the library). Of course, if the * header and library are very different, you should expect some * strangeness. Don't do that. */ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.4.3" +#define ARCHIVE_VERSION_ONLY_STRING "3.5.0" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); /* * Detailed textual name/version of the library and its dependencies. * This has the form: * "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..." * the list of libraries described here will vary depending on how * libarchive was compiled. */ __LA_DECL const char * archive_version_details(void); /* * Returns NULL if libarchive was compiled without the associated library. * Otherwise, returns the version number that libarchive was compiled * against. */ __LA_DECL const char * archive_zlib_version(void); __LA_DECL const char * archive_liblzma_version(void); __LA_DECL const char * archive_bzlib_version(void); __LA_DECL const char * archive_liblz4_version(void); __LA_DECL const char * archive_libzstd_version(void); /* Declare our basic types. */ struct archive; struct archive_entry; /* * Error codes: Use archive_errno() and archive_error_string() * to retrieve details. Unless specified otherwise, all functions * that return 'int' use these codes. */ #define ARCHIVE_EOF 1 /* Found end of archive. */ #define ARCHIVE_OK 0 /* Operation was successful. */ #define ARCHIVE_RETRY (-10) /* Retry might succeed. */ #define ARCHIVE_WARN (-20) /* Partial success. */ /* For example, if write_header "fails", then you can't push data. */ #define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */ /* But if write_header is "fatal," then this archive is dead and useless. */ #define ARCHIVE_FATAL (-30) /* No more operations are possible. */ /* * As far as possible, archive_errno returns standard platform errno codes. * Of course, the details vary by platform, so the actual definitions * here are stored in "archive_platform.h". The symbols are listed here * for reference; as a rule, clients should not need to know the exact * platform-dependent error code. */ /* Unrecognized or invalid file format. */ /* #define ARCHIVE_ERRNO_FILE_FORMAT */ /* Illegal usage of the library. */ /* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */ /* Unknown or unclassified error. */ /* #define ARCHIVE_ERRNO_MISC */ /* * Callbacks are invoked to automatically read/skip/write/open/close the * archive. You can provide your own for complex tasks (like breaking * archives across multiple tapes) or use standard ones built into the * library. */ /* Returns pointer and size of next block of data from archive. */ typedef la_ssize_t archive_read_callback(struct archive *, void *_client_data, const void **_buffer); /* Skips at most request bytes from archive and returns the skipped amount. * This may skip fewer bytes than requested; it may even skip zero bytes. * If you do skip fewer bytes than requested, libarchive will invoke your * read callback and discard data as necessary to make up the full skip. */ typedef la_int64_t archive_skip_callback(struct archive *, void *_client_data, la_int64_t request); /* Seeks to specified location in the file and returns the position. * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h. * Return ARCHIVE_FATAL if the seek fails for any reason. */ typedef la_int64_t archive_seek_callback(struct archive *, void *_client_data, la_int64_t offset, int whence); /* Returns size actually written, zero on EOF, -1 on error. */ typedef la_ssize_t archive_write_callback(struct archive *, void *_client_data, const void *_buffer, size_t _length); typedef int archive_open_callback(struct archive *, void *_client_data); typedef int archive_close_callback(struct archive *, void *_client_data); +typedef int archive_free_callback(struct archive *, void *_client_data); + /* Switches from one client data object to the next/prev client data object. * This is useful for reading from different data blocks such as a set of files * that make up one large file. */ typedef int archive_switch_callback(struct archive *, void *_client_data1, void *_client_data2); /* * Returns a passphrase used for encryption or decryption, NULL on nothing * to do and give it up. */ typedef const char *archive_passphrase_callback(struct archive *, void *_client_data); /* * Codes to identify various stream filters. */ #define ARCHIVE_FILTER_NONE 0 #define ARCHIVE_FILTER_GZIP 1 #define ARCHIVE_FILTER_BZIP2 2 #define ARCHIVE_FILTER_COMPRESS 3 #define ARCHIVE_FILTER_PROGRAM 4 #define ARCHIVE_FILTER_LZMA 5 #define ARCHIVE_FILTER_XZ 6 #define ARCHIVE_FILTER_UU 7 #define ARCHIVE_FILTER_RPM 8 #define ARCHIVE_FILTER_LZIP 9 #define ARCHIVE_FILTER_LRZIP 10 #define ARCHIVE_FILTER_LZOP 11 #define ARCHIVE_FILTER_GRZIP 12 #define ARCHIVE_FILTER_LZ4 13 #define ARCHIVE_FILTER_ZSTD 14 #if ARCHIVE_VERSION_NUMBER < 4000000 #define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE #define ARCHIVE_COMPRESSION_GZIP ARCHIVE_FILTER_GZIP #define ARCHIVE_COMPRESSION_BZIP2 ARCHIVE_FILTER_BZIP2 #define ARCHIVE_COMPRESSION_COMPRESS ARCHIVE_FILTER_COMPRESS #define ARCHIVE_COMPRESSION_PROGRAM ARCHIVE_FILTER_PROGRAM #define ARCHIVE_COMPRESSION_LZMA ARCHIVE_FILTER_LZMA #define ARCHIVE_COMPRESSION_XZ ARCHIVE_FILTER_XZ #define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU #define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM #define ARCHIVE_COMPRESSION_LZIP ARCHIVE_FILTER_LZIP #define ARCHIVE_COMPRESSION_LRZIP ARCHIVE_FILTER_LRZIP #endif /* * Codes returned by archive_format. * * Top 16 bits identifies the format family (e.g., "tar"); lower * 16 bits indicate the variant. This is updated by read_next_header. * Note that the lower 16 bits will often vary from entry to entry. * In some cases, this variation occurs as libarchive learns more about * the archive (for example, later entries might utilize extensions that * weren't necessary earlier in the archive; in this case, libarchive * will change the format code to indicate the extended format that * was used). In other cases, it's because different tools have * modified the archive and so different parts of the archive * actually have slightly different formats. (Both tar and cpio store * format codes in each entry, so it is quite possible for each * entry to be in a different format.) */ #define ARCHIVE_FORMAT_BASE_MASK 0xff0000 #define ARCHIVE_FORMAT_CPIO 0x10000 #define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) #define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2) #define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) #define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) #define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) #define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6) #define ARCHIVE_FORMAT_SHAR 0x20000 #define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) #define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) #define ARCHIVE_FORMAT_TAR 0x30000 #define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1) #define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2) #define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3) #define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4) #define ARCHIVE_FORMAT_ISO9660 0x40000 #define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) #define ARCHIVE_FORMAT_ZIP 0x50000 #define ARCHIVE_FORMAT_EMPTY 0x60000 #define ARCHIVE_FORMAT_AR 0x70000 #define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) #define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) #define ARCHIVE_FORMAT_MTREE 0x80000 #define ARCHIVE_FORMAT_RAW 0x90000 #define ARCHIVE_FORMAT_XAR 0xA0000 #define ARCHIVE_FORMAT_LHA 0xB0000 #define ARCHIVE_FORMAT_CAB 0xC0000 #define ARCHIVE_FORMAT_RAR 0xD0000 #define ARCHIVE_FORMAT_7ZIP 0xE0000 #define ARCHIVE_FORMAT_WARC 0xF0000 #define ARCHIVE_FORMAT_RAR_V5 0x100000 /* * Codes returned by archive_read_format_capabilities(). * * This list can be extended with values between 0 and 0xffff. * The original purpose of this list was to let different archive * format readers expose their general capabilities in terms of * encryption. */ #define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */ #define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0) /* reader can detect encrypted data */ #define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1) /* reader can detect encryptable metadata (pathname, mtime, etc.) */ /* * Codes returned by archive_read_has_encrypted_entries(). * * In case the archive does not support encryption detection at all * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader * for some other reason (e.g. not enough bytes read) cannot say if * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW * is returned. */ #define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2 #define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1 /*- * Basic outline for reading an archive: * 1) Ask archive_read_new for an archive reader object. * 2) Update any global properties as appropriate. * In particular, you'll certainly want to call appropriate * archive_read_support_XXX functions. * 3) Call archive_read_open_XXX to open the archive * 4) Repeatedly call archive_read_next_header to get information about * successive archive entries. Call archive_read_data to extract * data for entries of interest. * 5) Call archive_read_free to end processing. */ __LA_DECL struct archive *archive_read_new(void); /* * The archive_read_support_XXX calls enable auto-detect for this * archive handle. They also link in the necessary support code. * For example, if you don't want bzlib linked in, don't invoke * support_compression_bzip2(). The "all" functions provide the * obvious shorthand. */ #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_read_support_compression_all(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_bzip2(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_compress(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_gzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_lzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_lzma(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_none(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program(struct archive *, const char *command) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program_signature (struct archive *, const char *, const void * /* match */, size_t) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_rpm(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_uu(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_xz(struct archive *) __LA_DEPRECATED; #endif __LA_DECL int archive_read_support_filter_all(struct archive *); +__LA_DECL int archive_read_support_filter_by_code(struct archive *, int); __LA_DECL int archive_read_support_filter_bzip2(struct archive *); __LA_DECL int archive_read_support_filter_compress(struct archive *); __LA_DECL int archive_read_support_filter_gzip(struct archive *); __LA_DECL int archive_read_support_filter_grzip(struct archive *); __LA_DECL int archive_read_support_filter_lrzip(struct archive *); __LA_DECL int archive_read_support_filter_lz4(struct archive *); __LA_DECL int archive_read_support_filter_lzip(struct archive *); __LA_DECL int archive_read_support_filter_lzma(struct archive *); __LA_DECL int archive_read_support_filter_lzop(struct archive *); __LA_DECL int archive_read_support_filter_none(struct archive *); __LA_DECL int archive_read_support_filter_program(struct archive *, const char *command); __LA_DECL int archive_read_support_filter_program_signature (struct archive *, const char * /* cmd */, const void * /* match */, size_t); __LA_DECL int archive_read_support_filter_rpm(struct archive *); __LA_DECL int archive_read_support_filter_uu(struct archive *); __LA_DECL int archive_read_support_filter_xz(struct archive *); __LA_DECL int archive_read_support_filter_zstd(struct archive *); __LA_DECL int archive_read_support_format_7zip(struct archive *); __LA_DECL int archive_read_support_format_all(struct archive *); __LA_DECL int archive_read_support_format_ar(struct archive *); __LA_DECL int archive_read_support_format_by_code(struct archive *, int); __LA_DECL int archive_read_support_format_cab(struct archive *); __LA_DECL int archive_read_support_format_cpio(struct archive *); __LA_DECL int archive_read_support_format_empty(struct archive *); __LA_DECL int archive_read_support_format_gnutar(struct archive *); __LA_DECL int archive_read_support_format_iso9660(struct archive *); __LA_DECL int archive_read_support_format_lha(struct archive *); __LA_DECL int archive_read_support_format_mtree(struct archive *); __LA_DECL int archive_read_support_format_rar(struct archive *); __LA_DECL int archive_read_support_format_rar5(struct archive *); __LA_DECL int archive_read_support_format_raw(struct archive *); __LA_DECL int archive_read_support_format_tar(struct archive *); __LA_DECL int archive_read_support_format_warc(struct archive *); __LA_DECL int archive_read_support_format_xar(struct archive *); /* archive_read_support_format_zip() enables both streamable and seekable * zip readers. */ __LA_DECL int archive_read_support_format_zip(struct archive *); /* Reads Zip archives as stream from beginning to end. Doesn't * correctly handle SFX ZIP files or ZIP archives that have been modified * in-place. */ __LA_DECL int archive_read_support_format_zip_streamable(struct archive *); /* Reads starting from central directory; requires seekable input. */ __LA_DECL int archive_read_support_format_zip_seekable(struct archive *); /* Functions to manually set the format and filters to be used. This is * useful to bypass the bidding process when the format and filters to use * is known in advance. */ __LA_DECL int archive_read_set_format(struct archive *, int); __LA_DECL int archive_read_append_filter(struct archive *, int); __LA_DECL int archive_read_append_filter_program(struct archive *, const char *); __LA_DECL int archive_read_append_filter_program_signature (struct archive *, const char *, const void * /* match */, size_t); /* Set various callbacks. */ __LA_DECL int archive_read_set_open_callback(struct archive *, archive_open_callback *); __LA_DECL int archive_read_set_read_callback(struct archive *, archive_read_callback *); __LA_DECL int archive_read_set_seek_callback(struct archive *, archive_seek_callback *); __LA_DECL int archive_read_set_skip_callback(struct archive *, archive_skip_callback *); __LA_DECL int archive_read_set_close_callback(struct archive *, archive_close_callback *); /* Callback used to switch between one data object to the next */ __LA_DECL int archive_read_set_switch_callback(struct archive *, archive_switch_callback *); /* This sets the first data object. */ __LA_DECL int archive_read_set_callback_data(struct archive *, void *); /* This sets data object at specified index */ __LA_DECL int archive_read_set_callback_data2(struct archive *, void *, unsigned int); /* This adds a data object at the specified index. */ __LA_DECL int archive_read_add_callback_data(struct archive *, void *, unsigned int); /* This appends a data object to the end of list */ __LA_DECL int archive_read_append_callback_data(struct archive *, void *); /* This prepends a data object to the beginning of list */ __LA_DECL int archive_read_prepend_callback_data(struct archive *, void *); /* Opening freezes the callbacks. */ __LA_DECL int archive_read_open1(struct archive *); /* Convenience wrappers around the above. */ __LA_DECL int archive_read_open(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_close_callback *); __LA_DECL int archive_read_open2(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_skip_callback *, archive_close_callback *); /* * A variety of shortcuts that invoke archive_read_open() with * canned callbacks suitable for common situations. The ones that * accept a block size handle tape blocking correctly. */ /* Use this if you know the filename. Note: NULL indicates stdin. */ __LA_DECL int archive_read_open_filename(struct archive *, const char *_filename, size_t _block_size); /* Use this for reading multivolume files by filenames. * NOTE: Must be NULL terminated. Sorting is NOT done. */ __LA_DECL int archive_read_open_filenames(struct archive *, const char **_filenames, size_t _block_size); __LA_DECL int archive_read_open_filename_w(struct archive *, const wchar_t *_filename, size_t _block_size); /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ __LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size) __LA_DEPRECATED; /* Read an archive that's stored in memory. */ __LA_DECL int archive_read_open_memory(struct archive *, const void * buff, size_t size); /* A more involved version that is only used for internal testing. */ __LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff, size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ __LA_DECL int archive_read_open_fd(struct archive *, int _fd, size_t _block_size); /* Read an archive that's already open, using a FILE *. */ /* Note: DO NOT use this with tape drives. */ __LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); /* Parses and returns next entry header. */ __LA_DECL int archive_read_next_header(struct archive *, struct archive_entry **); /* Parses and returns next entry header using the archive_entry passed in */ __LA_DECL int archive_read_next_header2(struct archive *, struct archive_entry *); /* * Retrieve the byte offset in UNCOMPRESSED data where last-read * header started. */ __LA_DECL la_int64_t archive_read_header_position(struct archive *); /* * Returns 1 if the archive contains at least one encrypted entry. * If the archive format not support encryption at all * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. * If for any other reason (e.g. not enough data read so far) * we cannot say whether there are encrypted entries, then * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned. * In general, this function will return values below zero when the * reader is uncertain or totally incapable of encryption support. * When this function returns 0 you can be sure that the reader * supports encryption detection but no encrypted entries have * been found yet. * * NOTE: If the metadata/header of an archive is also encrypted, you * cannot rely on the number of encrypted entries. That is why this * function does not return the number of encrypted entries but# * just shows that there are some. */ __LA_DECL int archive_read_has_encrypted_entries(struct archive *); /* * Returns a bitmask of capabilities that are supported by the archive format reader. * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned. */ __LA_DECL int archive_read_format_capabilities(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ __LA_DECL la_ssize_t archive_read_data(struct archive *, void *, size_t); /* Seek within the body of an entry. Similar to lseek(2). */ __LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int); /* * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify * the desired size of the block. The API does guarantee that offsets will * be strictly increasing and that returned blocks will not overlap. */ __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, la_int64_t *offset); /*- * Some convenience functions that are built on archive_read_data: * 'skip': skips entire entry * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ __LA_DECL int archive_read_data_skip(struct archive *); __LA_DECL int archive_read_data_into_fd(struct archive *, int fd); /* * Set read options. */ /* Apply option to the format only. */ __LA_DECL int archive_read_set_format_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to the filter only. */ __LA_DECL int archive_read_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to both the format and the filter. */ __LA_DECL int archive_read_set_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_read_set_options(struct archive *_a, const char *opts); /* * Add a decryption passphrase. */ __LA_DECL int archive_read_add_passphrase(struct archive *, const char *); __LA_DECL int archive_read_set_passphrase_callback(struct archive *, void *client_data, archive_passphrase_callback *); /*- * Convenience function to recreate the current entry (whose header * has just been read) on disk. * * This does quite a bit more than just copy data to disk. It also: * - Creates intermediate directories as required. * - Manages directory permissions: non-writable directories will * be initially created with write permission enabled; when the * archive is closed, dir permissions are edited to the values specified * in the archive. * - Checks hardlinks: hardlinks will not be extracted unless the * linked-to file was also extracted within the same session. (TODO) */ /* The "flags" argument selects optional behavior, 'OR' the flags you want. */ /* Default: Do not try to set owner/group. */ #define ARCHIVE_EXTRACT_OWNER (0x0001) /* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */ #define ARCHIVE_EXTRACT_PERM (0x0002) /* Default: Do not restore mtime/atime. */ #define ARCHIVE_EXTRACT_TIME (0x0004) /* Default: Replace existing files. */ #define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008) /* Default: Try create first, unlink only if create fails with EEXIST. */ #define ARCHIVE_EXTRACT_UNLINK (0x0010) /* Default: Do not restore ACLs. */ #define ARCHIVE_EXTRACT_ACL (0x0020) /* Default: Do not restore fflags. */ #define ARCHIVE_EXTRACT_FFLAGS (0x0040) /* Default: Do not restore xattrs. */ #define ARCHIVE_EXTRACT_XATTR (0x0080) /* Default: Do not try to guard against extracts redirected by symlinks. */ /* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */ #define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100) /* Default: Do not reject entries with '..' as path elements. */ #define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200) /* Default: Create parent directories as needed. */ #define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400) /* Default: Overwrite files, even if one on disk is newer. */ #define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800) /* Detect blocks of 0 and write holes instead. */ #define ARCHIVE_EXTRACT_SPARSE (0x1000) /* Default: Do not restore Mac extended metadata. */ /* This has no effect except on Mac OS. */ #define ARCHIVE_EXTRACT_MAC_METADATA (0x2000) /* Default: Use HFS+ compression if it was compressed. */ /* This has no effect except on Mac OS v10.6 or later. */ #define ARCHIVE_EXTRACT_NO_HFS_COMPRESSION (0x4000) /* Default: Do not use HFS+ compression if it was not compressed. */ /* This has no effect except on Mac OS v10.6 or later. */ #define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000) /* Default: Do not reject entries with absolute paths */ #define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000) /* Default: Do not clear no-change flags when unlinking object */ #define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000) /* Default: Do not extract atomically (using rename) */ #define ARCHIVE_EXTRACT_SAFE_WRITES (0x40000) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); __LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, struct archive * /* dest */); __LA_DECL void archive_read_extract_set_progress_callback(struct archive *, void (*_progress_func)(void *), void *_user_data); /* Record the dev/ino of a file that will not be written. This is * generally set to the dev/ino of the archive being read. */ __LA_DECL void archive_read_extract_set_skip_file(struct archive *, la_int64_t, la_int64_t); /* Close the file and release most resources. */ __LA_DECL int archive_read_close(struct archive *); /* Release all resources and destroy the object. */ /* Note that archive_read_free will call archive_read_close for you. */ __LA_DECL int archive_read_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_read_free() for backwards compatibility. */ __LA_DECL int archive_read_finish(struct archive *) __LA_DEPRECATED; #endif /*- * To create an archive: * 1) Ask archive_write_new for an archive writer object. * 2) Set any global properties. In particular, you should set * the compression and format to use. * 3) Call archive_write_open to open the file (most people * will use archive_write_open_file or archive_write_open_fd, * which provide convenient canned I/O callbacks for you). * 4) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to write the header * - archive_write_data to write the entry data * 5) archive_write_close to close the output * 6) archive_write_free to cleanup the writer and release resources */ __LA_DECL struct archive *archive_write_new(void); __LA_DECL int archive_write_set_bytes_per_block(struct archive *, int bytes_per_block); __LA_DECL int archive_write_get_bytes_per_block(struct archive *); /* XXX This is badly misnamed; suggestions appreciated. XXX */ __LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, int bytes_in_last_block); __LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); /* The dev/ino of a file that won't be archived. This is used * to avoid recursively adding an archive to itself. */ __LA_DECL int archive_write_set_skip_file(struct archive *, la_int64_t, la_int64_t); #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_write_set_compression_bzip2(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_compress(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_gzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_lzip(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_lzma(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_none(struct archive *) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd) __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_xz(struct archive *) __LA_DEPRECATED; #endif /* A convenience function to set the filter based on the code. */ __LA_DECL int archive_write_add_filter(struct archive *, int filter_code); __LA_DECL int archive_write_add_filter_by_name(struct archive *, const char *name); __LA_DECL int archive_write_add_filter_b64encode(struct archive *); __LA_DECL int archive_write_add_filter_bzip2(struct archive *); __LA_DECL int archive_write_add_filter_compress(struct archive *); __LA_DECL int archive_write_add_filter_grzip(struct archive *); __LA_DECL int archive_write_add_filter_gzip(struct archive *); __LA_DECL int archive_write_add_filter_lrzip(struct archive *); __LA_DECL int archive_write_add_filter_lz4(struct archive *); __LA_DECL int archive_write_add_filter_lzip(struct archive *); __LA_DECL int archive_write_add_filter_lzma(struct archive *); __LA_DECL int archive_write_add_filter_lzop(struct archive *); __LA_DECL int archive_write_add_filter_none(struct archive *); __LA_DECL int archive_write_add_filter_program(struct archive *, const char *cmd); __LA_DECL int archive_write_add_filter_uuencode(struct archive *); __LA_DECL int archive_write_add_filter_xz(struct archive *); __LA_DECL int archive_write_add_filter_zstd(struct archive *); /* A convenience function to set the format based on the code or name. */ __LA_DECL int archive_write_set_format(struct archive *, int format_code); __LA_DECL int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ __LA_DECL int archive_write_set_format_7zip(struct archive *); __LA_DECL int archive_write_set_format_ar_bsd(struct archive *); __LA_DECL int archive_write_set_format_ar_svr4(struct archive *); __LA_DECL int archive_write_set_format_cpio(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_gnutar(struct archive *); __LA_DECL int archive_write_set_format_iso9660(struct archive *); __LA_DECL int archive_write_set_format_mtree(struct archive *); __LA_DECL int archive_write_set_format_mtree_classic(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __LA_DECL int archive_write_set_format_pax(struct archive *); __LA_DECL int archive_write_set_format_pax_restricted(struct archive *); __LA_DECL int archive_write_set_format_raw(struct archive *); __LA_DECL int archive_write_set_format_shar(struct archive *); __LA_DECL int archive_write_set_format_shar_dump(struct archive *); __LA_DECL int archive_write_set_format_ustar(struct archive *); __LA_DECL int archive_write_set_format_v7tar(struct archive *); __LA_DECL int archive_write_set_format_warc(struct archive *); __LA_DECL int archive_write_set_format_xar(struct archive *); __LA_DECL int archive_write_set_format_zip(struct archive *); __LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename); __LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext); __LA_DECL int archive_write_zip_set_compression_deflate(struct archive *); __LA_DECL int archive_write_zip_set_compression_store(struct archive *); +/* Deprecated; use archive_write_open2 instead */ __LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); +__LA_DECL int archive_write_open2(struct archive *, void *, + archive_open_callback *, archive_write_callback *, + archive_close_callback *, archive_free_callback *); __LA_DECL int archive_write_open_fd(struct archive *, int _fd); __LA_DECL int archive_write_open_filename(struct archive *, const char *_file); __LA_DECL int archive_write_open_filename_w(struct archive *, const wchar_t *_file); /* A deprecated synonym for archive_write_open_filename() */ __LA_DECL int archive_write_open_file(struct archive *, const char *_file) __LA_DEPRECATED; __LA_DECL int archive_write_open_FILE(struct archive *, FILE *); /* _buffSize is the size of the buffer, _used refers to a variable that * will be updated after each write into the buffer. */ __LA_DECL int archive_write_open_memory(struct archive *, void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided * to archive_write_header or pad if the provided data is short. */ __LA_DECL int archive_write_header(struct archive *, struct archive_entry *); __LA_DECL la_ssize_t archive_write_data(struct archive *, const void *, size_t); /* This interface is currently only available for archive_write_disk handles. */ __LA_DECL la_ssize_t archive_write_data_block(struct archive *, const void *, size_t, la_int64_t); __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); /* Marks the archive as FATAL so that a subsequent free() operation * won't try to close() cleanly. Provides a fast abort capability * when the client discovers that things have gone wrong. */ __LA_DECL int archive_write_fail(struct archive *); /* This can fail if the archive wasn't already closed, in which case * archive_write_free() will implicitly call archive_write_close(). */ __LA_DECL int archive_write_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_write_free() for backwards compatibility. */ __LA_DECL int archive_write_finish(struct archive *) __LA_DEPRECATED; #endif /* * Set write options. */ /* Apply option to the format only. */ __LA_DECL int archive_write_set_format_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to the filter only. */ __LA_DECL int archive_write_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option to both the format and the filter. */ __LA_DECL int archive_write_set_option(struct archive *_a, const char *m, const char *o, const char *v); /* Apply option string to both the format and the filter. */ __LA_DECL int archive_write_set_options(struct archive *_a, const char *opts); /* * Set a encryption passphrase. */ __LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p); __LA_DECL int archive_write_set_passphrase_callback(struct archive *, void *client_data, archive_passphrase_callback *); /*- * ARCHIVE_WRITE_DISK API * * To create objects on disk: * 1) Ask archive_write_disk_new for a new archive_write_disk object. * 2) Set any global properties. In particular, you probably * want to set the options. * 3) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to create the file/dir/etc on disk * - archive_write_data to write the entry data * 4) archive_write_free to cleanup the writer and release resources * * In particular, you can use this in conjunction with archive_read() * to pull entries out of an archive and create them on disk. */ __LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ __LA_DECL int archive_write_disk_set_skip_file(struct archive *, la_int64_t, la_int64_t); /* Set flags to control how the next item gets created. * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */ __LA_DECL int archive_write_disk_set_options(struct archive *, int flags); /* * The lookup functions are given uname/uid (or gname/gid) pairs and * return a uid (gid) suitable for this system. These are used for * restoring ownership and for setting ACLs. The default functions * are naive, they just return the uid/gid. These are small, so reasonable * for applications that don't need to preserve ownership; they * are probably also appropriate for applications that are doing * same-system backup and restore. */ /* * The "standard" lookup functions use common system calls to lookup * the uname/gname, falling back to the uid/gid if the names can't be * found. They cache lookups and are reasonably fast, but can be very * large, so they are not used unless you ask for them. In * particular, these match the specifications of POSIX "pax" and old * POSIX "tar". */ __LA_DECL int archive_write_disk_set_standard_lookup(struct archive *); /* * If neither the default (naive) nor the standard (big) functions suit * your needs, you can write your own and register them. Be sure to * include a cleanup function if you have allocated private data. */ __LA_DECL int archive_write_disk_set_group_lookup(struct archive *, void * /* private_data */, la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, void * /* private_data */, la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t); __LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t); /* * ARCHIVE_READ_DISK API * * This is still evolving and somewhat experimental. */ __LA_DECL struct archive *archive_read_disk_new(void); /* The names for symlink modes here correspond to an old BSD * command-line argument convention: -L, -P, -H */ /* Follow all symlinks. */ __LA_DECL int archive_read_disk_set_symlink_logical(struct archive *); /* Follow no symlinks. */ __LA_DECL int archive_read_disk_set_symlink_physical(struct archive *); /* Follow symlink initially, then not. */ __LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *); /* TODO: Handle Linux stat32/stat64 ugliness. */ __LA_DECL int archive_read_disk_entry_from_file(struct archive *, struct archive_entry *, int /* fd */, const struct stat *); /* Look up gname for gid or uname for uid. */ /* Default implementations are very, very stupid. */ __LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t); __LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t); /* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the * results for performance. */ __LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); /* You can install your own lookups if you like. */ __LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, la_int64_t), void (* /* cleanup_fn */)(void *)); __LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, void * /* private_data */, const char *(* /* lookup_fn */)(void *, la_int64_t), void (* /* cleanup_fn */)(void *)); /* Start traversal. */ __LA_DECL int archive_read_disk_open(struct archive *, const char *); __LA_DECL int archive_read_disk_open_w(struct archive *, const wchar_t *); /* * Request that current entry be visited. If you invoke it on every * directory, you'll get a physical traversal. This is ignored if the * current entry isn't a directory or a link to a directory. So, if * you invoke this on every returned path, you'll get a full logical * traversal. */ __LA_DECL int archive_read_disk_descend(struct archive *); __LA_DECL int archive_read_disk_can_descend(struct archive *); __LA_DECL int archive_read_disk_current_filesystem(struct archive *); __LA_DECL int archive_read_disk_current_filesystem_is_synthetic(struct archive *); __LA_DECL int archive_read_disk_current_filesystem_is_remote(struct archive *); /* Request that the access time of the entry visited by traversal be restored. */ __LA_DECL int archive_read_disk_set_atime_restored(struct archive *); /* * Set behavior. The "flags" argument selects optional behavior. */ /* Request that the access time of the entry visited by traversal be restored. * This is the same as archive_read_disk_set_atime_restored. */ #define ARCHIVE_READDISK_RESTORE_ATIME (0x0001) /* Default: Do not skip an entry which has nodump flags. */ #define ARCHIVE_READDISK_HONOR_NODUMP (0x0002) /* Default: Skip a mac resource fork file whose prefix is "._" because of * using copyfile. */ #define ARCHIVE_READDISK_MAC_COPYFILE (0x0004) /* Default: Traverse mount points. */ #define ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS (0x0008) /* Default: Xattrs are read from disk. */ #define ARCHIVE_READDISK_NO_XATTR (0x0010) /* Default: ACLs are read from disk. */ #define ARCHIVE_READDISK_NO_ACL (0x0020) /* Default: File flags are read from disk. */ #define ARCHIVE_READDISK_NO_FFLAGS (0x0040) __LA_DECL int archive_read_disk_set_behavior(struct archive *, int flags); /* * Set archive_match object that will be used in archive_read_disk to * know whether an entry should be skipped. The callback function * _excluded_func will be invoked when an entry is skipped by the result * of archive_match. */ __LA_DECL int archive_read_disk_set_matching(struct archive *, struct archive *_matching, void (*_excluded_func) (struct archive *, void *, struct archive_entry *), void *_client_data); __LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data); /* Simplified cleanup interface; * This calls archive_read_free() or archive_write_free() as needed. */ __LA_DECL int archive_free(struct archive *); /* * Accessor functions to read/set various information in * the struct archive object: */ /* Number of filters in the current filter pipeline. */ /* Filter #0 is the one closest to the format, -1 is a synonym for the * last filter, which is always the pseudo-filter that wraps the * client callbacks. */ __LA_DECL int archive_filter_count(struct archive *); __LA_DECL la_int64_t archive_filter_bytes(struct archive *, int); __LA_DECL int archive_filter_code(struct archive *, int); __LA_DECL const char * archive_filter_name(struct archive *, int); #if ARCHIVE_VERSION_NUMBER < 4000000 /* These don't properly handle multiple filters, so are deprecated and * will eventually be removed. */ /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */ __LA_DECL la_int64_t archive_position_compressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */ __LA_DECL la_int64_t archive_position_uncompressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */ __LA_DECL const char *archive_compression_name(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */ __LA_DECL int archive_compression(struct archive *) __LA_DEPRECATED; #endif __LA_DECL int archive_errno(struct archive *); __LA_DECL const char *archive_error_string(struct archive *); __LA_DECL const char *archive_format_name(struct archive *); __LA_DECL int archive_format(struct archive *); __LA_DECL void archive_clear_error(struct archive *); __LA_DECL void archive_set_error(struct archive *, int _err, const char *fmt, ...) __LA_PRINTF(3, 4); __LA_DECL void archive_copy_error(struct archive *dest, struct archive *src); __LA_DECL int archive_file_count(struct archive *); /* * ARCHIVE_MATCH API */ __LA_DECL struct archive *archive_match_new(void); __LA_DECL int archive_match_free(struct archive *); /* * Test if archive_entry is excluded. * This is a convenience function. This is the same as calling all * archive_match_path_excluded, archive_match_time_excluded * and archive_match_owner_excluded. */ __LA_DECL int archive_match_excluded(struct archive *, struct archive_entry *); /* * Test if pathname is excluded. The conditions are set by following functions. */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); /* Control recursive inclusion of directory content when directory is included. Default on. */ __LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, const wchar_t *); /* Add exclusion pathname pattern from file. */ __LA_DECL int archive_match_exclude_pattern_from_file(struct archive *, const char *, int _nullSeparator); __LA_DECL int archive_match_exclude_pattern_from_file_w(struct archive *, const wchar_t *, int _nullSeparator); /* Add inclusion pathname pattern. */ __LA_DECL int archive_match_include_pattern(struct archive *, const char *); __LA_DECL int archive_match_include_pattern_w(struct archive *, const wchar_t *); /* Add inclusion pathname pattern from file. */ __LA_DECL int archive_match_include_pattern_from_file(struct archive *, const char *, int _nullSeparator); __LA_DECL int archive_match_include_pattern_from_file_w(struct archive *, const wchar_t *, int _nullSeparator); /* * How to get statistic information for inclusion patterns. */ /* Return the amount number of unmatched inclusion patterns. */ __LA_DECL int archive_match_path_unmatched_inclusions(struct archive *); /* Return the pattern of unmatched inclusion with ARCHIVE_OK. * Return ARCHIVE_EOF if there is no inclusion pattern. */ __LA_DECL int archive_match_path_unmatched_inclusions_next( struct archive *, const char **); __LA_DECL int archive_match_path_unmatched_inclusions_next_w( struct archive *, const wchar_t **); /* * Test if a file is excluded by its time stamp. * The conditions are set by following functions. */ __LA_DECL int archive_match_time_excluded(struct archive *, struct archive_entry *); /* * Flags to tell a matching type of time stamps. These are used for * following functions. */ /* Time flag: mtime to be tested. */ #define ARCHIVE_MATCH_MTIME (0x0100) /* Time flag: ctime to be tested. */ #define ARCHIVE_MATCH_CTIME (0x0200) /* Comparison flag: Match the time if it is newer than. */ #define ARCHIVE_MATCH_NEWER (0x0001) /* Comparison flag: Match the time if it is older than. */ #define ARCHIVE_MATCH_OLDER (0x0002) /* Comparison flag: Match the time if it is equal to. */ #define ARCHIVE_MATCH_EQUAL (0x0010) /* Set inclusion time. */ __LA_DECL int archive_match_include_time(struct archive *, int _flag, time_t _sec, long _nsec); /* Set inclusion time by a date string. */ __LA_DECL int archive_match_include_date(struct archive *, int _flag, const char *_datestr); __LA_DECL int archive_match_include_date_w(struct archive *, int _flag, const wchar_t *_datestr); /* Set inclusion time by a particular file. */ __LA_DECL int archive_match_include_file_time(struct archive *, int _flag, const char *_pathname); __LA_DECL int archive_match_include_file_time_w(struct archive *, int _flag, const wchar_t *_pathname); /* Add exclusion entry. */ __LA_DECL int archive_match_exclude_entry(struct archive *, int _flag, struct archive_entry *); /* * Test if a file is excluded by its uid ,gid, uname or gname. * The conditions are set by following functions. */ __LA_DECL int archive_match_owner_excluded(struct archive *, struct archive_entry *); /* Add inclusion uid, gid, uname and gname. */ __LA_DECL int archive_match_include_uid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_gid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_uname(struct archive *, const char *); __LA_DECL int archive_match_include_uname_w(struct archive *, const wchar_t *); __LA_DECL int archive_match_include_gname(struct archive *, const char *); __LA_DECL int archive_match_include_gname_w(struct archive *, const wchar_t *); /* Utility functions */ /* Convenience function to sort a NULL terminated list of strings */ __LA_DECL int archive_utility_string_sort(char **); #ifdef __cplusplus } #endif /* These are meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_H_INCLUDED */ Index: stable/11/contrib/libarchive/libarchive/archive_acl.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_acl.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_acl.c (revision 368707) @@ -1,2097 +1,2097 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive_acl_private.h" #include "archive_entry.h" #include "archive_private.h" #undef max #define max(a, b) ((a)>(b)?(a):(b)) #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif static int acl_special(struct archive_acl *acl, int type, int permset, int tag); static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, int type, int permset, int tag, int id); static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc); static int archive_acl_text_want_type(struct archive_acl *acl, int flags); static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, int wide, struct archive *a, struct archive_string_conv *sc); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *result); static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, int tag, int flags, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); static int isint(const char *start, const char *end, int *result); static int ismode(const char *start, const char *end, int *result); static int is_nfs4_flags(const char *start, const char *end, int *result); static int is_nfs4_perms(const char *start, const char *end, int *result); static void next_field(const char **p, const char **start, const char **end, char *sep); static void append_entry(char **p, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id); static void append_id(char **p, int id); static const struct { const int perm; const char c; const wchar_t wc; } nfsv4_acl_perm_map[] = { { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r', L'r' }, { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w', L'w' }, { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' }, { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, 'p', L'p' }, { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' }, { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' }, { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' }, { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' }, { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' }, { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' }, { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' }, { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' }, { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' }, { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' } }; static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) / sizeof(nfsv4_acl_perm_map[0])); static const struct { const int perm; const char c; const wchar_t wc; } nfsv4_acl_flag_map[] = { { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' }, { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' }, { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' }, { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' }, { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' }, { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' }, { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' } }; static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) / sizeof(nfsv4_acl_flag_map[0])); void archive_acl_clear(struct archive_acl *acl) { struct archive_acl_entry *ap; while (acl->acl_head != NULL) { ap = acl->acl_head->next; archive_mstring_clean(&acl->acl_head->name); free(acl->acl_head); acl->acl_head = ap; } free(acl->acl_text_w); acl->acl_text_w = NULL; free(acl->acl_text); acl->acl_text = NULL; acl->acl_p = NULL; acl->acl_types = 0; acl->acl_state = 0; /* Not counting. */ } void archive_acl_copy(struct archive_acl *dest, struct archive_acl *src) { struct archive_acl_entry *ap, *ap2; archive_acl_clear(dest); dest->mode = src->mode; ap = src->acl_head; while (ap != NULL) { ap2 = acl_new_entry(dest, ap->type, ap->permset, ap->tag, ap->id); if (ap2 != NULL) archive_mstring_copy(&ap2->name, &ap->name); ap = ap->next; } } int archive_acl_add_entry(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name) { struct archive_acl_entry *ap; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != '\0') archive_mstring_copy_mbs(&ap->name, name); else archive_mstring_clean(&ap->name); return ARCHIVE_OK; } int archive_acl_add_entry_w_len(struct archive_acl *acl, int type, int permset, int tag, int id, const wchar_t *name, size_t len) { struct archive_acl_entry *ap; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != L'\0' && len > 0) archive_mstring_copy_wcs_len(&ap->name, name, len); else archive_mstring_clean(&ap->name); return ARCHIVE_OK; } static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc) { struct archive_acl_entry *ap; int r; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != '\0' && len > 0) { r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); } else { r = 0; archive_mstring_clean(&ap->name); } if (r == 0) return (ARCHIVE_OK); else if (errno == ENOMEM) return (ARCHIVE_FATAL); else return (ARCHIVE_WARN); } /* * If this ACL entry is part of the standard POSIX permissions set, * store the permissions in the stat structure and return zero. */ static int acl_special(struct archive_acl *acl, int type, int permset, int tag) { if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && ((permset & ~007) == 0)) { switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: acl->mode &= ~0700; acl->mode |= (permset & 7) << 6; return (0); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl->mode &= ~0070; acl->mode |= (permset & 7) << 3; return (0); case ARCHIVE_ENTRY_ACL_OTHER: acl->mode &= ~0007; acl->mode |= permset & 7; return (0); } } return (1); } /* * Allocate and populate a new ACL entry with everything but the * name. */ static struct archive_acl_entry * acl_new_entry(struct archive_acl *acl, int type, int permset, int tag, int id) { struct archive_acl_entry *ap, *aq; /* Type argument must be a valid NFS4 or POSIX.1e type. * The type must agree with anything already set and * the permset must be compatible. */ if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { return (NULL); } if (permset & ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { return (NULL); } } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { return (NULL); } if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { return (NULL); } } else { return (NULL); } /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ switch (tag) { case ARCHIVE_ENTRY_ACL_USER: case ARCHIVE_ENTRY_ACL_USER_OBJ: case ARCHIVE_ENTRY_ACL_GROUP: case ARCHIVE_ENTRY_ACL_GROUP_OBJ: /* Tags valid in both NFS4 and POSIX.1e */ break; case ARCHIVE_ENTRY_ACL_MASK: case ARCHIVE_ENTRY_ACL_OTHER: /* Tags valid only in POSIX.1e. */ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { return (NULL); } break; case ARCHIVE_ENTRY_ACL_EVERYONE: /* Tags valid only in NFS4. */ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { return (NULL); } break; default: /* No other values are valid. */ return (NULL); } free(acl->acl_text_w); acl->acl_text_w = NULL; free(acl->acl_text); acl->acl_text = NULL; /* * If there's a matching entry already in the list, overwrite it. * NFSv4 entries may be repeated and are not overwritten. * * TODO: compare names of no id is provided (needs more rework) */ ap = acl->acl_head; aq = NULL; while (ap != NULL) { if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) && ap->type == type && ap->tag == tag && ap->id == id) { if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && tag != ARCHIVE_ENTRY_ACL_GROUP)) { ap->permset = permset; return (ap); } } aq = ap; ap = ap->next; } /* Add a new entry to the end of the list. */ ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap)); if (ap == NULL) return (NULL); if (aq == NULL) acl->acl_head = ap; else aq->next = ap; ap->type = type; ap->tag = tag; ap->id = id; ap->permset = permset; acl->acl_types |= type; return (ap); } /* * Return a count of entries matching "want_type". */ int archive_acl_count(struct archive_acl *acl, int want_type) { int count; struct archive_acl_entry *ap; count = 0; ap = acl->acl_head; while (ap != NULL) { if ((ap->type & want_type) != 0) count++; ap = ap->next; } if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) count += 3; return (count); } /* * Return a bitmask of stored ACL types in an ACL list */ int archive_acl_types(struct archive_acl *acl) { return (acl->acl_types); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_acl_reset(struct archive_acl *acl, int want_type) { int count, cutoff; count = archive_acl_count(acl, want_type); /* * If the only entries are the three standard ones, * then don't return any ACL data. (In this case, * client can just use chmod(2) to set permissions.) */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) cutoff = 3; else cutoff = 0; if (count > cutoff) acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; else acl->acl_state = 0; acl->acl_p = acl->acl_head; return (count); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { *name = NULL; *id = -1; /* * The acl_state is either zero (no entries available), -1 * (reading from list), or an entry type (retrieve that type * from ae_stat.aest_mode). */ if (acl->acl_state == 0) return (ARCHIVE_WARN); /* The first three access entries are special. */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { switch (acl->acl_state) { case ARCHIVE_ENTRY_ACL_USER_OBJ: *permset = (acl->mode >> 6) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: *permset = (acl->mode >> 3) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_OTHER: *permset = acl->mode & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_OTHER; acl->acl_state = -1; acl->acl_p = acl->acl_head; return (ARCHIVE_OK); default: break; } } while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) acl->acl_p = acl->acl_p->next; if (acl->acl_p == NULL) { acl->acl_state = 0; *type = 0; *permset = 0; *tag = 0; *id = -1; *name = NULL; return (ARCHIVE_EOF); /* End of ACL entries. */ } *type = acl->acl_p->type; *permset = acl->acl_p->permset; *tag = acl->acl_p->tag; *id = acl->acl_p->id; if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { if (errno == ENOMEM) return (ARCHIVE_FATAL); *name = NULL; } acl->acl_p = acl->acl_p->next; return (ARCHIVE_OK); } /* * Determine what type of ACL do we want */ static int archive_acl_text_want_type(struct archive_acl *acl, int flags) { int want_type; /* Check if ACL is NFSv4 */ if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { /* NFSv4 should never mix with POSIX.1e */ if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) return (0); else return (ARCHIVE_ENTRY_ACL_TYPE_NFS4); } /* Now deal with POSIX.1e ACLs */ want_type = 0; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; /* By default we want both access and default ACLs */ if (want_type == 0) return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E); return (want_type); } /* * Calculate ACL text string length */ static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, int wide, struct archive *a, struct archive_string_conv *sc) { struct archive_acl_entry *ap; const char *name; const wchar_t *wname; int count, idlen, tmp, r; ssize_t length; size_t len; count = 0; length = 0; for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & want_type) == 0) continue; /* * Filemode-mapping ACL entries are stored exclusively in * ap->mode so they should not be in the list */ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) continue; count++; if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) length += 8; /* "default:" */ switch (ap->tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { length += 6; /* "owner@" */ break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: case ARCHIVE_ENTRY_ACL_MASK: length += 4; /* "user", "mask" */ break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { length += 6; /* "group@" */ break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: case ARCHIVE_ENTRY_ACL_OTHER: length += 5; /* "group", "other" */ break; case ARCHIVE_ENTRY_ACL_EVERYONE: length += 9; /* "everyone@" */ break; } length += 1; /* colon after tag */ if (ap->tag == ARCHIVE_ENTRY_ACL_USER || ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { if (wide) { r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0 && wname != NULL) length += wcslen(wname); else if (r < 0 && errno == ENOMEM) return (0); else length += sizeof(uid_t) * 3 + 1; } else { - r = archive_mstring_get_mbs_l(&ap->name, &name, + r = archive_mstring_get_mbs_l(a, &ap->name, &name, &len, sc); if (r != 0) return (0); if (len > 0 && name != NULL) length += len; else length += sizeof(uid_t) * 3 + 1; } length += 1; /* colon after user or group name */ } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) length += 1; /* 2nd colon empty user,group or other */ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) { /* Solaris has no colon after other: and mask: */ length = length - 1; } if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { /* rwxpdDaARWcCos:fdinSFI:deny */ length += 27; if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0) length += 1; /* allow, alarm, audit */ } else length += 3; /* rwx */ if ((ap->tag == ARCHIVE_ENTRY_ACL_USER || ap->tag == ARCHIVE_ENTRY_ACL_GROUP) && (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) { length += 1; /* colon */ /* ID digit count */ idlen = 1; tmp = ap->id; while (tmp > 9) { tmp = tmp / 10; idlen++; } length += idlen; } length ++; /* entry separator */ } /* Add filemode-mapping access entries to the length */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) { /* "user::rwx\ngroup::rwx\nother:rwx\n" */ length += 31; } else { /* "user::rwx\ngroup::rwx\nother::rwx\n" */ length += 32; } } else if (count == 0) return (0); /* The terminating character is included in count */ return (length); } /* * Generate a wide text version of the ACL. The flags parameter controls * the type and style of the generated ACL. */ wchar_t * archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive *a) { int count; ssize_t length; size_t len; const wchar_t *wname; const wchar_t *prefix; wchar_t separator; struct archive_acl_entry *ap; int id, r, want_type; wchar_t *wp, *ws; want_type = archive_acl_text_want_type(acl, flags); /* Both NFSv4 and POSIX.1 types found */ if (want_type == 0) return (NULL); if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL); if (length == 0) return (NULL); if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) separator = L','; else separator = L'\n'; /* Now, allocate the string and actually populate it. */ wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wp == NULL) { if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } count = 0; if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); *wp++ = separator; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); *wp++ = separator; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); count += 3; } for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & want_type) == 0) continue; /* * Filemode-mapping ACL entries are stored exclusively in * ap->mode so they should not be in the list */ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) continue; if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) prefix = L"default:"; else prefix = NULL; r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0) { if (count > 0) *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, prefix, ap->type, ap->tag, flags, wname, ap->permset, id); count++; } else if (r < 0 && errno == ENOMEM) { free(ws); return (NULL); } } /* Add terminating character */ *wp++ = L'\0'; len = wcslen(ws); if ((ssize_t)len > (length - 1)) __archive_errx(1, "Buffer overrun"); if (text_len != NULL) *text_len = len; return (ws); } static void append_id_w(wchar_t **wp, int id) { if (id < 0) id = 0; if (id > 9) append_id_w(wp, id / 10); *(*wp)++ = L"0123456789"[id % 10]; } static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, int tag, int flags, const wchar_t *wname, int perm, int id) { int i; if (prefix != NULL) { wcscpy(*wp, prefix); *wp += wcslen(*wp); } switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { wcscpy(*wp, L"owner@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { wcscpy(*wp, L"group@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: wcscpy(*wp, L"group"); break; case ARCHIVE_ENTRY_ACL_MASK: wcscpy(*wp, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: wcscpy(*wp, L"other"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: wcscpy(*wp, L"everyone@"); wname = NULL; id = -1; break; } *wp += wcslen(*wp); *(*wp)++ = L':'; if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { if (wname != NULL) { wcscpy(*wp, wname); *wp += wcslen(*wp); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id_w(wp, id); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) id = -1; } /* Solaris style has no second colon after other and mask */ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) || (tag != ARCHIVE_ENTRY_ACL_OTHER && tag != ARCHIVE_ENTRY_ACL_MASK)) *(*wp)++ = L':'; } if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { /* POSIX.1e ACL perms */ *(*wp)++ = (perm & 0444) ? L'r' : L'-'; *(*wp)++ = (perm & 0222) ? L'w' : L'-'; *(*wp)++ = (perm & 0111) ? L'x' : L'-'; } else { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) *(*wp)++ = nfsv4_acl_perm_map[i].wc; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*wp)++ = L'-'; } *(*wp)++ = L':'; for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) *(*wp)++ = nfsv4_acl_flag_map[i].wc; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*wp)++ = L'-'; } *(*wp)++ = L':'; switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: wcscpy(*wp, L"allow"); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: wcscpy(*wp, L"deny"); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: wcscpy(*wp, L"audit"); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: wcscpy(*wp, L"alarm"); break; default: break; } *wp += wcslen(*wp); } if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); } } /* * Generate a text version of the ACL. The flags parameter controls * the type and style of the generated ACL. */ char * archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive_string_conv *sc) { int count; ssize_t length; size_t len; const char *name; const char *prefix; char separator; struct archive_acl_entry *ap; int id, r, want_type; char *p, *s; want_type = archive_acl_text_want_type(acl, flags); /* Both NFSv4 and POSIX.1 types found */ if (want_type == 0) return (NULL); if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); if (length == 0) return (NULL); if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) separator = ','; else separator = '\n'; /* Now, allocate the string and actually populate it. */ p = s = (char *)malloc(length * sizeof(char)); if (p == NULL) { if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } count = 0; if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); *p++ = separator; append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); *p++ = separator; append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); count += 3; } for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & want_type) == 0) continue; /* * Filemode-mapping ACL entries are stored exclusively in * ap->mode so they should not be in the list */ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) continue; if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) prefix = "default:"; else prefix = NULL; r = archive_mstring_get_mbs_l( - &ap->name, &name, &len, sc); + NULL, &ap->name, &name, &len, sc); if (r != 0) { free(s); return (NULL); } if (count > 0) *p++ = separator; if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { id = ap->id; } else { id = -1; } append_entry(&p, prefix, ap->type, ap->tag, flags, name, ap->permset, id); count++; } /* Add terminating character */ *p++ = '\0'; len = strlen(s); if ((ssize_t)len > (length - 1)) __archive_errx(1, "Buffer overrun"); if (text_len != NULL) *text_len = len; return (s); } static void append_id(char **p, int id) { if (id < 0) id = 0; if (id > 9) append_id(p, id / 10); *(*p)++ = "0123456789"[id % 10]; } static void append_entry(char **p, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id) { int i; if (prefix != NULL) { strcpy(*p, prefix); *p += strlen(*p); } switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: name = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { strcpy(*p, "owner@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: strcpy(*p, "user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: name = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { strcpy(*p, "group@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: strcpy(*p, "group"); break; case ARCHIVE_ENTRY_ACL_MASK: strcpy(*p, "mask"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: strcpy(*p, "other"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: strcpy(*p, "everyone@"); name = NULL; id = -1; break; } *p += strlen(*p); *(*p)++ = ':'; if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { if (name != NULL) { strcpy(*p, name); *p += strlen(*p); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id(p, id); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) id = -1; } /* Solaris style has no second colon after other and mask */ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) || (tag != ARCHIVE_ENTRY_ACL_OTHER && tag != ARCHIVE_ENTRY_ACL_MASK)) *(*p)++ = ':'; } if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { /* POSIX.1e ACL perms */ *(*p)++ = (perm & 0444) ? 'r' : '-'; *(*p)++ = (perm & 0222) ? 'w' : '-'; *(*p)++ = (perm & 0111) ? 'x' : '-'; } else { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) *(*p)++ = nfsv4_acl_perm_map[i].c; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*p)++ = '-'; } *(*p)++ = ':'; for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) *(*p)++ = nfsv4_acl_flag_map[i].c; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*p)++ = '-'; } *(*p)++ = ':'; switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: strcpy(*p, "allow"); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: strcpy(*p, "deny"); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: strcpy(*p, "audit"); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: strcpy(*p, "alarm"); break; } *p += strlen(*p); } if (id != -1) { *(*p)++ = ':'; append_id(p, id); } } /* * Parse a wide ACL text string. * * The want_type argument may be one of the following: * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL * * POSIX.1e ACL entries prefixed with "default:" are treated as * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 */ int archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, int want_type) { struct { const wchar_t *start; const wchar_t *end; } field[6], name; const wchar_t *s, *st; int numfields, fields, n, r, sol, ret; int type, types, tag, permset, id; size_t len; wchar_t sep; ret = ARCHIVE_OK; types = 0; switch (want_type) { case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; __LA_FALLTHROUGH; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: numfields = 5; break; case ARCHIVE_ENTRY_ACL_TYPE_NFS4: numfields = 6; break; default: return (ARCHIVE_FATAL); } while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); if (fields < numfields) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == L':'); /* Set remaining fields to blank. */ for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; if (field[0].start != NULL && *(field[0].start) == L'#') { /* Comment, skip entry */ continue; } n = 0; sol = 0; id = -1; permset = 0; name.start = name.end = NULL; if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { /* POSIX.1e ACLs */ /* * Default keyword "default:user::rwx" * if found, we have one more field * * We also support old Solaris extension: * "defaultuser::rwx" is the default ACL corresponding * to "user::rwx", etc. valid only for first field */ s = field[0].start; len = field[0].end - field[0].start; if (*s == L'd' && (len == 1 || (len >= 7 && wmemcmp((s + 1), L"efault", 6) == 0))) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; if (len > 7) field[0].start += 7; else n = 1; } else type = want_type; /* Check for a numeric ID in field n+1 or n+3. */ isint_w(field[n + 1].start, field[n + 1].end, &id); /* Field n+3 is optional. */ if (id == -1 && fields > n+3) isint_w(field[n + 3].start, field[n + 3].end, &id); tag = 0; s = field[n].start; st = field[n].start + 1; len = field[n].end - field[n].start; switch (*s) { case L'u': if (len == 1 || (len == 4 && wmemcmp(st, L"ser", 3) == 0)) tag = ARCHIVE_ENTRY_ACL_USER_OBJ; break; case L'g': if (len == 1 || (len == 5 && wmemcmp(st, L"roup", 4) == 0)) tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case L'o': if (len == 1 || (len == 5 && wmemcmp(st, L"ther", 4) == 0)) tag = ARCHIVE_ENTRY_ACL_OTHER; break; case L'm': if (len == 1 || (len == 4 && wmemcmp(st, L"ask", 3) == 0)) tag = ARCHIVE_ENTRY_ACL_MASK; break; default: break; } switch (tag) { case ARCHIVE_ENTRY_ACL_OTHER: case ARCHIVE_ENTRY_ACL_MASK: if (fields == (n + 2) && field[n + 1].start < field[n + 1].end && ismode_w(field[n + 1].start, field[n + 1].end, &permset)) { /* This is Solaris-style "other:rwx" */ sol = 1; } else if (fields == (n + 3) && field[n + 1].start < field[n + 1].end) { /* Invalid mask or other field */ ret = ARCHIVE_WARN; continue; } break; case ARCHIVE_ENTRY_ACL_USER_OBJ: case ARCHIVE_ENTRY_ACL_GROUP_OBJ: if (id != -1 || field[n + 1].start < field[n + 1].end) { name = field[n + 1]; if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) tag = ARCHIVE_ENTRY_ACL_USER; else tag = ARCHIVE_ENTRY_ACL_GROUP; } break; default: /* Invalid tag, skip entry */ ret = ARCHIVE_WARN; continue; } /* * Without "default:" we expect mode in field 2 * Exception: Solaris other and mask fields */ if (permset == 0 && !ismode_w(field[n + 2 - sol].start, field[n + 2 - sol].end, &permset)) { /* Invalid mode, skip entry */ ret = ARCHIVE_WARN; continue; } } else { /* NFS4 ACLs */ s = field[0].start; len = field[0].end - field[0].start; tag = 0; switch (len) { case 4: if (wmemcmp(s, L"user", 4) == 0) tag = ARCHIVE_ENTRY_ACL_USER; break; case 5: if (wmemcmp(s, L"group", 5) == 0) tag = ARCHIVE_ENTRY_ACL_GROUP; break; case 6: if (wmemcmp(s, L"owner@", 6) == 0) tag = ARCHIVE_ENTRY_ACL_USER_OBJ; else if (wmemcmp(s, L"group@", len) == 0) tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case 9: if (wmemcmp(s, L"everyone@", 9) == 0) tag = ARCHIVE_ENTRY_ACL_EVERYONE; default: break; } if (tag == 0) { /* Invalid tag, skip entry */ ret = ARCHIVE_WARN; continue; } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { n = 1; name = field[1]; isint_w(name.start, name.end, &id); } else n = 0; if (!is_nfs4_perms_w(field[1 + n].start, field[1 + n].end, &permset)) { /* Invalid NFSv4 perms, skip entry */ ret = ARCHIVE_WARN; continue; } if (!is_nfs4_flags_w(field[2 + n].start, field[2 + n].end, &permset)) { /* Invalid NFSv4 flags, skip entry */ ret = ARCHIVE_WARN; continue; } s = field[3 + n].start; len = field[3 + n].end - field[3 + n].start; type = 0; if (len == 4) { if (wmemcmp(s, L"deny", 4) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_DENY; } else if (len == 5) { if (wmemcmp(s, L"allow", 5) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; else if (wmemcmp(s, L"audit", 5) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; else if (wmemcmp(s, L"alarm", 5) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; } if (type == 0) { /* Invalid entry type, skip entry */ ret = ARCHIVE_WARN; continue; } isint_w(field[4 + n].start, field[4 + n].end, &id); } /* Add entry to the internal list. */ r = archive_acl_add_entry_w_len(acl, type, permset, tag, id, name.start, name.end - name.start); if (r < ARCHIVE_WARN) return (r); if (r != ARCHIVE_OK) ret = ARCHIVE_WARN; types |= type; } /* Reset ACL */ archive_acl_reset(acl, types); return (ret); } /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint_w(const wchar_t *start, const wchar_t *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { - if (*start < '0' || *start > '9') + if (*start < L'0' || *start > L'9') return (0); if (n > (INT_MAX / 10) || - (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { + (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) { n = INT_MAX; } else { n *= 10; - n += *start - '0'; + n += *start - L'0'; } start++; } *result = n; return (1); } /* * Parse a string as a mode field. Returns true if * the string is non-empty and consists only of mode characters, * false otherwise. */ static int ismode_w(const wchar_t *start, const wchar_t *end, int *permset) { const wchar_t *p; if (start >= end) return (0); p = start; *permset = 0; while (p < end) { switch (*p++) { case L'r': case L'R': *permset |= ARCHIVE_ENTRY_ACL_READ; break; case L'w': case L'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case L'x': case L'X': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case L'-': break; default: return (0); } } return (1); } /* * Parse a string as a NFS4 ACL permission field. * Returns true if the string is non-empty and consists only of NFS4 ACL * permission characters, false otherwise */ static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) { const wchar_t *p = start; while (p < end) { switch (*p++) { case L'r': *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; break; case L'w': *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; break; case L'x': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case L'p': *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; break; case L'D': *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; break; case L'd': *permset |= ARCHIVE_ENTRY_ACL_DELETE; break; case L'a': *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; break; case L'A': *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; break; case L'R': *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; break; case L'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; break; case L'c': *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; break; case L'C': *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; break; case L'o': *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; break; case L's': *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; break; case L'-': break; default: return(0); } } return (1); } /* * Parse a string as a NFS4 ACL flags field. * Returns true if the string is non-empty and consists only of NFS4 ACL * flag characters, false otherwise */ static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) { const wchar_t *p = start; while (p < end) { switch(*p++) { case L'f': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; break; case L'd': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; break; case L'i': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; break; case L'n': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; break; case L'S': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; break; case L'F': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; break; case L'I': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; break; case L'-': break; default: return (0); } } return (1); } /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep) { /* Skip leading whitespace to find start of field. */ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { (*wp)++; } *start = *wp; /* Scan for the separator. */ while (**wp != L'\0' && **wp != L',' && **wp != L':' && **wp != L'\n' && **wp != L'#') { (*wp)++; } *sep = **wp; /* Locate end of field, trim trailing whitespace if necessary */ if (*wp == *start) { *end = *wp; } else { *end = *wp - 1; while (**end == L' ' || **end == L'\t' || **end == L'\n') { (*end)--; } (*end)++; } /* Handle in-field comments */ if (*sep == L'#') { while (**wp != L'\0' && **wp != L',' && **wp != L'\n') { (*wp)++; } *sep = **wp; } /* Adjust scanner location. */ if (**wp != L'\0') (*wp)++; } /* * Parse an ACL text string. * * The want_type argument may be one of the following: * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL * * POSIX.1e ACL entries prefixed with "default:" are treated as * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 */ int archive_acl_from_text_l(struct archive_acl *acl, const char *text, int want_type, struct archive_string_conv *sc) { struct { const char *start; const char *end; } field[6], name; const char *s, *st; int numfields, fields, n, r, sol, ret; int type, types, tag, permset, id; size_t len; char sep; switch (want_type) { case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; __LA_FALLTHROUGH; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: numfields = 5; break; case ARCHIVE_ENTRY_ACL_TYPE_NFS4: numfields = 6; break; default: return (ARCHIVE_FATAL); } ret = ARCHIVE_OK; types = 0; while (text != NULL && *text != '\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const char *start, *end; next_field(&text, &start, &end, &sep); if (fields < numfields) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == ':'); /* Set remaining fields to blank. */ for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; if (field[0].start != NULL && *(field[0].start) == '#') { /* Comment, skip entry */ continue; } n = 0; sol = 0; id = -1; permset = 0; name.start = name.end = NULL; if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { /* POSIX.1e ACLs */ /* * Default keyword "default:user::rwx" * if found, we have one more field * * We also support old Solaris extension: * "defaultuser::rwx" is the default ACL corresponding * to "user::rwx", etc. valid only for first field */ s = field[0].start; len = field[0].end - field[0].start; if (*s == 'd' && (len == 1 || (len >= 7 && memcmp((s + 1), "efault", 6) == 0))) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; if (len > 7) field[0].start += 7; else n = 1; } else type = want_type; /* Check for a numeric ID in field n+1 or n+3. */ isint(field[n + 1].start, field[n + 1].end, &id); /* Field n+3 is optional. */ if (id == -1 && fields > (n + 3)) isint(field[n + 3].start, field[n + 3].end, &id); tag = 0; s = field[n].start; st = field[n].start + 1; len = field[n].end - field[n].start; if (len == 0) { ret = ARCHIVE_WARN; continue; } switch (*s) { case 'u': if (len == 1 || (len == 4 && memcmp(st, "ser", 3) == 0)) tag = ARCHIVE_ENTRY_ACL_USER_OBJ; break; case 'g': if (len == 1 || (len == 5 && memcmp(st, "roup", 4) == 0)) tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case 'o': if (len == 1 || (len == 5 && memcmp(st, "ther", 4) == 0)) tag = ARCHIVE_ENTRY_ACL_OTHER; break; case 'm': if (len == 1 || (len == 4 && memcmp(st, "ask", 3) == 0)) tag = ARCHIVE_ENTRY_ACL_MASK; break; default: break; } switch (tag) { case ARCHIVE_ENTRY_ACL_OTHER: case ARCHIVE_ENTRY_ACL_MASK: if (fields == (n + 2) && field[n + 1].start < field[n + 1].end && ismode(field[n + 1].start, field[n + 1].end, &permset)) { /* This is Solaris-style "other:rwx" */ sol = 1; } else if (fields == (n + 3) && field[n + 1].start < field[n + 1].end) { /* Invalid mask or other field */ ret = ARCHIVE_WARN; continue; } break; case ARCHIVE_ENTRY_ACL_USER_OBJ: case ARCHIVE_ENTRY_ACL_GROUP_OBJ: if (id != -1 || field[n + 1].start < field[n + 1].end) { name = field[n + 1]; if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) tag = ARCHIVE_ENTRY_ACL_USER; else tag = ARCHIVE_ENTRY_ACL_GROUP; } break; default: /* Invalid tag, skip entry */ ret = ARCHIVE_WARN; continue; } /* * Without "default:" we expect mode in field 3 * Exception: Solaris other and mask fields */ if (permset == 0 && !ismode(field[n + 2 - sol].start, field[n + 2 - sol].end, &permset)) { /* Invalid mode, skip entry */ ret = ARCHIVE_WARN; continue; } } else { /* NFS4 ACLs */ s = field[0].start; len = field[0].end - field[0].start; tag = 0; switch (len) { case 4: if (memcmp(s, "user", 4) == 0) tag = ARCHIVE_ENTRY_ACL_USER; break; case 5: if (memcmp(s, "group", 5) == 0) tag = ARCHIVE_ENTRY_ACL_GROUP; break; case 6: if (memcmp(s, "owner@", 6) == 0) tag = ARCHIVE_ENTRY_ACL_USER_OBJ; else if (memcmp(s, "group@", 6) == 0) tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case 9: if (memcmp(s, "everyone@", 9) == 0) tag = ARCHIVE_ENTRY_ACL_EVERYONE; break; default: break; } if (tag == 0) { /* Invalid tag, skip entry */ ret = ARCHIVE_WARN; continue; } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { n = 1; name = field[1]; isint(name.start, name.end, &id); } else n = 0; if (!is_nfs4_perms(field[1 + n].start, field[1 + n].end, &permset)) { /* Invalid NFSv4 perms, skip entry */ ret = ARCHIVE_WARN; continue; } if (!is_nfs4_flags(field[2 + n].start, field[2 + n].end, &permset)) { /* Invalid NFSv4 flags, skip entry */ ret = ARCHIVE_WARN; continue; } s = field[3 + n].start; len = field[3 + n].end - field[3 + n].start; type = 0; if (len == 4) { if (memcmp(s, "deny", 4) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_DENY; } else if (len == 5) { if (memcmp(s, "allow", 5) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; else if (memcmp(s, "audit", 5) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; else if (memcmp(s, "alarm", 5) == 0) type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; } if (type == 0) { /* Invalid entry type, skip entry */ ret = ARCHIVE_WARN; continue; } isint(field[4 + n].start, field[4 + n].end, &id); } /* Add entry to the internal list. */ r = archive_acl_add_entry_len_l(acl, type, permset, tag, id, name.start, name.end - name.start, sc); if (r < ARCHIVE_WARN) return (r); if (r != ARCHIVE_OK) ret = ARCHIVE_WARN; types |= type; } /* Reset ACL */ archive_acl_reset(acl, types); return (ret); } /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint(const char *start, const char *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10) || (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { n = INT_MAX; } else { n *= 10; n += *start - '0'; } start++; } *result = n; return (1); } /* * Parse a string as a mode field. Returns true if * the string is non-empty and consists only of mode characters, * false otherwise. */ static int ismode(const char *start, const char *end, int *permset) { const char *p; if (start >= end) return (0); p = start; *permset = 0; while (p < end) { switch (*p++) { case 'r': case 'R': *permset |= ARCHIVE_ENTRY_ACL_READ; break; case 'w': case 'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case 'x': case 'X': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case '-': break; default: return (0); } } return (1); } /* * Parse a string as a NFS4 ACL permission field. * Returns true if the string is non-empty and consists only of NFS4 ACL * permission characters, false otherwise */ static int is_nfs4_perms(const char *start, const char *end, int *permset) { const char *p = start; while (p < end) { switch (*p++) { case 'r': *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; break; case 'w': *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; break; case 'x': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case 'p': *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; break; case 'D': *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; break; case 'd': *permset |= ARCHIVE_ENTRY_ACL_DELETE; break; case 'a': *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; break; case 'A': *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; break; case 'R': *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; break; case 'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; break; case 'c': *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; break; case 'C': *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; break; case 'o': *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; break; case 's': *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; break; case '-': break; default: return(0); } } return (1); } /* * Parse a string as a NFS4 ACL flags field. * Returns true if the string is non-empty and consists only of NFS4 ACL * flag characters, false otherwise */ static int is_nfs4_flags(const char *start, const char *end, int *permset) { const char *p = start; while (p < end) { switch(*p++) { case 'f': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; break; case 'd': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; break; case 'i': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; break; case 'n': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; break; case 'S': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; break; case 'F': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; break; case 'I': *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; break; case '-': break; default: return (0); } } return (1); } /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field(const char **p, const char **start, const char **end, char *sep) { /* Skip leading whitespace to find start of field. */ while (**p == ' ' || **p == '\t' || **p == '\n') { (*p)++; } *start = *p; /* Scan for the separator. */ while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' && **p != '#') { (*p)++; } *sep = **p; /* Locate end of field, trim trailing whitespace if necessary */ if (*p == *start) { *end = *p; } else { *end = *p - 1; while (**end == ' ' || **end == '\t' || **end == '\n') { (*end)--; } (*end)++; } /* Handle in-field comments */ if (*sep == '#') { while (**p != '\0' && **p != ',' && **p != '\n') { (*p)++; } *sep = **p; } /* Adjust scanner location. */ if (**p != '\0') (*p)++; } Index: stable/11/contrib/libarchive/libarchive/archive_check_magic.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_check_magic.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_check_magic.c (revision 368707) @@ -1,175 +1,175 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #endif #include "archive_private.h" static void errmsg(const char *m) { size_t s = strlen(m); ssize_t written; while (s > 0) { - written = write(2, m, strlen(m)); + written = write(2, m, s); if (written <= 0) return; m += written; s -= written; } } static __LA_DEAD void diediedie(void) { #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) /* Cause a breakpoint exception */ DebugBreak(); #endif abort(); /* Terminate the program abnormally. */ } static const char * state_name(unsigned s) { switch (s) { case ARCHIVE_STATE_NEW: return ("new"); case ARCHIVE_STATE_HEADER: return ("header"); case ARCHIVE_STATE_DATA: return ("data"); case ARCHIVE_STATE_EOF: return ("eof"); case ARCHIVE_STATE_CLOSED: return ("closed"); case ARCHIVE_STATE_FATAL: return ("fatal"); default: return ("??"); } } static const char * archive_handle_type_name(unsigned m) { switch (m) { case ARCHIVE_WRITE_MAGIC: return ("archive_write"); case ARCHIVE_READ_MAGIC: return ("archive_read"); case ARCHIVE_WRITE_DISK_MAGIC: return ("archive_write_disk"); case ARCHIVE_READ_DISK_MAGIC: return ("archive_read_disk"); case ARCHIVE_MATCH_MAGIC: return ("archive_match"); default: return NULL; } } static char * write_all_states(char *buff, unsigned int states) { unsigned int lowbit; buff[0] = '\0'; /* A trick for computing the lowest set bit. */ while ((lowbit = states & (1 + ~states)) != 0) { states &= ~lowbit; /* Clear the low bit. */ strcat(buff, state_name(lowbit)); if (states != 0) strcat(buff, "/"); } return buff; } /* * Check magic value and current state. * Magic value mismatches are fatal and result in calls to abort(). * State mismatches return ARCHIVE_FATAL. * Otherwise, returns ARCHIVE_OK. * * This is designed to catch serious programming errors that violate * the libarchive API. */ int __archive_check_magic(struct archive *a, unsigned int magic, unsigned int state, const char *function) { char states1[64]; char states2[64]; const char *handle_type; /* * If this isn't some form of archive handle, * then the library user has screwed up so bad that * we don't even have a reliable way to report an error. */ handle_type = archive_handle_type_name(a->magic); if (!handle_type) { errmsg("PROGRAMMER ERROR: Function "); errmsg(function); errmsg(" invoked with invalid archive handle.\n"); diediedie(); } if (a->magic != magic) { archive_set_error(a, -1, "PROGRAMMER ERROR: Function '%s' invoked" " on '%s' archive object, which is not supported.", function, handle_type); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if ((a->state & state) == 0) { /* If we're already FATAL, don't overwrite the error. */ if (a->state != ARCHIVE_STATE_FATAL) archive_set_error(a, -1, "INTERNAL ERROR: Function '%s' invoked with" " archive structure in state '%s'," " should be in state '%s'", function, write_all_states(states1, a->state), write_all_states(states2, state)); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } return ARCHIVE_OK; } Index: stable/11/contrib/libarchive/libarchive/archive_cryptor.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_cryptor.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_cryptor.c (revision 368707) @@ -1,519 +1,542 @@ /*- * Copyright (c) 2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_cryptor_private.h" /* * On systems that do not support any recognized crypto libraries, * this file will normally define no usable symbols. * * But some compilers and linkers choke on empty object files, so * define a public symbol that will always exist. This could * be removed someday if this file gains another always-present * symbol definition. */ int __libarchive_cryptor_build_hack(void) { return 0; } #ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw, pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds, derived_key, derived_key_len); return 0; } #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifdef _MSC_VER #pragma comment(lib, "Bcrypt.lib") #endif static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { NTSTATUS status; BCRYPT_ALG_HANDLE hAlg; status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (!BCRYPT_SUCCESS(status)) return -1; status = BCryptDeriveKeyPBKDF2(hAlg, (PUCHAR)(uintptr_t)pw, (ULONG)pw_len, (PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds, (PUCHAR)derived_key, (ULONG)derived_key_len, 0); BCryptCloseAlgorithmProvider(hAlg, 0); return (BCRYPT_SUCCESS(status)) ? 0: -1; } #elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H) static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { mbedtls_md_context_t ctx; const mbedtls_md_info_t *info; int ret; mbedtls_md_init(&ctx); info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); if (info == NULL) { mbedtls_md_free(&ctx); return (-1); } ret = mbedtls_md_setup(&ctx, info, 1); if (ret != 0) { mbedtls_md_free(&ctx); return (-1); } ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw, pw_len, salt, salt_len, rounds, derived_key_len, derived_key); mbedtls_md_free(&ctx); return (ret); } #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H) static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds, salt_len, salt, derived_key_len, derived_key); return 0; } #elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1) static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds, derived_key_len, derived_key); return 0; } #else /* Stub */ static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { (void)pw; /* UNUSED */ (void)pw_len; /* UNUSED */ (void)salt; /* UNUSED */ (void)salt_len; /* UNUSED */ (void)rounds; /* UNUSED */ (void)derived_key; /* UNUSED */ (void)derived_key_len; /* UNUSED */ return -1; /* UNSUPPORTED */ } #endif #ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto # if MAC_OS_X_VERSION_MAX_ALLOWED < 1090 # define kCCAlgorithmAES kCCAlgorithmAES128 # endif static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { CCCryptorStatus r; ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES, ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx); return (r == kCCSuccess)? 0: -1; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { CCCryptorRef ref = ctx->ctx; CCCryptorStatus r; r = CCCryptorReset(ref, NULL); if (r != kCCSuccess && r != kCCUnimplemented) return -1; r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf, AES_BLOCK_SIZE, NULL); return (r == kCCSuccess)? 0: -1; } static int aes_ctr_release(archive_crypto_ctx *ctx) { memset(ctx->key, 0, ctx->key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); return 0; } #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { BCRYPT_ALG_HANDLE hAlg; BCRYPT_KEY_HANDLE hKey; DWORD keyObj_len, aes_key_len; PBYTE keyObj; ULONG result; NTSTATUS status; BCRYPT_KEY_LENGTHS_STRUCT key_lengths; ctx->hAlg = NULL; ctx->hKey = NULL; ctx->keyObj = NULL; switch (key_len) { case 16: aes_key_len = 128; break; case 24: aes_key_len = 192; break; case 32: aes_key_len = 256; break; default: return -1; } status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); if (!BCRYPT_SUCCESS(status)) return -1; status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths, sizeof(key_lengths), &result, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } if (key_lengths.dwMinLength > aes_key_len || key_lengths.dwMaxLength < aes_key_len) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len, sizeof(keyObj_len), &result, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len); if (keyObj == NULL) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); HeapFree(GetProcessHeap(), 0, keyObj); return -1; } status = BCryptGenerateSymmetricKey(hAlg, &hKey, keyObj, keyObj_len, (PUCHAR)(uintptr_t)key, (ULONG)key_len, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); HeapFree(GetProcessHeap(), 0, keyObj); return -1; } ctx->hAlg = hAlg; ctx->hKey = hKey; ctx->keyObj = keyObj; ctx->keyObj_len = keyObj_len; ctx->encr_pos = AES_BLOCK_SIZE; return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { NTSTATUS status; ULONG result; status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE, NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE, &result, 0); return BCRYPT_SUCCESS(status) ? 0 : -1; } static int aes_ctr_release(archive_crypto_ctx *ctx) { if (ctx->hAlg != NULL) { BCryptCloseAlgorithmProvider(ctx->hAlg, 0); ctx->hAlg = NULL; BCryptDestroyKey(ctx->hKey); ctx->hKey = NULL; HeapFree(GetProcessHeap(), 0, ctx->keyObj); ctx->keyObj = NULL; } memset(ctx, 0, sizeof(*ctx)); return 0; } #elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { mbedtls_aes_init(&ctx->ctx); ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key, ctx->key_len * 8) != 0) return (-1); if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce, ctx->encr_buf) != 0) return (-1); return 0; } static int aes_ctr_release(archive_crypto_ctx *ctx) { mbedtls_aes_free(&ctx->ctx); memset(ctx, 0, sizeof(*ctx)); return 0; } #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; memset(&ctx->ctx, 0, sizeof(ctx->ctx)); return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { +#if NETTLE_VERSION_MAJOR < 3 aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key); aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce); +#else + switch(ctx->key_len) { + case AES128_KEY_SIZE: + aes128_set_encrypt_key(&ctx->ctx.c128, ctx->key); + aes128_encrypt(&ctx->ctx.c128, AES_BLOCK_SIZE, ctx->encr_buf, + ctx->nonce); + break; + case AES192_KEY_SIZE: + aes192_set_encrypt_key(&ctx->ctx.c192, ctx->key); + aes192_encrypt(&ctx->ctx.c192, AES_BLOCK_SIZE, ctx->encr_buf, + ctx->nonce); + break; + case AES256_KEY_SIZE: + aes256_set_encrypt_key(&ctx->ctx.c256, ctx->key); + aes256_encrypt(&ctx->ctx.c256, AES_BLOCK_SIZE, ctx->encr_buf, + ctx->nonce); + break; + default: + return -1; + break; + } +#endif return 0; } static int aes_ctr_release(archive_crypto_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); return 0; } #elif defined(HAVE_LIBCRYPTO) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) return -1; switch (key_len) { case 16: ctx->type = EVP_aes_128_ecb(); break; case 24: ctx->type = EVP_aes_192_ecb(); break; case 32: ctx->type = EVP_aes_256_ecb(); break; default: ctx->type = NULL; return -1; } ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) if (!EVP_CIPHER_CTX_reset(ctx->ctx)) { EVP_CIPHER_CTX_free(ctx->ctx); ctx->ctx = NULL; } #else EVP_CIPHER_CTX_init(ctx->ctx); #endif return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { int outl = 0; int r; r = EVP_EncryptInit_ex(ctx->ctx, ctx->type, NULL, ctx->key, NULL); if (r == 0) return -1; r = EVP_EncryptUpdate(ctx->ctx, ctx->encr_buf, &outl, ctx->nonce, AES_BLOCK_SIZE); if (r == 0 || outl != AES_BLOCK_SIZE) return -1; return 0; } static int aes_ctr_release(archive_crypto_ctx *ctx) { EVP_CIPHER_CTX_free(ctx->ctx); memset(ctx->key, 0, ctx->key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); return 0; } #else #define ARCHIVE_CRYPTOR_STUB /* Stub */ static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { (void)ctx; /* UNUSED */ (void)key; /* UNUSED */ (void)key_len; /* UNUSED */ return -1; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { (void)ctx; /* UNUSED */ return -1; } static int aes_ctr_release(archive_crypto_ctx *ctx) { (void)ctx; /* UNUSED */ return 0; } #endif #ifdef ARCHIVE_CRYPTOR_STUB static int aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in, size_t in_len, uint8_t * const out, size_t *out_len) { (void)ctx; /* UNUSED */ (void)in; /* UNUSED */ (void)in_len; /* UNUSED */ (void)out; /* UNUSED */ (void)out_len; /* UNUSED */ aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */ return -1; } #else static void aes_ctr_increase_counter(archive_crypto_ctx *ctx) { uint8_t *const nonce = ctx->nonce; int j; for (j = 0; j < 8; j++) { if (++nonce[j]) break; } } static int aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in, size_t in_len, uint8_t * const out, size_t *out_len) { uint8_t *const ebuf = ctx->encr_buf; unsigned pos = ctx->encr_pos; unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len); unsigned i; for (i = 0; i < max; ) { if (pos == AES_BLOCK_SIZE) { aes_ctr_increase_counter(ctx); if (aes_ctr_encrypt_counter(ctx) != 0) return -1; while (max -i >= AES_BLOCK_SIZE) { for (pos = 0; pos < AES_BLOCK_SIZE; pos++) out[i+pos] = in[i+pos] ^ ebuf[pos]; i += AES_BLOCK_SIZE; aes_ctr_increase_counter(ctx); if (aes_ctr_encrypt_counter(ctx) != 0) return -1; } pos = 0; if (i >= max) break; } out[i] = in[i] ^ ebuf[pos++]; i++; } ctx->encr_pos = pos; *out_len = i; return 0; } #endif /* ARCHIVE_CRYPTOR_STUB */ const struct archive_cryptor __archive_cryptor = { &pbkdf2_sha1, &aes_ctr_init, &aes_ctr_update, &aes_ctr_release, &aes_ctr_init, &aes_ctr_update, &aes_ctr_release, }; Index: stable/11/contrib/libarchive/libarchive/archive_cryptor_private.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_cryptor_private.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_cryptor_private.h (revision 368707) @@ -1,179 +1,188 @@ /*- * Copyright (c) 2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED #define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif /* * On systems that do not support any recognized crypto libraries, * the archive_cryptor.c file will normally define no usable symbols. * * But some compilers and linkers choke on empty object files, so * define a public symbol that will always exist. This could * be removed someday if this file gains another always-present * symbol definition. */ int __libarchive_cryptor_build_hack(void); #ifdef __APPLE__ # include # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 # define ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto # endif #endif #ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto #include #include #define AES_BLOCK_SIZE 16 #define AES_MAX_KEY_SIZE kCCKeySizeAES256 typedef struct { CCCryptorRef ctx; uint8_t key[AES_MAX_KEY_SIZE]; unsigned key_len; uint8_t nonce[AES_BLOCK_SIZE]; uint8_t encr_buf[AES_BLOCK_SIZE]; unsigned encr_pos; } archive_crypto_ctx; #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #include /* Common in other bcrypt implementations, but missing from VS2008. */ #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) #endif #define AES_MAX_KEY_SIZE 32 #define AES_BLOCK_SIZE 16 typedef struct { BCRYPT_ALG_HANDLE hAlg; BCRYPT_KEY_HANDLE hKey; PBYTE keyObj; DWORD keyObj_len; uint8_t nonce[AES_BLOCK_SIZE]; uint8_t encr_buf[AES_BLOCK_SIZE]; unsigned encr_pos; } archive_crypto_ctx; #elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H) #include #include #include #define AES_MAX_KEY_SIZE 32 #define AES_BLOCK_SIZE 16 typedef struct { mbedtls_aes_context ctx; uint8_t key[AES_MAX_KEY_SIZE]; unsigned key_len; uint8_t nonce[AES_BLOCK_SIZE]; uint8_t encr_buf[AES_BLOCK_SIZE]; unsigned encr_pos; } archive_crypto_ctx; #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H) #if defined(HAVE_NETTLE_PBKDF2_H) #include #endif #include +#include typedef struct { +#if NETTLE_VERSION_MAJOR < 3 struct aes_ctx ctx; +#else + union { + struct aes128_ctx c128; + struct aes192_ctx c192; + struct aes256_ctx c256; + } ctx; +#endif uint8_t key[AES_MAX_KEY_SIZE]; unsigned key_len; uint8_t nonce[AES_BLOCK_SIZE]; uint8_t encr_buf[AES_BLOCK_SIZE]; unsigned encr_pos; } archive_crypto_ctx; #elif defined(HAVE_LIBCRYPTO) #include "archive_openssl_evp_private.h" #define AES_BLOCK_SIZE 16 #define AES_MAX_KEY_SIZE 32 typedef struct { EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; uint8_t key[AES_MAX_KEY_SIZE]; unsigned key_len; uint8_t nonce[AES_BLOCK_SIZE]; uint8_t encr_buf[AES_BLOCK_SIZE]; unsigned encr_pos; } archive_crypto_ctx; #else #define AES_BLOCK_SIZE 16 #define AES_MAX_KEY_SIZE 32 typedef int archive_crypto_ctx; #endif /* defines */ #define archive_pbkdf2_sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)\ __archive_cryptor.pbkdf2sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len) #define archive_decrypto_aes_ctr_init(ctx, key, key_len) \ __archive_cryptor.decrypto_aes_ctr_init(ctx, key, key_len) #define archive_decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \ __archive_cryptor.decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) #define archive_decrypto_aes_ctr_release(ctx) \ __archive_cryptor.decrypto_aes_ctr_release(ctx) #define archive_encrypto_aes_ctr_init(ctx, key, key_len) \ __archive_cryptor.encrypto_aes_ctr_init(ctx, key, key_len) #define archive_encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \ __archive_cryptor.encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) #define archive_encrypto_aes_ctr_release(ctx) \ __archive_cryptor.encrypto_aes_ctr_release(ctx) /* Minimal interface to cryptographic functionality for internal use in * libarchive */ struct archive_cryptor { /* PKCS5 PBKDF2 HMAC-SHA1 */ int (*pbkdf2sha1)(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len); /* AES CTR mode(little endian version) */ int (*decrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t); int (*decrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *, size_t, uint8_t *, size_t *); int (*decrypto_aes_ctr_release)(archive_crypto_ctx *); int (*encrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t); int (*encrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *, size_t, uint8_t *, size_t *); int (*encrypto_aes_ctr_release)(archive_crypto_ctx *); }; extern const struct archive_cryptor __archive_cryptor; #endif Index: stable/11/contrib/libarchive/libarchive/archive_digest_private.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_digest_private.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_digest_private.h (revision 368707) @@ -1,412 +1,416 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Andres Mejia * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED #define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif +#ifndef __LIBARCHIVE_CONFIG_H_INCLUDED +#error "Should have include config.h first!" +#endif + /* * Crypto support in various Operating Systems: * * NetBSD: * - MD5 and SHA1 in libc: without _ after algorithm name * - SHA2 in libc: with _ after algorithm name * * OpenBSD: * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name * * DragonFly and FreeBSD: * - MD5 libmd: without _ after algorithm name * - SHA1, SHA256 and SHA512 in libmd: with _ after algorithm name * * Mac OS X (10.4 and later): * - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name * * OpenSSL: * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name * * Windows: * - MD5, SHA1 and SHA2 in archive_crypto.c using Windows crypto API */ /* libc crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) #include #endif #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC3) #include #endif /* libmd crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||\ defined(ARCHIVE_CRYPTO_RMD160_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) #define ARCHIVE_CRYPTO_LIBMD 1 #endif #if defined(ARCHIVE_CRYPTO_MD5_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_SHA256_LIBMD) #include #endif #if defined(ARCHIVE_CRYPTO_SHA512_LIBMD) #include #endif /* libSystem crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) #include #endif /* mbed TLS crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) #include #endif #if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) #include #endif #if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) #include #endif /* Nettle crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_NETTLE) #include #endif #if defined(ARCHIVE_CRYPTO_RMD160_NETTLE) #include #endif #if defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) #include #endif /* OpenSSL crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) #define ARCHIVE_CRYPTO_OPENSSL 1 #include "archive_openssl_evp_private.h" #endif /* Windows crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) #include #include typedef struct { int valid; HCRYPTPROV cryptProv; HCRYPTHASH hash; } Digest_CTX; #endif /* typedefs */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) typedef MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_LIBMD) typedef MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) typedef CC_MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) typedef mbedtls_md5_context archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE) typedef struct md5_ctx archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL) typedef EVP_MD_CTX *archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_WIN) typedef Digest_CTX archive_md5_ctx; #else typedef unsigned char archive_md5_ctx; #endif #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) typedef RMD160_CTX archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD) typedef RIPEMD160_CTX archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) typedef mbedtls_ripemd160_context archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE) typedef struct ripemd160_ctx archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) typedef EVP_MD_CTX *archive_rmd160_ctx; #else typedef unsigned char archive_rmd160_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) typedef SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD) typedef SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) typedef CC_SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) typedef mbedtls_sha1_context archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE) typedef struct sha1_ctx archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) typedef EVP_MD_CTX *archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_WIN) typedef Digest_CTX archive_sha1_ctx; #else typedef unsigned char archive_sha1_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2) typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3) typedef SHA2_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD) typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) typedef CC_SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) typedef mbedtls_sha256_context archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE) typedef struct sha256_ctx archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) typedef EVP_MD_CTX *archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_WIN) typedef Digest_CTX archive_sha256_ctx; #else typedef unsigned char archive_sha256_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA384_LIBC) typedef SHA384_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2) typedef SHA384_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3) typedef SHA2_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) typedef CC_SHA512_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) typedef mbedtls_sha512_context archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE) typedef struct sha384_ctx archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) typedef EVP_MD_CTX *archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_WIN) typedef Digest_CTX archive_sha384_ctx; #else typedef unsigned char archive_sha384_ctx; #endif #if defined(ARCHIVE_CRYPTO_SHA512_LIBC) typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2) typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3) typedef SHA2_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD) typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) typedef CC_SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) typedef mbedtls_sha512_context archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) typedef struct sha512_ctx archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) typedef EVP_MD_CTX *archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_WIN) typedef Digest_CTX archive_sha512_ctx; #else typedef unsigned char archive_sha512_ctx; #endif /* defines */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\ defined(ARCHIVE_CRYPTO_MD5_LIBMD) || \ defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\ defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_MD5_WIN) #define ARCHIVE_HAS_MD5 #endif #define archive_md5_init(ctx)\ __archive_digest.md5init(ctx) #define archive_md5_final(ctx, md)\ __archive_digest.md5final(ctx, md) #define archive_md5_update(ctx, buf, n)\ __archive_digest.md5update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\ defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) #define ARCHIVE_HAS_RMD160 #endif #define archive_rmd160_init(ctx)\ __archive_digest.rmd160init(ctx) #define archive_rmd160_final(ctx, md)\ __archive_digest.rmd160final(ctx, md) #define archive_rmd160_update(ctx, buf, n)\ __archive_digest.rmd160update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA1_WIN) #define ARCHIVE_HAS_SHA1 #endif #define archive_sha1_init(ctx)\ __archive_digest.sha1init(ctx) #define archive_sha1_final(ctx, md)\ __archive_digest.sha1final(ctx, md) #define archive_sha1_update(ctx, buf, n)\ __archive_digest.sha1update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA256_WIN) #define ARCHIVE_HAS_SHA256 #endif #define archive_sha256_init(ctx)\ __archive_digest.sha256init(ctx) #define archive_sha256_final(ctx, md)\ __archive_digest.sha256final(ctx, md) #define archive_sha256_update(ctx, buf, n)\ __archive_digest.sha256update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) #define ARCHIVE_HAS_SHA384 #endif #define archive_sha384_init(ctx)\ __archive_digest.sha384init(ctx) #define archive_sha384_final(ctx, md)\ __archive_digest.sha384final(ctx, md) #define archive_sha384_update(ctx, buf, n)\ __archive_digest.sha384update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) #define ARCHIVE_HAS_SHA512 #endif #define archive_sha512_init(ctx)\ __archive_digest.sha512init(ctx) #define archive_sha512_final(ctx, md)\ __archive_digest.sha512final(ctx, md) #define archive_sha512_update(ctx, buf, n)\ __archive_digest.sha512update(ctx, buf, n) /* Minimal interface to digest functionality for internal use in libarchive */ struct archive_digest { /* Message Digest */ int (*md5init)(archive_md5_ctx *ctx); int (*md5update)(archive_md5_ctx *, const void *, size_t); int (*md5final)(archive_md5_ctx *, void *); int (*rmd160init)(archive_rmd160_ctx *); int (*rmd160update)(archive_rmd160_ctx *, const void *, size_t); int (*rmd160final)(archive_rmd160_ctx *, void *); int (*sha1init)(archive_sha1_ctx *); int (*sha1update)(archive_sha1_ctx *, const void *, size_t); int (*sha1final)(archive_sha1_ctx *, void *); int (*sha256init)(archive_sha256_ctx *); int (*sha256update)(archive_sha256_ctx *, const void *, size_t); int (*sha256final)(archive_sha256_ctx *, void *); int (*sha384init)(archive_sha384_ctx *); int (*sha384update)(archive_sha384_ctx *, const void *, size_t); int (*sha384final)(archive_sha384_ctx *, void *); int (*sha512init)(archive_sha512_ctx *); int (*sha512update)(archive_sha512_ctx *, const void *, size_t); int (*sha512final)(archive_sha512_ctx *, void *); }; extern const struct archive_digest __archive_digest; #endif Index: stable/11/contrib/libarchive/libarchive/archive_entry.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_entry.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_entry.c (revision 368707) @@ -1,2066 +1,2135 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #if MAJOR_IN_MKDEV #include #define HAVE_MAJOR #elif MAJOR_IN_SYSMACROS #include #define HAVE_MAJOR #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* for Linux file flags */ #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" #include "archive_acl_private.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_entry_private.h" #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) #endif /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define ae_makedev(maj, min) makedev((maj), (min)) #endif /* * This adjustment is needed to support the following idiom for adding * 1000ns to the stored time: * archive_entry_set_atime(archive_entry_atime(), * archive_entry_atime_nsec() + 1000) * The additional if() here compensates for ambiguity in the C standard, * which permits two possible interpretations of a % b when a is negative. */ #define FIX_NS(t,ns) \ do { \ t += ns / 1000000000; \ ns %= 1000000000; \ if (ns < 0) { --t; ns += 1000000000; } \ } while (0) static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p != L'\0') ++p; return p - s; } #endif #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); archive_mstring_clean(&entry->ae_fflags_text); archive_mstring_clean(&entry->ae_gname); archive_mstring_clean(&entry->ae_hardlink); archive_mstring_clean(&entry->ae_pathname); archive_mstring_clean(&entry->ae_sourcepath); archive_mstring_clean(&entry->ae_symlink); archive_mstring_clean(&entry->ae_uname); archive_entry_copy_mac_metadata(entry, NULL, 0); archive_acl_clear(&entry->acl); archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; memset(entry, 0, sizeof(*entry)); return entry; } struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; struct ae_xattr *xp; struct ae_sparse *sp; size_t s; const void *p; /* Allocate new structure and copy over all of the fields. */ /* TODO: Should we copy the archive over? Or require a new archive * as an argument? */ entry2 = archive_entry_new2(entry->archive); if (entry2 == NULL) return (NULL); entry2->ae_stat = entry->ae_stat; entry2->ae_fflags_set = entry->ae_fflags_set; entry2->ae_fflags_clear = entry->ae_fflags_clear; /* TODO: XXX If clone can have a different archive, what do we do here if * character sets are different? XXX */ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname); archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink); archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname); archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy symlink type */ entry2->ae_symlink_type = entry->ae_symlink_type; /* Copy encryption status */ entry2->encryption = entry->encryption; + + /* Copy digests */ +#define copy_digest(_e2, _e, _t) \ + memcpy(_e2->digest._t, _e->digest._t, sizeof(_e2->digest._t)) + + copy_digest(entry2, entry, md5); + copy_digest(entry2, entry, rmd160); + copy_digest(entry2, entry, sha1); + copy_digest(entry2, entry, sha256); + copy_digest(entry2, entry, sha384); + copy_digest(entry2, entry, sha512); + +#undef copy_digest /* Copy ACL data over. */ archive_acl_copy(&entry2->acl, &entry->acl); /* Copy Mac OS metadata. */ p = archive_entry_mac_metadata(entry, &s); archive_entry_copy_mac_metadata(entry2, p, s); /* Copy xattr data over. */ xp = entry->xattr_head; while (xp != NULL) { archive_entry_xattr_add_entry(entry2, xp->name, xp->value, xp->size); xp = xp->next; } /* Copy sparse data over. */ sp = entry->sparse_head; while (sp != NULL) { archive_entry_sparse_add_entry(entry2, sp->offset, sp->length); sp = sp->next; } return (entry2); } void archive_entry_free(struct archive_entry *entry) { archive_entry_clear(entry); free(entry); } struct archive_entry * archive_entry_new(void) { return archive_entry_new2(NULL); } struct archive_entry * archive_entry_new2(struct archive *a) { struct archive_entry *entry; entry = (struct archive_entry *)calloc(1, sizeof(*entry)); if (entry == NULL) return (NULL); entry->archive = a; entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; return (entry); } /* * Functions for reading fields from an archive_entry. */ time_t archive_entry_atime(struct archive_entry *entry) { return (entry->ae_stat.aest_atime); } long archive_entry_atime_nsec(struct archive_entry *entry) { 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_birthtime(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime); } long archive_entry_birthtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime_nsec); } int archive_entry_birthtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_BIRTHTIME); } 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) { return (entry->ae_stat.aest_ctime_nsec); } dev_t archive_entry_dev(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return ae_makedev(entry->ae_stat.aest_devmajor, entry->ae_stat.aest_devminor); else return (entry->ae_stat.aest_dev); } int archive_entry_dev_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_DEV); } dev_t archive_entry_devmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devmajor); else return major(entry->ae_stat.aest_dev); } dev_t archive_entry_devminor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devminor); else return minor(entry->ae_stat.aest_dev); } __LA_MODE_T archive_entry_filetype(struct archive_entry *entry) { return (AE_IFMT & entry->acl.mode); } void archive_entry_fflags(struct archive_entry *entry, unsigned long *set, unsigned long *clear) { *set = entry->ae_fflags_set; *clear = entry->ae_fflags_clear; } /* * Note: if text was provided, this just returns that text. If you * really need the text to be rebuilt in a canonical form, set the * text, ask for the bitmaps, then set the bitmaps. (Setting the * bitmaps clears any stored text.) This design is deliberate: if * we're editing archives, we don't want to discard flags just because * they aren't supported on the current system. The bitmap<->text * conversions are platform-specific (see below). */ const char * archive_entry_fflags_text(struct archive_entry *entry) { const char *f; char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) { if (f != NULL) return (f); } else if (errno == ENOMEM) __archive_errx(1, "No memory"); if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) return (NULL); p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); if (p == NULL) return (NULL); archive_mstring_copy_mbs(&entry->ae_fflags_text, p); free(p); if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) return (f); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } la_int64_t archive_entry_gid(struct archive_entry *entry) { return (entry->ae_stat.aest_gid); } const char * archive_entry_gname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_gname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_gname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { - return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_gname, p, len, sc)); } const char * archive_entry_hardlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_hardlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_hardlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_HARDLINK) == 0) { *p = NULL; *len = 0; return (0); } - return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_hardlink, p, len, sc)); } la_int64_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } int archive_entry_ino_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_INO); } la_int64_t archive_entry_ino64(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } __LA_MODE_T archive_entry_mode(struct archive_entry *entry) { return (entry->acl.mode); } time_t archive_entry_mtime(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime); } long archive_entry_mtime_nsec(struct archive_entry *entry) { 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) { return (entry->ae_stat.aest_nlink); } const char * archive_entry_pathname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_pathname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_pathname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { - return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_pathname, p, len, sc)); } __LA_MODE_T archive_entry_perm(struct archive_entry *entry) { return (~AE_IFMT & entry->acl.mode); } dev_t archive_entry_rdev(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return ae_makedev(entry->ae_stat.aest_rdevmajor, entry->ae_stat.aest_rdevminor); else return (entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevmajor); else return major(entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevminor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevminor); else return minor(entry->ae_stat.aest_rdev); } la_int64_t archive_entry_size(struct archive_entry *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) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_sourcepath_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); return (NULL); } const char * archive_entry_symlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int archive_entry_symlink_type(struct archive_entry *entry) { return (entry->ae_symlink_type); } const char * archive_entry_symlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_symlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_SYMLINK) == 0) { *p = NULL; *len = 0; return (0); } - return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_symlink, p, len, sc)); } la_int64_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_uid); } const char * archive_entry_uname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_uname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_uname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { - return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_uname, p, len, sc)); } int archive_entry_is_data_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA); } int archive_entry_is_metadata_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA); } int archive_entry_is_encrypted(struct archive_entry *entry) { return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA)); } /* * Functions to set archive_entry properties. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->acl.mode &= ~AE_IFMT; entry->acl.mode |= AE_IFMT & type; } void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { archive_mstring_clean(&entry->ae_fflags_text); entry->ae_fflags_set = set; entry->ae_fflags_clear = clear; } const char * archive_entry_copy_fflags_text(struct archive_entry *entry, const char *flags) { archive_mstring_copy_mbs(&entry->ae_fflags_text, flags); return (ae_strtofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) { archive_mstring_copy_wcs(&entry->ae_fflags_text, flags); return (ae_wcstofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } void archive_entry_set_gid(struct archive_entry *entry, la_int64_t g) { entry->stat_valid = 0; entry->ae_stat.aest_gid = g; } void archive_entry_set_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_gname, name); } void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_gname, name); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_gname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_gname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc)); } void archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target) { archive_mstring_copy_utf8(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { archive_mstring_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } int archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target) { if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_hardlink_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); if (target != NULL && r == 0) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; return (r); } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, 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_birthtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = t; entry->ae_stat.aest_birthtime_nsec = ns; } void archive_entry_unset_birthtime(struct archive_entry *entry) { archive_entry_set_birthtime(entry, 0, 0); entry->ae_set &= ~AE_SET_BIRTHTIME; } void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, 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; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 0; entry->ae_stat.aest_dev = d; } void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devmajor = m; } void archive_entry_set_devminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devminor = m; } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_set_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } void archive_entry_set_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_utf8(&entry->ae_symlink, target); else archive_mstring_copy_utf8(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_wcs(&entry->ae_symlink, target); else archive_mstring_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, target); else r = archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target); if (r == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_link_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, target, len, sc); else r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); return (r); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->acl.mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = t; 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; entry->ae_stat.aest_nlink = nlink; } void archive_entry_set_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_pathname, name); } void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_pathname, name); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_pathname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_pathname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname, name, len, sc)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; entry->acl.mode |= ~AE_IFMT & p; } void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; } void archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevmajor = m; } void archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevminor = m; } void archive_entry_set_size(struct archive_entry *entry, la_int64_t s) { 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 archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) { archive_mstring_copy_mbs(&entry->ae_sourcepath, path); } void archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path) { archive_mstring_copy_wcs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_set_symlink_type(struct archive_entry *entry, int type) { entry->ae_symlink_type = type; } void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_utf8(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { archive_mstring_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } int archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname) { if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, linkname) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_symlink_l(struct archive_entry *entry, const char *linkname, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, linkname, len, sc); if (linkname != NULL && r == 0) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; return (r); } void archive_entry_set_uid(struct archive_entry *entry, la_int64_t u) { entry->stat_valid = 0; entry->ae_stat.aest_uid = u; } void archive_entry_set_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_uname, name); } void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_uname, name); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_uname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } void archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_DATA; } else { entry->encryption &= ~AE_ENCRYPTION_DATA; } } void archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_METADATA; } else { entry->encryption &= ~AE_ENCRYPTION_METADATA; } } int _archive_entry_copy_uname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_uname, name, len, sc)); } const void * archive_entry_mac_metadata(struct archive_entry *entry, size_t *s) { *s = entry->mac_metadata_size; return entry->mac_metadata; } void archive_entry_copy_mac_metadata(struct archive_entry *entry, const void *p, size_t s) { free(entry->mac_metadata); if (p == NULL || s == 0) { entry->mac_metadata = NULL; entry->mac_metadata_size = 0; } else { entry->mac_metadata_size = s; entry->mac_metadata = malloc(s); if (entry->mac_metadata == NULL) abort(); memcpy(entry->mac_metadata, p, s); } +} + +/* Digest handling */ +const unsigned char * +archive_entry_digest(struct archive_entry *entry, int type) +{ + switch (type) { + case ARCHIVE_ENTRY_DIGEST_MD5: + return entry->digest.md5; + case ARCHIVE_ENTRY_DIGEST_RMD160: + return entry->digest.rmd160; + case ARCHIVE_ENTRY_DIGEST_SHA1: + return entry->digest.sha1; + case ARCHIVE_ENTRY_DIGEST_SHA256: + return entry->digest.sha256; + case ARCHIVE_ENTRY_DIGEST_SHA384: + return entry->digest.sha384; + case ARCHIVE_ENTRY_DIGEST_SHA512: + return entry->digest.sha512; + default: + return NULL; + } +} + +int +archive_entry_set_digest(struct archive_entry *entry, int type, + const unsigned char *digest) +{ +#define copy_digest(_e, _t, _d)\ + memcpy(_e->digest._t, _d, sizeof(_e->digest._t)) + + switch (type) { + case ARCHIVE_ENTRY_DIGEST_MD5: + copy_digest(entry, md5, digest); + break; + case ARCHIVE_ENTRY_DIGEST_RMD160: + copy_digest(entry, rmd160, digest); + break; + case ARCHIVE_ENTRY_DIGEST_SHA1: + copy_digest(entry, sha1, digest); + break; + case ARCHIVE_ENTRY_DIGEST_SHA256: + copy_digest(entry, sha256, digest); + break; + case ARCHIVE_ENTRY_DIGEST_SHA384: + copy_digest(entry, sha384, digest); + break; + case ARCHIVE_ENTRY_DIGEST_SHA512: + copy_digest(entry, sha512, digest); + break; + default: + return ARCHIVE_WARN; + } + + return ARCHIVE_OK; +#undef copy_digest } /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and * complete standard that addressed the needs of ACL archiving and 2) * everyone followed it faithfully. Alas, neither is true, so the * following is a lot more complex than might seem necessary to the * uninitiated. */ struct archive_acl * archive_entry_acl(struct archive_entry *entry) { return &entry->acl; } void archive_entry_acl_clear(struct archive_entry *entry) { archive_acl_clear(&entry->acl); } /* * Add a single ACL entry to the internal list of ACL data. */ int archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name); } /* * As above, but with a wide-character name. */ int archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { return archive_acl_add_entry_w_len(&entry->acl, type, permset, tag, id, name, wcslen(name)); } /* * Return a bitmask of ACL types in an archive entry ACL list */ int archive_entry_acl_types(struct archive_entry *entry) { return (archive_acl_types(&entry->acl)); } /* * Return a count of entries matching "want_type". */ int archive_entry_acl_count(struct archive_entry *entry, int want_type) { return archive_acl_count(&entry->acl, want_type); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_entry_acl_reset(struct archive_entry *entry, int want_type) { return archive_acl_reset(&entry->acl, want_type); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { int r; r = archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name); if (r == ARCHIVE_FATAL && errno == ENOMEM) __archive_errx(1, "No memory"); return (r); } /* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ wchar_t * archive_entry_acl_to_text_w(struct archive_entry *entry, la_ssize_t *len, int flags) { return (archive_acl_to_text_w(&entry->acl, len, flags, entry->archive)); } char * archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len, int flags) { return (archive_acl_to_text_l(&entry->acl, len, flags, NULL)); } char * _archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len, int flags, struct archive_string_conv *sc) { return (archive_acl_to_text_l(&entry->acl, len, flags, sc)); } /* * ACL text parser. */ int archive_entry_acl_from_text_w(struct archive_entry *entry, const wchar_t *wtext, int type) { return (archive_acl_from_text_w(&entry->acl, wtext, type)); } int archive_entry_acl_from_text(struct archive_entry *entry, const char *text, int type) { return (archive_acl_from_text_l(&entry->acl, text, type, NULL)); } int _archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text, int type, struct archive_string_conv *sc) { return (archive_acl_from_text_l(&entry->acl, text, type, sc)); } /* Deprecated */ static int archive_entry_acl_text_compat(int *flags) { if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0) return (1); /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) *flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID; /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) *flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; *flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA; return (0); } /* Deprecated */ const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { free(entry->acl.acl_text_w); entry->acl.acl_text_w = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl, NULL, flags, entry->archive); return (entry->acl.acl_text_w); } /* Deprecated */ const char * archive_entry_acl_text(struct archive_entry *entry, int flags) { free(entry->acl.acl_text); entry->acl.acl_text = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL, flags, NULL); return (entry->acl.acl_text); } /* Deprecated */ int _archive_entry_acl_text_l(struct archive_entry *entry, int flags, const char **acl_text, size_t *len, struct archive_string_conv *sc) { free(entry->acl.acl_text); entry->acl.acl_text = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, (ssize_t *)len, flags, sc); *acl_text = entry->acl.acl_text; return (0); } /* * Following code is modified from UC Berkeley sources, and * is subject to the following copyright notice. */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Supported file flags on FreeBSD and Mac OS: * sappnd,sappend SF_APPEND * arch,archived SF_ARCHIVED * schg,schange,simmutable SF_IMMUTABLE * sunlnk,sunlink SF_NOUNLINK (FreeBSD only) * uappnd,uappend UF_APPEND * compressed UF_COMPRESSED (Mac OS only) * hidden,uhidden UF_HIDDEN * uchg,uchange,uimmutable UF_IMMUTABLE * nodump UF_NODUMP * uunlnk,uunlink UF_NOUNLINK (FreeBSD only) * offline,uoffline UF_OFFLINE (FreeBSD only) * opaque UF_OPAQUE * rdonly,urdonly,readonly UF_READONLY (FreeBSD only) * reparse,ureparse UF_REPARSE (FreeBSD only) * sparse,usparse UF_SPARSE (FreeBSD only) * system,usystem UF_SYSTEM (FreeBSD only) * * See chflags(2) for more information * * Supported file attributes on Linux: * a append only FS_APPEND_FL sappnd * A no atime updates FS_NOATIME_FL atime * c compress FS_COMPR_FL compress * C no copy on write FS_NOCOW_FL cow * d no dump FS_NODUMP_FL dump * D synchronous directory updates FS_DIRSYNC_FL dirsync * i immutable FS_IMMUTABLE_FL schg * j data journalling FS_JOURNAL_DATA_FL journal * P project hierarchy FS_PROJINHERIT_FL projinherit * s secure deletion FS_SECRM_FL securedeletion * S synchronous updates FS_SYNC_FL sync * t no tail-merging FS_NOTAIL_FL tail * T top of directory hierarchy FS_TOPDIR_FL topdir * u undeletable FS_UNRM_FL undel * * See ioctl_iflags(2) for more information * * Equivalent file flags supported on FreeBSD / Mac OS and Linux: * SF_APPEND FS_APPEND_FL sappnd * SF_IMMUTABLE FS_IMMUTABLE_FL schg * UF_NODUMP FS_NODUMP_FL nodump */ static const struct flag { const char *name; const wchar_t *wname; unsigned long set; unsigned long clear; } fileflags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND { "nosappnd", L"nosappnd", SF_APPEND, 0}, { "nosappend", L"nosappend", SF_APPEND, 0}, #endif #if defined(FS_APPEND_FL) /* 'a' */ { "nosappnd", L"nosappnd", FS_APPEND_FL, 0}, { "nosappend", L"nosappend", FS_APPEND_FL, 0}, #elif defined(EXT2_APPEND_FL) /* 'a' */ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0}, { "nosappend", L"nosappend", EXT2_APPEND_FL, 0}, #endif #ifdef SF_ARCHIVED { "noarch", L"noarch", SF_ARCHIVED, 0}, { "noarchived", L"noarchived", SF_ARCHIVED, 0}, #endif #ifdef SF_IMMUTABLE { "noschg", L"noschg", SF_IMMUTABLE, 0}, { "noschange", L"noschange", SF_IMMUTABLE, 0}, { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0}, #endif #if defined(FS_IMMUTABLE_FL) /* 'i' */ { "noschg", L"noschg", FS_IMMUTABLE_FL, 0}, { "noschange", L"noschange", FS_IMMUTABLE_FL, 0}, { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0}, #elif defined(EXT2_IMMUTABLE_FL) /* 'i' */ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0}, { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0}, { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0}, #endif #ifdef SF_NOUNLINK { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0}, { "nosunlink", L"nosunlink", SF_NOUNLINK, 0}, #endif #ifdef UF_APPEND { "nouappnd", L"nouappnd", UF_APPEND, 0}, { "nouappend", L"nouappend", UF_APPEND, 0}, #endif #ifdef UF_IMMUTABLE { "nouchg", L"nouchg", UF_IMMUTABLE, 0}, { "nouchange", L"nouchange", UF_IMMUTABLE, 0}, { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0}, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #if defined(FS_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, FS_NODUMP_FL}, #elif defined(EXT2_NODUMP_FL) { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE { "noopaque", L"noopaque", UF_OPAQUE, 0}, #endif #ifdef UF_NOUNLINK { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0}, { "nouunlink", L"nouunlink", UF_NOUNLINK, 0}, #endif #ifdef UF_COMPRESSED /* Mac OS */ { "nocompressed", L"nocompressed", UF_COMPRESSED, 0}, #endif #ifdef UF_HIDDEN { "nohidden", L"nohidden", UF_HIDDEN, 0}, { "nouhidden", L"nouhidden", UF_HIDDEN, 0}, #endif #ifdef FILE_ATTRIBUTE_HIDDEN { "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0}, { "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0}, #endif #ifdef UF_OFFLINE { "nooffline", L"nooffline", UF_OFFLINE, 0}, { "nouoffline", L"nouoffline", UF_OFFLINE, 0}, #endif #ifdef UF_READONLY { "nordonly", L"nordonly", UF_READONLY, 0}, { "nourdonly", L"nourdonly", UF_READONLY, 0}, { "noreadonly", L"noreadonly", UF_READONLY, 0}, #endif #ifdef FILE_ATTRIBUTE_READONLY { "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0}, { "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0}, { "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0}, #endif #ifdef UF_SPARSE { "nosparse", L"nosparse", UF_SPARSE, 0}, { "nousparse", L"nousparse", UF_SPARSE, 0}, #endif #ifdef UF_REPARSE { "noreparse", L"noreparse", UF_REPARSE, 0}, { "noureparse", L"noureparse", UF_REPARSE, 0}, #endif #ifdef UF_SYSTEM { "nosystem", L"nosystem", UF_SYSTEM, 0}, { "nousystem", L"nousystem", UF_SYSTEM, 0}, #endif #ifdef FILE_ATTRIBUTE_SYSTEM { "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0}, { "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0}, #endif #if defined(FS_UNRM_FL) /* 'u' */ { "noundel", L"noundel", FS_UNRM_FL, 0}, #elif defined(EXT2_UNRM_FL) { "noundel", L"noundel", EXT2_UNRM_FL, 0}, #endif #if defined(FS_COMPR_FL) /* 'c' */ { "nocompress", L"nocompress", FS_COMPR_FL, 0}, #elif defined(EXT2_COMPR_FL) { "nocompress", L"nocompress", EXT2_COMPR_FL, 0}, #endif #if defined(FS_NOATIME_FL) /* 'A' */ { "noatime", L"noatime", 0, FS_NOATIME_FL}, #elif defined(EXT2_NOATIME_FL) { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif #if defined(FS_DIRSYNC_FL) /* 'D' */ { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, #elif defined(EXT2_DIRSYNC_FL) { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, #endif #if defined(FS_JOURNAL_DATA_FL) /* 'j' */ { "nojournal-data",L"nojournal-data", FS_JOURNAL_DATA_FL, 0}, { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, #elif defined(EXT3_JOURNAL_DATA_FL) { "nojournal-data",L"nojournal-data", EXT3_JOURNAL_DATA_FL, 0}, { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, #endif #if defined(FS_SECRM_FL) /* 's' */ { "nosecdel", L"nosecdel", FS_SECRM_FL, 0}, { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, #elif defined(EXT2_SECRM_FL) { "nosecdel", L"nosecdel", EXT2_SECRM_FL, 0}, { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, #endif #if defined(FS_SYNC_FL) /* 'S' */ { "nosync", L"nosync", FS_SYNC_FL, 0}, #elif defined(EXT2_SYNC_FL) { "nosync", L"nosync", EXT2_SYNC_FL, 0}, #endif #if defined(FS_NOTAIL_FL) /* 't' */ { "notail", L"notail", 0, FS_NOTAIL_FL}, #elif defined(EXT2_NOTAIL_FL) { "notail", L"notail", 0, EXT2_NOTAIL_FL}, #endif #if defined(FS_TOPDIR_FL) /* 'T' */ { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, #elif defined(EXT2_TOPDIR_FL) { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, #endif #ifdef FS_NOCOW_FL /* 'C' */ { "nocow", L"nocow", 0, FS_NOCOW_FL}, #endif #ifdef FS_PROJINHERIT_FL /* 'P' */ { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, #endif { NULL, NULL, 0, 0} }; /* * fflagstostr -- * Convert file flags to a comma-separated string. If no flags * are set, return the empty string. */ static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; const char *sp; unsigned long bits; const struct flag *flag; size_t length; bits = bitset | bitclear; length = 0; for (flag = fileflags; flag->name != NULL; flag++) if (bits & (flag->set | flag->clear)) { length += strlen(flag->name) + 1; bits &= ~(flag->set | flag->clear); } if (length == 0) return (NULL); string = (char *)malloc(length); if (string == NULL) return (NULL); dp = string; for (flag = fileflags; flag->name != NULL; flag++) { if (bitset & flag->set || bitclear & flag->clear) { sp = flag->name + 2; } else if (bitset & flag->clear || bitclear & flag->set) { sp = flag->name; } else continue; bitset &= ~(flag->set | flag->clear); bitclear &= ~(flag->set | flag->clear); if (dp > string) *dp++ = ','; while ((*dp++ = *sp++) != '\0') ; dp--; } *dp = '\0'; return (string); } /* * strtofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const char * ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) { const char *start, *end; const struct flag *flag; unsigned long set, clear; const char *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { size_t length; /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; length = end - start; for (flag = fileflags; flag->name != NULL; flag++) { size_t flag_length = strlen(flag->name); if (length == flag_length && memcmp(start, flag->name, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && memcmp(start, flag->name + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->name == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == '\t' || *start == ' ' || *start == ',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } /* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const wchar_t * ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) { const wchar_t *start, *end; const struct flag *flag; unsigned long set, clear; const wchar_t *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == L'\t' || *start == L' ' || *start == L',') start++; while (*start != L'\0') { size_t length; /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; length = end - start; for (flag = fileflags; flag->wname != NULL; flag++) { size_t flag_length = wcslen(flag->wname); if (length == flag_length && wmemcmp(start, flag->wname, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && wmemcmp(start, flag->wname + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->wname == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == L'\t' || *start == L' ' || *start == L',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } #ifdef TEST #include int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); } #endif Index: stable/11/contrib/libarchive/libarchive/archive_entry.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_entry.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_entry.h (revision 368707) @@ -1,708 +1,721 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3004003 +#define ARCHIVE_VERSION_NUMBER 3005000 /* * Note: archive_entry.h is for use outside of libarchive; the * configuration headers (config.h, archive_platform.h, etc.) are * purely internal. Do NOT use HAVE_XXX configuration macros to * control the behavior of this header! If you must conditionalize, * use predefined compiler and/or platform macros. */ #include #include /* for wchar_t */ #include #include #if defined(_WIN32) && !defined(__CYGWIN__) #include #endif /* Get a suitable 64-bit integer type. */ #if !defined(__LA_INT64_T_DEFINED) # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_INT64_T la_int64_t # endif #define __LA_INT64_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) typedef __int64 la_int64_t; # else #include # if defined(_SCO_DS) || defined(__osf__) typedef long long la_int64_t; # else typedef int64_t la_int64_t; # endif # endif #endif /* The la_ssize_t should match the type used in 'struct stat' */ #if !defined(__LA_SSIZE_T_DEFINED) /* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ # if ARCHIVE_VERSION_NUMBER < 4000000 #define __LA_SSIZE_T la_ssize_t # endif #define __LA_SSIZE_T_DEFINED # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) typedef ssize_t la_ssize_t; # elif defined(_WIN64) typedef __int64 la_ssize_t; # else typedef long la_ssize_t; # endif # else # include /* ssize_t */ typedef ssize_t la_ssize_t; # endif #endif /* Get a suitable definition for mode_t */ #if ARCHIVE_VERSION_NUMBER >= 3999000 /* Switch to plain 'int' for libarchive 4.0. It's less broken than 'mode_t' */ # define __LA_MODE_T int #elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__) # define __LA_MODE_T unsigned short #else # define __LA_MODE_T mode_t #endif /* Large file support for Android */ #ifdef __ANDROID__ #include "android_lf.h" #endif /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ #if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern # else # define __LA_DECL __declspec(dllexport) # endif # else # ifdef __GNUC__ # define __LA_DECL # else # define __LA_DECL __declspec(dllimport) # endif # endif #else /* Static libraries on all platforms and shared libraries on non-Windows. */ # define __LA_DECL #endif #if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 # define __LA_DEPRECATED __attribute__((deprecated)) #else # define __LA_DEPRECATED #endif #ifdef __cplusplus extern "C" { #endif /* * Description of an archive entry. * * You can think of this as "struct stat" with some text fields added in. * * TODO: Add "comment", "charset", and possibly other entries that are * supported by "pax interchange" format. However, GNU, ustar, cpio, * and other variants don't support these features, so they're not an * excruciatingly high priority right now. * * TODO: "pax interchange" format allows essentially arbitrary * key/value attributes to be attached to any entry. Supporting * such extensions may make this library useful for special * applications (e.g., a package manager could attach special * package-management attributes to each entry). */ struct archive; struct archive_entry; /* * File-type constants. These are returned from archive_entry_filetype() * and passed to archive_entry_set_filetype(). * * These values match S_XXX defines on every platform I've checked, * including Windows, AIX, Linux, Solaris, and BSD. They're * (re)defined here because platforms generally don't define the ones * they don't support. For example, Windows doesn't define S_IFLNK or * S_IFBLK. Instead of having a mass of conditional logic and system * checks to define any S_XXX values that aren't supported locally, * I've just defined a new set of such constants so that * libarchive-based applications can manipulate and identify archive * entries properly even if the hosting platform can't store them on * disk. * * These values are also used directly within some portable formats, * such as cpio. If you find a platform that varies from these, the * correct solution is to leave these alone and translate from these * portable values to platform-native values when entries are read from * or written to disk. */ /* * In libarchive 4.0, we can drop the casts here. * They're needed to work around Borland C's broken mode_t. */ #define AE_IFMT ((__LA_MODE_T)0170000) #define AE_IFREG ((__LA_MODE_T)0100000) #define AE_IFLNK ((__LA_MODE_T)0120000) #define AE_IFSOCK ((__LA_MODE_T)0140000) #define AE_IFCHR ((__LA_MODE_T)0020000) #define AE_IFBLK ((__LA_MODE_T)0060000) #define AE_IFDIR ((__LA_MODE_T)0040000) #define AE_IFIFO ((__LA_MODE_T)0010000) /* * Symlink types */ #define AE_SYMLINK_TYPE_UNDEFINED 0 #define AE_SYMLINK_TYPE_FILE 1 #define AE_SYMLINK_TYPE_DIRECTORY 2 /* * Basic object manipulation */ __LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *); /* The 'clone' function does a deep copy; all of the strings are copied too. */ __LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *); __LA_DECL void archive_entry_free(struct archive_entry *); __LA_DECL struct archive_entry *archive_entry_new(void); /* * This form of archive_entry_new2() will pull character-set * conversion information from the specified archive handle. The * older archive_entry_new(void) form is equivalent to calling * archive_entry_new2(NULL) and will result in the use of an internal * default character-set conversion. */ __LA_DECL struct archive_entry *archive_entry_new2(struct 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_birthtime(struct archive_entry *); __LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_birthtime_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 int archive_entry_dev_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_devminor(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); __LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); __LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); __LA_DECL la_int64_t archive_entry_gid(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); __LA_DECL const char *archive_entry_gname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *); __LA_DECL int archive_entry_ino_is_set(struct archive_entry *); __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 char *archive_entry_pathname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_perm(struct archive_entry *); __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); __LA_DECL const char *archive_entry_sourcepath(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_sourcepath_w(struct archive_entry *); __LA_DECL la_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 char *archive_entry_symlink_utf8(struct archive_entry *); __LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); __LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); __LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *); __LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *); __LA_DECL int archive_entry_is_encrypted(struct archive_entry *); /* * Set fields in an archive_entry. * * Note: Before libarchive 2.4, there were 'set' and 'copy' versions * of the string setters. 'copy' copied the actual string, 'set' just * stored the pointer. In libarchive 2.4 and later, strings are * always copied. */ __LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_atime(struct archive_entry *); #if defined(_WIN32) && !defined(__CYGWIN__) __LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *); #endif __LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_birthtime(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); __LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_fflags(struct archive_entry *, unsigned long /* set */, unsigned long /* clear */); /* Returns pointer to start of first invalid token, or NULL if none. */ /* Note that all recognized tokens are processed, regardless. */ __LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *, const char *); __LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_gid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_gname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_hardlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *); __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_set_pathname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t); __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 *, la_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_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int); __LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_uid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_uname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted); __LA_DECL void archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted); /* * Routines to bulk copy fields to/from a platform-native "struct * stat." Libarchive used to just store a struct stat inside of each * archive_entry object, but this created issues when trying to * manipulate archives on systems different than the ones they were * created on. * * TODO: On Linux and other LFS systems, provide both stat32 and * stat64 versions of these functions and all of the macro glue so * that archive_entry_stat is magically defined to * archive_entry_stat32 or archive_entry_stat64 as appropriate. */ __LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); /* * Storage for Mac OS-specific AppleDouble metadata information. * Apple-format tar files store a separate binary blob containing * encoded metadata with ACL, extended attributes, etc. * This provides a place to store that blob. */ __LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *); __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t); + +/* + * Digest routine. This is used to query the raw hex digest for the + * given entry. The type of digest is provided as an argument. + */ +#define ARCHIVE_ENTRY_DIGEST_MD5 0x00000001 +#define ARCHIVE_ENTRY_DIGEST_RMD160 0x00000002 +#define ARCHIVE_ENTRY_DIGEST_SHA1 0x00000003 +#define ARCHIVE_ENTRY_DIGEST_SHA256 0x00000004 +#define ARCHIVE_ENTRY_DIGEST_SHA384 0x00000005 +#define ARCHIVE_ENTRY_DIGEST_SHA512 0x00000006 + +__LA_DECL const unsigned char * archive_entry_digest(struct archive_entry *, int /* type */); /* * ACL routines. This used to simply store and return text-format ACL * strings, but that proved insufficient for a number of reasons: * = clients need control over uname/uid and gname/gid mappings * = there are many different ACL text formats * = would like to be able to read/convert archives containing ACLs * on platforms that lack ACL libraries * * This last point, in particular, forces me to implement a reasonably * complete set of ACL support routines. */ /* * Permission bits. */ #define ARCHIVE_ENTRY_ACL_EXECUTE 0x00000001 #define ARCHIVE_ENTRY_ACL_WRITE 0x00000002 #define ARCHIVE_ENTRY_ACL_READ 0x00000004 #define ARCHIVE_ENTRY_ACL_READ_DATA 0x00000008 #define ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 0x00000008 #define ARCHIVE_ENTRY_ACL_WRITE_DATA 0x00000010 #define ARCHIVE_ENTRY_ACL_ADD_FILE 0x00000010 #define ARCHIVE_ENTRY_ACL_APPEND_DATA 0x00000020 #define ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY 0x00000020 #define ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS 0x00000040 #define ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS 0x00000080 #define ARCHIVE_ENTRY_ACL_DELETE_CHILD 0x00000100 #define ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES 0x00000200 #define ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES 0x00000400 #define ARCHIVE_ENTRY_ACL_DELETE 0x00000800 #define ARCHIVE_ENTRY_ACL_READ_ACL 0x00001000 #define ARCHIVE_ENTRY_ACL_WRITE_ACL 0x00002000 #define ARCHIVE_ENTRY_ACL_WRITE_OWNER 0x00004000 #define ARCHIVE_ENTRY_ACL_SYNCHRONIZE 0x00008000 #define ARCHIVE_ENTRY_ACL_PERMS_POSIX1E \ (ARCHIVE_ENTRY_ACL_EXECUTE \ | ARCHIVE_ENTRY_ACL_WRITE \ | ARCHIVE_ENTRY_ACL_READ) #define ARCHIVE_ENTRY_ACL_PERMS_NFS4 \ (ARCHIVE_ENTRY_ACL_EXECUTE \ | ARCHIVE_ENTRY_ACL_READ_DATA \ | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY \ | ARCHIVE_ENTRY_ACL_WRITE_DATA \ | ARCHIVE_ENTRY_ACL_ADD_FILE \ | ARCHIVE_ENTRY_ACL_APPEND_DATA \ | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY \ | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS \ | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS \ | ARCHIVE_ENTRY_ACL_DELETE_CHILD \ | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES \ | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES \ | ARCHIVE_ENTRY_ACL_DELETE \ | ARCHIVE_ENTRY_ACL_READ_ACL \ | ARCHIVE_ENTRY_ACL_WRITE_ACL \ | ARCHIVE_ENTRY_ACL_WRITE_OWNER \ | ARCHIVE_ENTRY_ACL_SYNCHRONIZE) /* * Inheritance values (NFS4 ACLs only); included in permset. */ #define ARCHIVE_ENTRY_ACL_ENTRY_INHERITED 0x01000000 #define ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT 0x02000000 #define ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT 0x04000000 #define ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT 0x08000000 #define ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY 0x10000000 #define ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS 0x20000000 #define ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS 0x40000000 #define ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4 \ (ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT \ | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT \ | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT \ | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY \ | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS \ | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS \ | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) /* We need to be able to specify combinations of these. */ #define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 0x00000100 /* POSIX.1e only */ #define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 0x00000200 /* POSIX.1e only */ #define ARCHIVE_ENTRY_ACL_TYPE_ALLOW 0x00000400 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_DENY 0x00000800 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_AUDIT 0x00001000 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_ALARM 0x00002000 /* NFS4 only */ #define ARCHIVE_ENTRY_ACL_TYPE_POSIX1E (ARCHIVE_ENTRY_ACL_TYPE_ACCESS \ | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) #define ARCHIVE_ENTRY_ACL_TYPE_NFS4 (ARCHIVE_ENTRY_ACL_TYPE_ALLOW \ | ARCHIVE_ENTRY_ACL_TYPE_DENY \ | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \ | ARCHIVE_ENTRY_ACL_TYPE_ALARM) /* Tag values mimic POSIX.1e */ #define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */ #define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */ #define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */ #define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */ #define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access (POSIX.1e only) */ #define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public (POSIX.1e only) */ #define ARCHIVE_ENTRY_ACL_EVERYONE 10107 /* Everyone (NFS4 only) */ /* * Set the ACL by clearing it and adding entries one at a time. * Unlike the POSIX.1e ACL routines, you must specify the type * (access/default) for each entry. Internally, the ACL data is just * a soup of entries. API calls here allow you to retrieve just the * entries of interest. This design (which goes against the spirit of * POSIX.1e) is useful for handling archive formats that combine * default and access information in a single ACL list. */ __LA_DECL void archive_entry_acl_clear(struct archive_entry *); __LA_DECL int archive_entry_acl_add_entry(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const char * /* name */); __LA_DECL int archive_entry_acl_add_entry_w(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const wchar_t * /* name */); /* * To retrieve the ACL, first "reset", then repeatedly ask for the * "next" entry. The want_type parameter allows you to request only * certain types of entries. */ __LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */); __LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const char ** /* name */); /* * Construct a text-format ACL. The flags argument is a bitmask that * can include any of the following: * * Flags only for archive entries with POSIX.1e ACL: * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries. * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries. * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each * default ACL entry. * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and * "mask" entries. * * Flags only for archive entries with NFSv4 ACL: * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for * unset permissions and flags in NFSv4 ACL permission and flag fields * * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL: * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in * each ACL entry. * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma * instead of newline. */ #define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 0x00000001 #define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 0x00000002 #define ARCHIVE_ENTRY_ACL_STYLE_SOLARIS 0x00000004 #define ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 0x00000008 #define ARCHIVE_ENTRY_ACL_STYLE_COMPACT 0x00000010 __LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *, la_ssize_t * /* len */, int /* flags */); __LA_DECL char *archive_entry_acl_to_text(struct archive_entry *, la_ssize_t * /* len */, int /* flags */); __LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *, const wchar_t * /* wtext */, int /* type */); __LA_DECL int archive_entry_acl_from_text(struct archive_entry *, const char * /* text */, int /* type */); /* Deprecated constants */ #define OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024 #define OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 /* Deprecated functions */ __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int /* flags */) __LA_DEPRECATED; __LA_DECL const char *archive_entry_acl_text(struct archive_entry *, int /* flags */) __LA_DEPRECATED; /* Return bitmask of ACL types in an archive entry */ __LA_DECL int archive_entry_acl_types(struct archive_entry *); /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); /* Return an opaque ACL object. */ /* There's not yet anything clients can actually do with this... */ struct archive_acl; __LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *); /* * extended attributes */ __LA_DECL void archive_entry_xattr_clear(struct archive_entry *); __LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *, const char * /* name */, const void * /* value */, size_t /* size */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_xattr_count(struct archive_entry *); __LA_DECL int archive_entry_xattr_reset(struct archive_entry *); __LA_DECL int archive_entry_xattr_next(struct archive_entry *, const char ** /* name */, const void ** /* value */, size_t *); /* * sparse */ __LA_DECL void archive_entry_sparse_clear(struct archive_entry *); __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *, la_int64_t /* offset */, la_int64_t /* length */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the * "next" entry. */ __LA_DECL int archive_entry_sparse_count(struct archive_entry *); __LA_DECL int archive_entry_sparse_reset(struct archive_entry *); __LA_DECL int archive_entry_sparse_next(struct archive_entry *, la_int64_t * /* offset */, la_int64_t * /* length */); /* * Utility to match up hardlinks. * * The 'struct archive_entry_linkresolver' is a cache of archive entries * for files with multiple links. Here's how to use it: * 1. Create a lookup object with archive_entry_linkresolver_new() * 2. Tell it the archive format you're using. * 3. Hand each archive_entry to archive_entry_linkify(). * That function will return 0, 1, or 2 entries that should * be written. * 4. Call archive_entry_linkify(resolver, NULL) until * no more entries are returned. * 5. Call archive_entry_linkresolver_free(resolver) to free resources. * * The entries returned have their hardlink and size fields updated * appropriately. If an entry is passed in that does not refer to * a file with multiple links, it is returned unchanged. The intention * is that you should be able to simply filter all entries through * this machine. * * To make things more efficient, be sure that each entry has a valid * nlinks value. The hardlink cache uses this to track when all links * have been found. If the nlinks value is zero, it will keep every * name in the cache indefinitely, which can use a lot of memory. * * Note that archive_entry_size() is reset to zero if the file * body should not be written to the archive. Pay attention! */ struct archive_entry_linkresolver; /* * There are three different strategies for marking hardlinks. * The descriptions below name them after the best-known * formats that rely on each strategy: * * "Old cpio" is the simplest, it always returns any entry unmodified. * As far as I know, only cpio formats use this. Old cpio archives * store every link with the full body; the onus is on the dearchiver * to detect and properly link the files as they are restored. * "tar" is also pretty simple; it caches a copy the first time it sees * any link. Subsequent appearances are modified to be hardlink * references to the first one without any body. Used by all tar * formats, although the newest tar formats permit the "old cpio" strategy * as well. This strategy is very simple for the dearchiver, * and reasonably straightforward for the archiver. * "new cpio" is trickier. It stores the body only with the last * occurrence. The complication is that we might not * see every link to a particular file in a single session, so * there's no easy way to know when we've seen the last occurrence. * The solution here is to queue one link until we see the next. * At the end of the session, you can enumerate any remaining * entries by calling archive_entry_linkify(NULL) and store those * bodies. If you have a file with three links l1, l2, and l3, * you'll get the following behavior if you see all three links: * linkify(l1) => NULL (the resolver stores l1 internally) * linkify(l2) => l1 (resolver stores l2, you write l1) * linkify(l3) => l2, l3 (all links seen, you can write both). * If you only see l1 and l2, you'll get this behavior: * linkify(l1) => NULL * linkify(l2) => l1 * linkify(NULL) => l2 (at end, you retrieve remaining links) * As the name suggests, this strategy is used by newer cpio variants. * It's noticeably more complex for the archiver, slightly more complex * for the dearchiver than the tar strategy, but makes it straightforward * to restore a file using any link by simply continuing to scan until * you see a link that is stored with a body. In contrast, the tar * strategy requires you to rescan the archive from the beginning to * correctly extract an arbitrary link. */ __LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void); __LA_DECL void archive_entry_linkresolver_set_strategy( struct archive_entry_linkresolver *, int /* format_code */); __LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *); __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); #ifdef __cplusplus } #endif /* This is meaningless outside of this header. */ #undef __LA_DECL #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ Index: stable/11/contrib/libarchive/libarchive/archive_entry_private.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_entry_private.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_entry_private.h (revision 368707) @@ -1,184 +1,200 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED #define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif #include "archive_acl_private.h" #include "archive_string.h" struct ae_xattr { struct ae_xattr *next; char *name; void *value; size_t size; }; struct ae_sparse { struct ae_sparse *next; int64_t offset; int64_t length; }; +struct ae_digest { + unsigned char md5[16]; + unsigned char rmd160[20]; + unsigned char sha1[20]; + unsigned char sha256[32]; + unsigned char sha384[48]; + unsigned char sha512[64]; +}; + /* * Description of an archive entry. * * Basically, this is a "struct stat" with a few text fields added in. * * TODO: Add "comment", "charset", and possibly other entries * that are supported by "pax interchange" format. However, GNU, ustar, * cpio, and other variants don't support these features, so they're not an * excruciatingly high priority right now. * * TODO: "pax interchange" format allows essentially arbitrary * key/value attributes to be attached to any entry. Supporting * such extensions may make this library useful for special * applications (e.g., a package manager could attach special * package-management attributes to each entry). There are tricky * API issues involved, so this is not going to happen until * there's a real demand for it. * * TODO: Design a good API for handling sparse files. */ struct archive_entry { struct archive *archive; /* * Note that ae_stat.st_mode & AE_IFMT can be 0! * * This occurs when the actual file type of the object is not * in the archive. For example, 'tar' archives store * hardlinks without marking the type of the underlying * object. */ /* * We have a "struct aest" for holding file metadata rather than just * a "struct stat" because on some platforms the "struct stat" has * fields which are too narrow to hold the range of possible values; * we don't want to lose information if we read an archive and write * out another (e.g., in "tar -cf new.tar @old.tar"). * * The "stat" pointer points to some form of platform-specific struct * stat; it is declared as a void * rather than a struct stat * as * some platforms have multiple varieties of stat structures. */ void *stat; int stat_valid; /* Set to 0 whenever a field in aest changes. */ struct aest { int64_t aest_atime; uint32_t aest_atime_nsec; int64_t aest_ctime; uint32_t aest_ctime_nsec; int64_t aest_mtime; uint32_t aest_mtime_nsec; int64_t aest_birthtime; uint32_t aest_birthtime_nsec; int64_t aest_gid; int64_t aest_ino; uint32_t aest_nlink; uint64_t aest_size; int64_t aest_uid; /* * Because converting between device codes and * major/minor values is platform-specific and * inherently a bit risky, we only do that conversion * lazily. That way, we will do a better job of * preserving information in those cases where no * conversion is actually required. */ int aest_dev_is_broken_down; dev_t aest_dev; dev_t aest_devmajor; dev_t aest_devminor; int aest_rdev_is_broken_down; dev_t aest_rdev; dev_t aest_rdevmajor; 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_BIRTHTIME 32 #define AE_SET_SIZE 64 #define AE_SET_INO 128 #define AE_SET_DEV 256 /* * Use aes here so that we get transparent mbs<->wcs conversions. */ struct archive_mstring ae_fflags_text; /* Text fflags per fflagstostr(3) */ unsigned long ae_fflags_set; /* Bitmap fflags */ unsigned long ae_fflags_clear; struct archive_mstring ae_gname; /* Name of owning group */ struct archive_mstring ae_hardlink; /* Name of target for hardlink */ struct archive_mstring ae_pathname; /* Name of entry */ struct archive_mstring ae_symlink; /* symlink contents */ struct archive_mstring ae_uname; /* Name of owner */ /* Not used within libarchive; useful for some clients. */ struct archive_mstring ae_sourcepath; /* Path this entry is sourced from. */ #define AE_ENCRYPTION_NONE 0 #define AE_ENCRYPTION_DATA 1 #define AE_ENCRYPTION_METADATA 2 char encryption; void *mac_metadata; size_t mac_metadata_size; + /* Digest support. */ + struct ae_digest digest; + /* ACL support. */ struct archive_acl acl; /* extattr support. */ struct ae_xattr *xattr_head; struct ae_xattr *xattr_p; /* sparse support. */ struct ae_sparse *sparse_head; struct ae_sparse *sparse_tail; struct ae_sparse *sparse_p; /* Miscellaneous. */ char strmode[12]; /* Symlink type support */ int ae_symlink_type; }; + +int +archive_entry_set_digest(struct archive_entry *entry, int type, + const unsigned char *digest); #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ Index: stable/11/contrib/libarchive/libarchive/archive_ppmd7.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_ppmd7.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_ppmd7.c (revision 368707) @@ -1,1168 +1,1168 @@ /* Ppmd7.c -- PPMdH codec 2010-03-12 : Igor Pavlov : Public domain This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ #include "archive_platform.h" -#include +#include #include "archive_ppmd7_private.h" #ifdef PPMD_32BIT #define Ppmd7_GetPtr(p, ptr) (ptr) #define Ppmd7_GetContext(p, ptr) (ptr) #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats) #else #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs))) #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs))) #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats))) #endif #define Ppmd7_GetBinSumm(p) \ &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \ p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \ (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \ 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \ ((p->RunLength >> 26) & 0x20)] #define kTopValue (1 << 24) #define MAX_FREQ 124 #define UNIT_SIZE 12 #define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) #define U2I(nu) (p->Units2Indx[(nu) - 1]) #define I2U(indx) (p->Indx2Units[indx]) #ifdef PPMD_32BIT #define REF(ptr) (ptr) #else #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) #endif #define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) #define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref)) #define STATS(ctx) Ppmd7_GetStats(p, ctx) #define ONE_STATE(ctx) Ppmd7Context_OneState(ctx) #define SUFFIX(ctx) CTX((ctx)->Suffix) static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; typedef CPpmd7_Context * CTX_PTR; struct CPpmd7_Node_; typedef #ifdef PPMD_32BIT struct CPpmd7_Node_ * #else UInt32 #endif CPpmd7_Node_Ref; typedef struct CPpmd7_Node_ { UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */ UInt16 NU; CPpmd7_Node_Ref Next; /* must be at offset >= 4 */ CPpmd7_Node_Ref Prev; } CPpmd7_Node; #ifdef PPMD_32BIT #define NODE(ptr) (ptr) #else #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs))) #endif static void Ppmd7_Update1(CPpmd7 *p); static void Ppmd7_Update1_0(CPpmd7 *p); static void Ppmd7_Update2(CPpmd7 *p); static void Ppmd7_UpdateBin(CPpmd7 *p); static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale); /* ----------- Base ----------- */ static void Ppmd7_Construct(CPpmd7 *p) { unsigned i, k, m; p->Base = 0; for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) { unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); do { p->Units2Indx[k++] = (Byte)i; } while(--step); p->Indx2Units[i] = (Byte)k; } p->NS2BSIndx[0] = (0 << 1); p->NS2BSIndx[1] = (1 << 1); memset(p->NS2BSIndx + 2, (2 << 1), 9); memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); for (i = 0; i < 3; i++) p->NS2Indx[i] = (Byte)i; for (m = i, k = 1; i < 256; i++) { p->NS2Indx[i] = (Byte)m; if (--k == 0) k = (++m) - 2; } memset(p->HB2Flag, 0, 0x40); memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40); } static void Ppmd7_Free(CPpmd7 *p) { free(p->Base); p->Size = 0; p->Base = 0; } static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size) { if (p->Base == 0 || p->Size != size) { /* RestartModel() below assumes that p->Size >= UNIT_SIZE (see the calculation of m->MinContext). */ if (size < UNIT_SIZE) { return False; } Ppmd7_Free(p); p->AlignOffset = #ifdef PPMD_32BIT (4 - size) & 3; #else 4 - (size & 3); #endif if ((p->Base = (Byte *)malloc(p->AlignOffset + size #ifndef PPMD_32BIT + UNIT_SIZE #endif )) == 0) return False; p->Size = size; } return True; } static void InsertNode(CPpmd7 *p, void *node, unsigned indx) { *((CPpmd_Void_Ref *)node) = p->FreeList[indx]; p->FreeList[indx] = REF(node); } static void *RemoveNode(CPpmd7 *p, unsigned indx) { CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]); p->FreeList[indx] = *node; return node; } static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx) { unsigned i, nu = I2U(oldIndx) - I2U(newIndx); ptr = (Byte *)ptr + U2B(I2U(newIndx)); if (I2U(i = U2I(nu)) != nu) { unsigned k = I2U(--i); InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); } InsertNode(p, ptr, i); } static void GlueFreeBlocks(CPpmd7 *p) { #ifdef PPMD_32BIT CPpmd7_Node headItem; CPpmd7_Node_Ref head = &headItem; #else CPpmd7_Node_Ref head = p->AlignOffset + p->Size; #endif CPpmd7_Node_Ref n = head; unsigned i; p->GlueCount = 255; /* create doubly-linked list of free blocks */ for (i = 0; i < PPMD_NUM_INDEXES; i++) { UInt16 nu = I2U(i); CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i]; p->FreeList[i] = 0; while (next != 0) { CPpmd7_Node *node = NODE(next); node->Next = n; n = NODE(n)->Prev = next; next = *(const CPpmd7_Node_Ref *)node; node->Stamp = 0; node->NU = (UInt16)nu; } } NODE(head)->Stamp = 1; NODE(head)->Next = n; NODE(n)->Prev = head; if (p->LoUnit != p->HiUnit) ((CPpmd7_Node *)p->LoUnit)->Stamp = 1; /* Glue free blocks */ while (n != head) { CPpmd7_Node *node = NODE(n); UInt32 nu = (UInt32)node->NU; for (;;) { CPpmd7_Node *node2 = NODE(n) + nu; nu += node2->NU; if (node2->Stamp != 0 || nu >= 0x10000) break; NODE(node2->Prev)->Next = node2->Next; NODE(node2->Next)->Prev = node2->Prev; node->NU = (UInt16)nu; } n = node->Next; } /* Fill lists of free blocks */ for (n = NODE(head)->Next; n != head;) { CPpmd7_Node *node = NODE(n); unsigned nu; CPpmd7_Node_Ref next = node->Next; for (nu = node->NU; nu > 128; nu -= 128, node += 128) InsertNode(p, node, PPMD_NUM_INDEXES - 1); if (I2U(i = U2I(nu)) != nu) { unsigned k = I2U(--i); InsertNode(p, node + k, nu - k - 1); } InsertNode(p, node, i); n = next; } } static void *AllocUnitsRare(CPpmd7 *p, unsigned indx) { unsigned i; void *retVal; if (p->GlueCount == 0) { GlueFreeBlocks(p); if (p->FreeList[indx] != 0) return RemoveNode(p, indx); } i = indx; do { if (++i == PPMD_NUM_INDEXES) { UInt32 numBytes = U2B(I2U(indx)); p->GlueCount--; return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); } } while (p->FreeList[i] == 0); retVal = RemoveNode(p, i); SplitBlock(p, retVal, i, indx); return retVal; } static void *AllocUnits(CPpmd7 *p, unsigned indx) { UInt32 numBytes; if (p->FreeList[indx] != 0) return RemoveNode(p, indx); numBytes = U2B(I2U(indx)); if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) { void *retVal = p->LoUnit; p->LoUnit += numBytes; return retVal; } return AllocUnitsRare(p, indx); } #define MyMem12Cpy(dest, src, num) \ { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \ do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); } static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU) { unsigned i0 = U2I(oldNU); unsigned i1 = U2I(newNU); if (i0 == i1) return oldPtr; if (p->FreeList[i1] != 0) { void *ptr = RemoveNode(p, i1); MyMem12Cpy(ptr, oldPtr, newNU); InsertNode(p, oldPtr, i0); return ptr; } SplitBlock(p, oldPtr, i0, i1); return oldPtr; } #define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) { (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); } static void RestartModel(CPpmd7 *p) { unsigned i, k, m; memset(p->FreeList, 0, sizeof(p->FreeList)); p->Text = p->Base + p->AlignOffset; p->HiUnit = p->Text + p->Size; p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; p->GlueCount = 0; p->OrderFall = p->MaxOrder; p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; p->PrevSuccess = 0; p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ p->MinContext->Suffix = 0; p->MinContext->NumStats = 256; p->MinContext->SummFreq = 256 + 1; p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ p->LoUnit += U2B(256 / 2); p->MinContext->Stats = REF(p->FoundState); for (i = 0; i < 256; i++) { CPpmd_State *s = &p->FoundState[i]; s->Symbol = (Byte)i; s->Freq = 1; SetSuccessor(s, 0); } for (i = 0; i < 128; i++) for (k = 0; k < 8; k++) { UInt16 *dest = p->BinSumm[i] + k; UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2)); for (m = 0; m < 64; m += 8) dest[m] = val; } for (i = 0; i < 25; i++) for (k = 0; k < 16; k++) { CPpmd_See *s = &p->See[i][k]; s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4)); s->Count = 4; } } static void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder) { p->MaxOrder = maxOrder; RestartModel(p); p->DummySee.Shift = PPMD_PERIOD_BITS; p->DummySee.Summ = 0; /* unused */ p->DummySee.Count = 64; /* unused */ } static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip) { CPpmd_State upState; CTX_PTR c = p->MinContext; CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); CPpmd_State *ps[PPMD7_MAX_ORDER]; unsigned numPs = 0; if (!skip) ps[numPs++] = p->FoundState; while (c->Suffix) { CPpmd_Void_Ref successor; CPpmd_State *s; c = SUFFIX(c); if (c->NumStats != 1) { for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); } else s = ONE_STATE(c); successor = SUCCESSOR(s); if (successor != upBranch) { c = CTX(successor); if (numPs == 0) return c; break; } ps[numPs++] = s; } upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch); SetSuccessor(&upState, upBranch + 1); if (c->NumStats == 1) upState.Freq = ONE_STATE(c)->Freq; else { UInt32 cf, s0; CPpmd_State *s; for (s = STATS(c); s->Symbol != upState.Symbol; s++); cf = s->Freq - 1; s0 = c->SummFreq - c->NumStats - cf; upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0)))); } while (numPs != 0) { /* Create Child */ CTX_PTR c1; /* = AllocContext(p); */ if (p->HiUnit != p->LoUnit) c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); else if (p->FreeList[0] != 0) c1 = (CTX_PTR)RemoveNode(p, 0); else { c1 = (CTX_PTR)AllocUnitsRare(p, 0); if (!c1) return NULL; } c1->NumStats = 1; *ONE_STATE(c1) = upState; c1->Suffix = REF(c); SetSuccessor(ps[--numPs], REF(c1)); c = c1; } return c; } static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) { CPpmd_State tmp = *t1; *t1 = *t2; *t2 = tmp; } static void UpdateModel(CPpmd7 *p) { CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); CTX_PTR c; unsigned s0, ns; if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) { c = SUFFIX(p->MinContext); if (c->NumStats == 1) { CPpmd_State *s = ONE_STATE(c); if (s->Freq < 32) s->Freq++; } else { CPpmd_State *s = STATS(c); if (s->Symbol != p->FoundState->Symbol) { do { s++; } while (s->Symbol != p->FoundState->Symbol); if (s[0].Freq >= s[-1].Freq) { SwapStates(&s[0], &s[-1]); s--; } } if (s->Freq < MAX_FREQ - 9) { s->Freq += 2; c->SummFreq += 2; } } } if (p->OrderFall == 0) { p->MinContext = p->MaxContext = CreateSuccessors(p, True); if (p->MinContext == 0) { RestartModel(p); return; } SetSuccessor(p->FoundState, REF(p->MinContext)); return; } *p->Text++ = p->FoundState->Symbol; successor = REF(p->Text); if (p->Text >= p->UnitsStart) { RestartModel(p); return; } if (fSuccessor) { if (fSuccessor <= successor) { CTX_PTR cs = CreateSuccessors(p, False); if (cs == NULL) { RestartModel(p); return; } fSuccessor = REF(cs); } if (--p->OrderFall == 0) { successor = fSuccessor; p->Text -= (p->MaxContext != p->MinContext); } } else { SetSuccessor(p->FoundState, successor); fSuccessor = REF(p->MinContext); } s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1); for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c)) { unsigned ns1; UInt32 cf, sf; if ((ns1 = c->NumStats) != 1) { if ((ns1 & 1) == 0) { /* Expand for one UNIT */ unsigned oldNU = ns1 >> 1; unsigned i = U2I(oldNU); if (i != U2I(oldNU + 1)) { void *ptr = AllocUnits(p, i + 1); void *oldPtr; if (!ptr) { RestartModel(p); return; } oldPtr = STATS(c); MyMem12Cpy(ptr, oldPtr, oldNU); InsertNode(p, oldPtr, i); c->Stats = STATS_REF(ptr); } } c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1))); } else { CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0); if (!s) { RestartModel(p); return; } *s = *ONE_STATE(c); c->Stats = REF(s); if (s->Freq < MAX_FREQ / 4 - 1) s->Freq <<= 1; else s->Freq = MAX_FREQ - 4; c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3)); } cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6); sf = (UInt32)s0 + c->SummFreq; if (cf < 6 * sf) { cf = 1 + (cf > sf) + (cf >= 4 * sf); c->SummFreq += 3; } else { cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf); c->SummFreq = (UInt16)(c->SummFreq + cf); } { CPpmd_State *s = STATS(c) + ns1; SetSuccessor(s, successor); s->Symbol = p->FoundState->Symbol; s->Freq = (Byte)cf; c->NumStats = (UInt16)(ns1 + 1); } } p->MaxContext = p->MinContext = CTX(fSuccessor); } static void Rescale(CPpmd7 *p) { unsigned i, adder, sumFreq, escFreq; CPpmd_State *stats = STATS(p->MinContext); CPpmd_State *s = p->FoundState; { CPpmd_State tmp = *s; for (; s != stats; s--) s[0] = s[-1]; *s = tmp; } escFreq = p->MinContext->SummFreq - s->Freq; s->Freq += 4; adder = (p->OrderFall != 0); s->Freq = (Byte)((s->Freq + adder) >> 1); sumFreq = s->Freq; i = p->MinContext->NumStats - 1; do { escFreq -= (++s)->Freq; s->Freq = (Byte)((s->Freq + adder) >> 1); sumFreq += s->Freq; if (s[0].Freq > s[-1].Freq) { CPpmd_State *s1 = s; CPpmd_State tmp = *s1; do s1[0] = s1[-1]; while (--s1 != stats && tmp.Freq > s1[-1].Freq); *s1 = tmp; } } while (--i); if (s->Freq == 0) { unsigned numStats = p->MinContext->NumStats; unsigned n0, n1; do { i++; } while ((--s)->Freq == 0); escFreq += i; p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i); if (p->MinContext->NumStats == 1) { CPpmd_State tmp = *stats; do { tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1)); escFreq >>= 1; } while (escFreq > 1); InsertNode(p, stats, U2I(((numStats + 1) >> 1))); *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; return; } n0 = (numStats + 1) >> 1; n1 = (p->MinContext->NumStats + 1) >> 1; if (n0 != n1) p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); } p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); p->FoundState = STATS(p->MinContext); } static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq) { CPpmd_See *see; unsigned nonMasked = p->MinContext->NumStats - numMasked; if (p->MinContext->NumStats != 256) { see = p->See[p->NS2Indx[nonMasked - 1]] + (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) + 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) + 4 * (numMasked > nonMasked) + p->HiBitsFlag; { unsigned r = (see->Summ >> see->Shift); see->Summ = (UInt16)(see->Summ - r); *escFreq = r + (r == 0); } } else { see = &p->DummySee; *escFreq = 1; } return see; } static void NextContext(CPpmd7 *p) { CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); if (p->OrderFall == 0 && (Byte *)c > p->Text) p->MinContext = p->MaxContext = c; else UpdateModel(p); } static void Ppmd7_Update1(CPpmd7 *p) { CPpmd_State *s = p->FoundState; s->Freq += 4; p->MinContext->SummFreq += 4; if (s[0].Freq > s[-1].Freq) { SwapStates(&s[0], &s[-1]); p->FoundState = --s; if (s->Freq > MAX_FREQ) Rescale(p); } NextContext(p); } static void Ppmd7_Update1_0(CPpmd7 *p) { p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq); p->RunLength += p->PrevSuccess; p->MinContext->SummFreq += 4; if ((p->FoundState->Freq += 4) > MAX_FREQ) Rescale(p); NextContext(p); } static void Ppmd7_UpdateBin(CPpmd7 *p) { p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0)); p->PrevSuccess = 1; p->RunLength++; NextContext(p); } static void Ppmd7_Update2(CPpmd7 *p) { p->MinContext->SummFreq += 4; if ((p->FoundState->Freq += 4) > MAX_FREQ) Rescale(p); p->RunLength = p->InitRL; UpdateModel(p); } /* ---------- Decode ---------- */ static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p) { unsigned i; p->Low = p->Bottom = 0; p->Range = 0xFFFFFFFF; for (i = 0; i < 4; i++) p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); return (p->Code < 0xFFFFFFFF); } static Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p) { if (p->Stream->Read((void *)p->Stream) != 0) return False; return Ppmd_RangeDec_Init(p); } static Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p) { if (!Ppmd_RangeDec_Init(p)) return False; p->Bottom = 0x8000; return True; } static UInt32 Range_GetThreshold(void *pp, UInt32 total) { CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; return (p->Code - p->Low) / (p->Range /= total); } static void Range_Normalize(CPpmd7z_RangeDec *p) { while (1) { if((p->Low ^ (p->Low + p->Range)) >= kTopValue) { if(p->Range >= p->Bottom) break; else p->Range = ((uint32_t)(-(int32_t)p->Low)) & (p->Bottom - 1); } p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); p->Range <<= 8; p->Low <<= 8; } } static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size) { CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; p->Code -= start * p->Range; p->Range *= size; Range_Normalize(p); } static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size) { CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; p->Low += start * p->Range; p->Range *= size; Range_Normalize(p); } static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0) { CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; UInt32 newBound = (p->Range >> 14) * size0; UInt32 symbol; if (p->Code < newBound) { symbol = 0; p->Range = newBound; } else { symbol = 1; p->Code -= newBound; p->Range -= newBound; } Range_Normalize(p); return symbol; } static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0) { CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE); if(value < size0) { bit = 0; p->p.Decode(p, 0, size0); } else { bit = 1; p->p.Decode(p, size0, PPMD_BIN_SCALE - size0); } return bit; } static void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p) { p->p.GetThreshold = Range_GetThreshold; p->p.Decode = Range_Decode_7z; p->p.DecodeBit = Range_DecodeBit_7z; } static void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p) { p->p.GetThreshold = Range_GetThreshold; p->p.Decode = Range_Decode_RAR; p->p.DecodeBit = Range_DecodeBit_RAR; } #define MASK(sym) ((signed char *)charMask)[sym] static int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc) { size_t charMask[256 / sizeof(size_t)]; if (p->MinContext->NumStats != 1) { CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext); unsigned i; UInt32 count, hiCnt; if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) { Byte symbol; rc->Decode(rc, 0, s->Freq); p->FoundState = s; symbol = s->Symbol; Ppmd7_Update1_0(p); return symbol; } p->PrevSuccess = 0; i = p->MinContext->NumStats - 1; do { if ((hiCnt += (++s)->Freq) > count) { Byte symbol; rc->Decode(rc, hiCnt - s->Freq, s->Freq); p->FoundState = s; symbol = s->Symbol; Ppmd7_Update1(p); return symbol; } } while (--i); if (count >= p->MinContext->SummFreq) return -2; p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]; rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt); PPMD_SetAllBitsIn256Bytes(charMask); MASK(s->Symbol) = 0; i = p->MinContext->NumStats - 1; do { MASK((--s)->Symbol) = 0; } while (--i); } else { UInt16 *prob = Ppmd7_GetBinSumm(p); if (rc->DecodeBit(rc, *prob) == 0) { Byte symbol; *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol; Ppmd7_UpdateBin(p); return symbol; } *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); p->InitEsc = PPMD7_kExpEscape[*prob >> 10]; PPMD_SetAllBitsIn256Bytes(charMask); MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0; p->PrevSuccess = 0; } for (;;) { CPpmd_State *ps[256], *s; UInt32 freqSum, count, hiCnt; CPpmd_See *see; unsigned i, num, numMasked = p->MinContext->NumStats; do { p->OrderFall++; if (!p->MinContext->Suffix) return -1; p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix); } while (p->MinContext->NumStats == numMasked); hiCnt = 0; s = Ppmd7_GetStats(p, p->MinContext); i = 0; num = p->MinContext->NumStats - numMasked; do { int k = (int)(MASK(s->Symbol)); hiCnt += (s->Freq & k); ps[i] = s++; i -= k; } while (i != num); see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum); freqSum += hiCnt; count = rc->GetThreshold(rc, freqSum); if (count < hiCnt) { Byte symbol; CPpmd_State **pps = ps; for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); s = *pps; rc->Decode(rc, hiCnt - s->Freq, s->Freq); Ppmd_See_Update(see); p->FoundState = s; symbol = s->Symbol; Ppmd7_Update2(p); return symbol; } if (count >= freqSum) return -2; rc->Decode(rc, hiCnt, freqSum - hiCnt); see->Summ = (UInt16)(see->Summ + freqSum); do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); } } /* ---------- Encode ---------- Ppmd7Enc.c */ #define kTopValue (1 << 24) static void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p) { p->Low = 0; p->Range = 0xFFFFFFFF; p->Cache = 0; p->CacheSize = 1; } static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p) { if ((UInt32)p->Low < (UInt32)0xFF000000 || (unsigned)(p->Low >> 32) != 0) { Byte temp = p->Cache; do { p->Stream->Write(p->Stream, (Byte)(temp + (Byte)(p->Low >> 32))); temp = 0xFF; } while(--p->CacheSize != 0); p->Cache = (Byte)((UInt32)p->Low >> 24); } p->CacheSize++; p->Low = ((UInt32)p->Low << 8) & 0xFFFFFFFF; } static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total) { p->Low += (UInt64)start * (UInt64)(p->Range /= total); p->Range *= size; while (p->Range < kTopValue) { p->Range <<= 8; RangeEnc_ShiftLow(p); } } static void RangeEnc_EncodeBit_0(CPpmd7z_RangeEnc *p, UInt32 size0) { p->Range = (p->Range >> 14) * size0; while (p->Range < kTopValue) { p->Range <<= 8; RangeEnc_ShiftLow(p); } } static void RangeEnc_EncodeBit_1(CPpmd7z_RangeEnc *p, UInt32 size0) { UInt32 newBound = (p->Range >> 14) * size0; p->Low += newBound; p->Range -= newBound; while (p->Range < kTopValue) { p->Range <<= 8; RangeEnc_ShiftLow(p); } } static void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p) { unsigned i; for (i = 0; i < 5; i++) RangeEnc_ShiftLow(p); } #define MASK(sym) ((signed char *)charMask)[sym] static void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol) { size_t charMask[256 / sizeof(size_t)]; if (p->MinContext->NumStats != 1) { CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext); UInt32 sum; unsigned i; if (s->Symbol == symbol) { RangeEnc_Encode(rc, 0, s->Freq, p->MinContext->SummFreq); p->FoundState = s; Ppmd7_Update1_0(p); return; } p->PrevSuccess = 0; sum = s->Freq; i = p->MinContext->NumStats - 1; do { if ((++s)->Symbol == symbol) { RangeEnc_Encode(rc, sum, s->Freq, p->MinContext->SummFreq); p->FoundState = s; Ppmd7_Update1(p); return; } sum += s->Freq; } while (--i); p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]; PPMD_SetAllBitsIn256Bytes(charMask); MASK(s->Symbol) = 0; i = p->MinContext->NumStats - 1; do { MASK((--s)->Symbol) = 0; } while (--i); RangeEnc_Encode(rc, sum, p->MinContext->SummFreq - sum, p->MinContext->SummFreq); } else { UInt16 *prob = Ppmd7_GetBinSumm(p); CPpmd_State *s = Ppmd7Context_OneState(p->MinContext); if (s->Symbol == symbol) { RangeEnc_EncodeBit_0(rc, *prob); *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); p->FoundState = s; Ppmd7_UpdateBin(p); return; } else { RangeEnc_EncodeBit_1(rc, *prob); *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); p->InitEsc = PPMD7_kExpEscape[*prob >> 10]; PPMD_SetAllBitsIn256Bytes(charMask); MASK(s->Symbol) = 0; p->PrevSuccess = 0; } } for (;;) { UInt32 escFreq; CPpmd_See *see; CPpmd_State *s; UInt32 sum; unsigned i, numMasked = p->MinContext->NumStats; do { p->OrderFall++; if (!p->MinContext->Suffix) return; /* EndMarker (symbol = -1) */ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix); } while (p->MinContext->NumStats == numMasked); see = Ppmd7_MakeEscFreq(p, numMasked, &escFreq); s = Ppmd7_GetStats(p, p->MinContext); sum = 0; i = p->MinContext->NumStats; do { int cur = s->Symbol; if (cur == symbol) { UInt32 low = sum; CPpmd_State *s1 = s; do { sum += (s->Freq & (int)(MASK(s->Symbol))); s++; } while (--i); RangeEnc_Encode(rc, low, s1->Freq, sum + escFreq); Ppmd_See_Update(see); p->FoundState = s1; Ppmd7_Update2(p); return; } sum += (s->Freq & (int)(MASK(cur))); MASK(cur) = 0; s++; } while (--i); RangeEnc_Encode(rc, sum, escFreq, sum + escFreq); see->Summ = (UInt16)(see->Summ + sum + escFreq); } } const IPpmd7 __archive_ppmd7_functions = { &Ppmd7_Construct, &Ppmd7_Alloc, &Ppmd7_Free, &Ppmd7_Init, &Ppmd7z_RangeDec_CreateVTable, &PpmdRAR_RangeDec_CreateVTable, &Ppmd7z_RangeDec_Init, &PpmdRAR_RangeDec_Init, &Ppmd7_DecodeSymbol, &Ppmd7z_RangeEnc_Init, &Ppmd7z_RangeEnc_FlushData, &Ppmd7_EncodeSymbol }; Index: stable/11/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (revision 368707) @@ -1,1044 +1,1084 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * Copyright (c) 2016 Martin Matuska * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD"); /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #if defined(HAVE_SYS_XATTR_H) #include #elif defined(HAVE_ATTR_XATTR_H) #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_FIEMAP_H #include #endif #ifdef HAVE_LINUX_FS_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif static int setup_mac_metadata(struct archive_read_disk *, struct archive_entry *, int *fd); +#ifdef ARCHIVE_XATTR_FREEBSD +static int setup_xattrs_namespace(struct archive_read_disk *, + struct archive_entry *, int *, int); +#endif static int setup_xattrs(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_sparse(struct archive_read_disk *, struct archive_entry *, int *fd); #if defined(HAVE_LINUX_FIEMAP_H) static int setup_sparse_fiemap(struct archive_read_disk *, struct archive_entry *, int *fd); #endif #if !ARCHIVE_ACL_SUPPORT int archive_read_disk_entry_setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif /* * Enter working directory and return working pathname of archive_entry. * If a pointer to an integer is provided and its value is below zero * open a file descriptor on this pathname. */ const char * archive_read_disk_entry_setup_path(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL || (a->tree != NULL && a->tree_enter_working_dir(a->tree) != 0)) path = archive_entry_pathname(entry); if (path == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't determine path"); } else if (fd != NULL && *fd < 0 && a->tree != NULL && (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) { *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK); } return (path); } int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_entry_from_file"); archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (la_stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; r = ioctl(fd, #if defined(FS_IOC_GETFLAGS) FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len + 1); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = '\0'; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = 0; if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0) r = archive_read_disk_entry_setup_acls(a, entry, &fd); if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) { r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; } if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); } #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) /* * The Mac OS "copyfile()" API copies the extended metadata for a * file into a separate file in AppleDouble format (see RFC 1740). * * Mac OS tar and cpio implementations store this extended * metadata as a separate entry just before the regular entry * with a "._" prefix added to the filename. * * Note that this is currently done unconditionally; the tar program has * an option to discard this information before the archive is written. * * TODO: If there's a failure, report it and return ARCHIVE_WARN. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int tempfd = -1; int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; struct stat copyfile_stat; int ret = ARCHIVE_OK; void *buff = NULL; int have_attrs; const char *name, *tempdir; struct archive_string tempfile; (void)fd; /* UNUSED */ name = archive_read_disk_entry_setup_path(a, entry, NULL); if (name == NULL) return (ARCHIVE_WARN); /* Short-circuit if there's nothing to do. */ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); if (have_attrs == -1) { archive_set_error(&a->archive, errno, "Could not check extended attributes"); return (ARCHIVE_WARN); } if (have_attrs == 0) return (ARCHIVE_OK); tempdir = NULL; if (issetugid() == 0) tempdir = getenv("TMPDIR"); if (tempdir == NULL) tempdir = _PATH_TMP; archive_string_init(&tempfile); archive_strcpy(&tempfile, tempdir); archive_strcat(&tempfile, "tar.md.XXXXXX"); tempfd = mkstemp(tempfile.s); if (tempfd < 0) { archive_set_error(&a->archive, errno, "Could not open extended attribute file"); ret = ARCHIVE_WARN; goto cleanup; } __archive_ensure_cloexec_flag(tempfd); /* XXX I wish copyfile() could pack directly to a memory * buffer; that would avoid the temp file here. For that * matter, it would be nice if fcopyfile() actually worked, * that would reduce the many open/close races here. */ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { archive_set_error(&a->archive, errno, "Could not pack extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } buff = malloc(copyfile_stat.st_size); if (buff == NULL) { archive_set_error(&a->archive, errno, "Could not allocate memory for extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { archive_set_error(&a->archive, errno, "Could not read extended attributes into memory"); ret = ARCHIVE_WARN; goto cleanup; } archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); cleanup: if (tempfd >= 0) { close(tempfd); unlink(tempfile.s); } archive_string_free(&tempfile); free(buff); return (ret); } #else /* * Stub implementation for non-Mac systems. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX /* * Linux, Darwin and AIX extended attribute support. * * TODO: By using a stack-allocated buffer for the first * call to getxattr(), we might be able to avoid the second * call entirely. We only need the second call if the * stack-allocated buffer is too small. But a modest buffer * of 1024 bytes or so will often be big enough. Same applies * to listxattr(). */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, const char *name, int fd, const char *accpath) { ssize_t size; void *value = NULL; if (fd >= 0) { #if ARCHIVE_XATTR_LINUX size = fgetxattr(fd, name, NULL, 0); #elif ARCHIVE_XATTR_DARWIN size = fgetxattr(fd, name, NULL, 0, 0, 0); #elif ARCHIVE_XATTR_AIX size = fgetea(fd, name, NULL, 0); #endif } else if (!a->follow_symlinks) { #if ARCHIVE_XATTR_LINUX size = lgetxattr(accpath, name, NULL, 0); #elif ARCHIVE_XATTR_DARWIN size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX size = lgetea(accpath, name, NULL, 0); #endif } else { #if ARCHIVE_XATTR_LINUX size = getxattr(accpath, name, NULL, 0); #elif ARCHIVE_XATTR_DARWIN size = getxattr(accpath, name, NULL, 0, 0, 0); #elif ARCHIVE_XATTR_AIX size = getea(accpath, name, NULL, 0); #endif } if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (fd >= 0) { #if ARCHIVE_XATTR_LINUX size = fgetxattr(fd, name, value, size); #elif ARCHIVE_XATTR_DARWIN size = fgetxattr(fd, name, value, size, 0, 0); #elif ARCHIVE_XATTR_AIX size = fgetea(fd, name, value, size); #endif } else if (!a->follow_symlinks) { #if ARCHIVE_XATTR_LINUX size = lgetxattr(accpath, name, value, size); #elif ARCHIVE_XATTR_DARWIN size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX size = lgetea(accpath, name, value, size); #endif } else { #if ARCHIVE_XATTR_LINUX size = getxattr(accpath, name, value, size); #elif ARCHIVE_XATTR_DARWIN size = getxattr(accpath, name, value, size, 0, 0); #elif ARCHIVE_XATTR_AIX size = getea(accpath, name, value, size); #endif } if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, name, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char *list, *p; const char *path; ssize_t list_size; path = NULL; if (*fd < 0) { path = archive_read_disk_entry_setup_path(a, entry, fd); if (path == NULL) return (ARCHIVE_WARN); } if (*fd >= 0) { #if ARCHIVE_XATTR_LINUX list_size = flistxattr(*fd, NULL, 0); #elif ARCHIVE_XATTR_DARWIN list_size = flistxattr(*fd, NULL, 0, 0); #elif ARCHIVE_XATTR_AIX list_size = flistea(*fd, NULL, 0); #endif } else if (!a->follow_symlinks) { #if ARCHIVE_XATTR_LINUX list_size = llistxattr(path, NULL, 0); #elif ARCHIVE_XATTR_DARWIN list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX list_size = llistea(path, NULL, 0); #endif } else { #if ARCHIVE_XATTR_LINUX list_size = listxattr(path, NULL, 0); #elif ARCHIVE_XATTR_DARWIN list_size = listxattr(path, NULL, 0, 0); #elif ARCHIVE_XATTR_AIX list_size = listea(path, NULL, 0); #endif } if (list_size == -1) { if (errno == ENOTSUP || errno == ENOSYS) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (*fd >= 0) { #if ARCHIVE_XATTR_LINUX list_size = flistxattr(*fd, list, list_size); #elif ARCHIVE_XATTR_DARWIN list_size = flistxattr(*fd, list, list_size, 0); #elif ARCHIVE_XATTR_AIX list_size = flistea(*fd, list, list_size); #endif } else if (!a->follow_symlinks) { #if ARCHIVE_XATTR_LINUX list_size = llistxattr(path, list, list_size); #elif ARCHIVE_XATTR_DARWIN list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX list_size = llistea(path, list, list_size); #endif } else { #if ARCHIVE_XATTR_LINUX list_size = listxattr(path, list, list_size); #elif ARCHIVE_XATTR_DARWIN list_size = listxattr(path, list, list_size, 0); #elif ARCHIVE_XATTR_AIX list_size = listea(path, list, list_size); #endif } if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } for (p = list; (p - list) < list_size; p += strlen(p) + 1) { #if ARCHIVE_XATTR_LINUX /* Linux: skip POSIX.1e ACL extended attributes */ if (strncmp(p, "system.", 7) == 0 && (strcmp(p + 7, "posix_acl_access") == 0 || strcmp(p + 7, "posix_acl_default") == 0)) continue; if (strncmp(p, "trusted.SGI_", 12) == 0 && (strcmp(p + 12, "ACL_DEFAULT") == 0 || strcmp(p + 12, "ACL_FILE") == 0)) continue; /* Linux: xfsroot namespace is obsolete and unsupported */ if (strncmp(p, "xfsroot.", 8) == 0) continue; #endif setup_xattr(a, entry, p, *fd, path); } free(list); return (ARCHIVE_OK); } #elif ARCHIVE_XATTR_FREEBSD /* * FreeBSD extattr interface. */ /* TODO: Implement this. Follow the Linux model above, but * with FreeBSD-specific system calls, of course. Be careful * to not include the system extattrs that hold ACLs; we handle * those separately. */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd, const char *path); static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd, const char *accpath) { ssize_t size; void *value = NULL; if (fd >= 0) size = extattr_get_fd(fd, namespace, name, NULL, 0); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, NULL, 0); else size = extattr_get_file(accpath, namespace, name, NULL, 0); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (fd >= 0) size = extattr_get_fd(fd, namespace, name, value, size); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, value, size); else size = extattr_get_file(accpath, namespace, name, value, size); if (size == -1) { free(value); archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, fullname, value, size); free(value); return (ARCHIVE_OK); } static int -setup_xattrs(struct archive_read_disk *a, - struct archive_entry *entry, int *fd) +setup_xattrs_namespace(struct archive_read_disk *a, + struct archive_entry *entry, int *fd, int namespace) { char buff[512]; char *list, *p; ssize_t list_size; const char *path; - int namespace = EXTATTR_NAMESPACE_USER; path = NULL; if (*fd < 0) { path = archive_read_disk_entry_setup_path(a, entry, fd); if (path == NULL) return (ARCHIVE_WARN); } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, NULL, 0); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, NULL, 0); else list_size = extattr_list_file(path, namespace, NULL, 0); if (list_size == -1 && errno == EOPNOTSUPP) return (ARCHIVE_OK); + if (list_size == -1 && errno == EPERM) + return (ARCHIVE_OK); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, list, list_size); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, list, list_size); else list_size = extattr_list_file(path, namespace, list, list_size); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } p = list; while ((p - list) < list_size) { size_t len = 255 & (int)*p; char *name; - strcpy(buff, "user."); + if (namespace == EXTATTR_NAMESPACE_SYSTEM) { + if (!strcmp(p + 1, "nfs4.acl") || + !strcmp(p + 1, "posix1e.acl_access") || + !strcmp(p + 1, "posix1e.acl_default")) { + p += 1 + len; + continue; + } + strcpy(buff, "system."); + } else { + strcpy(buff, "user."); + } name = buff + strlen(buff); memcpy(name, p + 1, len); name[len] = '\0'; setup_xattr(a, entry, namespace, name, buff, *fd, path); p += 1 + len; } free(list); + return (ARCHIVE_OK); +} + +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int *fd) +{ + int namespaces[2]; + int i, res; + + namespaces[0] = EXTATTR_NAMESPACE_USER; + namespaces[1] = EXTATTR_NAMESPACE_SYSTEM; + + for (i = 0; i < 2; i++) { + res = setup_xattrs_namespace(a, entry, fd, + namespaces[i]); + switch (res) { + case (ARCHIVE_OK): + case (ARCHIVE_WARN): + break; + default: + return (res); + } + } + return (ARCHIVE_OK); } #else /* * Generic (stub) extended attribute support. */ static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if defined(HAVE_LINUX_FIEMAP_H) /* * Linux FIEMAP sparse interface. * * The FIEMAP ioctl returns an "extent" for each physical allocation * on disk. We need to process those to generate a more compact list * of logical file blocks. We also need to be very careful to use * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes * does not report allocations for newly-written data that hasn't * been synced to disk. * * It's important to return a minimal sparse file list because we want * to not trigger sparse file extensions if we don't have to, since * not all readers support them. */ static int setup_sparse_fiemap(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[4096]; struct fiemap *fm; struct fiemap_extent *fe; int64_t size; int count, do_fiemap, iters; int exit_sts = ARCHIVE_OK; const char *path; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); if (*fd < 0) { path = archive_read_disk_entry_setup_path(a, entry, NULL); if (path == NULL) return (ARCHIVE_FAILED); if (a->tree != NULL) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); } /* Initialize buffer to avoid the error valgrind complains about. */ memset(buff, 0, sizeof(buff)); count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); fm = (struct fiemap *)buff; fm->fm_start = 0; fm->fm_length = ~0ULL;; fm->fm_flags = FIEMAP_FLAG_SYNC; fm->fm_extent_count = count; do_fiemap = 1; size = archive_entry_size(entry); for (iters = 0; ; ++iters) { int i, r; r = ioctl(*fd, FS_IOC_FIEMAP, fm); if (r < 0) { /* When something error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ goto exit_setup_sparse_fiemap; } if (fm->fm_mapped_extents == 0) { if (iters == 0) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } break; } fe = fm->fm_extents; for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { /* The fe_length of the last block does not * adjust itself to its size files. */ int64_t length = fe->fe_length; if (fe->fe_logical + length > (uint64_t)size) length -= fe->fe_logical + length - size; if (fe->fe_logical == 0 && length == size) { /* This is not sparse. */ do_fiemap = 0; break; } if (length > 0) archive_entry_sparse_add_entry(entry, fe->fe_logical, length); } if (fe->fe_flags & FIEMAP_EXTENT_LAST) do_fiemap = 0; } if (do_fiemap) { fe = fm->fm_extents + fm->fm_mapped_extents -1; fm->fm_start = fe->fe_logical + fe->fe_length; } else break; } exit_setup_sparse_fiemap: return (exit_sts); } #if !defined(SEEK_HOLE) || !defined(SEEK_DATA) static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { return setup_sparse_fiemap(a, entry, fd); } #endif #endif /* defined(HAVE_LINUX_FIEMAP_H) */ #if defined(SEEK_HOLE) && defined(SEEK_DATA) /* * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int64_t size; off_t initial_off; off_t off_s, off_e; int exit_sts = ARCHIVE_OK; int check_fully_sparse = 0; const char *path; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); /* Does filesystem support the reporting of hole ? */ if (*fd < 0) path = archive_read_disk_entry_setup_path(a, entry, fd); else path = NULL; if (*fd >= 0) { #ifdef _PC_MIN_HOLE_SIZE if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); #endif initial_off = lseek(*fd, 0, SEEK_CUR); if (initial_off != 0) lseek(*fd, 0, SEEK_SET); } else { if (path == NULL) return (ARCHIVE_FAILED); #ifdef _PC_MIN_HOLE_SIZE if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); #endif *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); initial_off = 0; } #ifndef _PC_MIN_HOLE_SIZE /* Check if the underlying filesystem supports seek hole */ off_s = lseek(*fd, 0, SEEK_HOLE); if (off_s < 0) #if defined(HAVE_LINUX_FIEMAP_H) return setup_sparse_fiemap(a, entry, fd); #else goto exit_setup_sparse; #endif else if (off_s > 0) lseek(*fd, 0, SEEK_SET); #endif off_s = 0; size = archive_entry_size(entry); while (off_s < size) { off_s = lseek(*fd, off_s, SEEK_DATA); if (off_s == (off_t)-1) { if (errno == ENXIO) { /* no more hole */ if (archive_entry_sparse_count(entry) == 0) { /* Potentially a fully-sparse file. */ check_fully_sparse = 1; } break; } archive_set_error(&a->archive, errno, "lseek(SEEK_HOLE) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } off_e = lseek(*fd, off_s, SEEK_HOLE); if (off_e == (off_t)-1) { if (errno == ENXIO) { off_e = lseek(*fd, 0, SEEK_END); if (off_e != (off_t)-1) break;/* no more data */ } archive_set_error(&a->archive, errno, "lseek(SEEK_DATA) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } if (off_s == 0 && off_e == size) break;/* This is not sparse. */ archive_entry_sparse_add_entry(entry, off_s, off_e - off_s); off_s = off_e; } if (check_fully_sparse) { if (lseek(*fd, 0, SEEK_HOLE) == 0 && lseek(*fd, 0, SEEK_END) == size) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } } exit_setup_sparse: lseek(*fd, initial_off, SEEK_SET); return (exit_sts); } #elif !defined(HAVE_LINUX_FIEMAP_H) /* * Generic (stub) sparse support. */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ Index: stable/11/contrib/libarchive/libarchive/archive_read_filter.3 =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_filter.3 (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_filter.3 (revision 368707) @@ -1,154 +1,164 @@ .\" Copyright (c) 2003-2011 Tim Kientzle .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd August 14, 2014 +.Dd June 9, 2020 .Dt ARCHIVE_READ_FILTER 3 .Os .Sh NAME .Nm archive_read_support_filter_all , .Nm archive_read_support_filter_bzip2 , .Nm archive_read_support_filter_compress , .Nm archive_read_support_filter_gzip , .Nm archive_read_support_filter_lz4 , .Nm archive_read_support_filter_lzma , .Nm archive_read_support_filter_none , .Nm archive_read_support_filter_rpm , .Nm archive_read_support_filter_uu , .Nm archive_read_support_filter_xz , .Nm archive_read_support_filter_zstd , .Nm archive_read_support_filter_program , .Nm archive_read_support_filter_program_signature .Nd functions for reading streaming archives .\" .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive.h .Ft int .Fn archive_read_support_filter_all "struct archive *" .Ft int +.Fn archive_read_support_filter_by_code "struct archive *" "int" +.Ft int .Fn archive_read_support_filter_bzip2 "struct archive *" .Ft int .Fn archive_read_support_filter_compress "struct archive *" .Ft int .Fn archive_read_support_filter_grzip "struct archive *" .Ft int .Fn archive_read_support_filter_gzip "struct archive *" .Ft int .Fn archive_read_support_filter_lrzip "struct archive *" .Ft int .Fn archive_read_support_filter_lz4 "struct archive *" .Ft int .Fn archive_read_support_filter_lzma "struct archive *" .Ft int .Fn archive_read_support_filter_lzop "struct archive *" .Ft int .Fn archive_read_support_filter_none "struct archive *" .Ft int .Fn archive_read_support_filter_rpm "struct archive *" .Ft int .Fn archive_read_support_filter_uu "struct archive *" .Ft int .Fn archive_read_support_filter_xz "struct archive *" .Ft int .Fn archive_read_support_filter_zstd "struct archive *" .Ft int .Fo archive_read_support_filter_program .Fa "struct archive *" .Fa "const char *cmd" .Fc .Ft int .Fo archive_read_support_filter_program_signature .Fa "struct archive *" .Fa "const char *cmd" .Fa "const void *signature" .Fa "size_t signature_length" .Fc .\" .Sh DESCRIPTION .Bl -tag -compact -width indent .It Xo .Fn archive_read_support_filter_bzip2 , .Fn archive_read_support_filter_compress , .Fn archive_read_support_filter_grzip , .Fn archive_read_support_filter_gzip , .Fn archive_read_support_filter_lrzip , .Fn archive_read_support_filter_lz4 , .Fn archive_read_support_filter_lzma , .Fn archive_read_support_filter_lzop , .Fn archive_read_support_filter_none , .Fn archive_read_support_filter_rpm , .Fn archive_read_support_filter_uu , .Fn archive_read_support_filter_xz , .Fn archive_read_support_filter_zstd , .Xc Enables auto-detection code and decompression support for the specified compression. These functions may fall back on external programs if an appropriate library was not available at build time. Decompression using an external program is usually slower than decompression through built-in libraries. Note that .Dq none is always enabled by default. .It Fn archive_read_support_filter_all Enables all available decompression filters. +.It Fn archive_read_support_filter_by_code +Enables a single filter specified by the filter code. +This function does not work with +.Cm ARCHIVE_FILTER_PROGRAM . +Note: In statically-linked executables, this will cause +your program to include support for every filter. +If executable size is a concern, you may wish to avoid +using this function. .It Fn archive_read_support_filter_program Data is fed through the specified external program before being dearchived. Note that this disables automatic detection of the compression format, so it makes no sense to specify this in conjunction with any other decompression option. .It Fn archive_read_support_filter_program_signature This feeds data through the specified external program but only if the initial bytes of the data match the specified signature value. .El .\" .\". Sh EXAMPLE .\" .Sh RETURN VALUES These functions return .Cm ARCHIVE_OK if the compression is fully supported, .Cm ARCHIVE_WARN if the compression is supported only through an external program. .Pp .Fn archive_read_support_filter_none always succeeds. .\" .Sh ERRORS Detailed error codes and textual descriptions are available from the .Fn archive_errno and .Fn archive_error_string functions. .\" .Sh SEE ALSO .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_format 3 , .Xr archive_read_format 3 , .Xr libarchive 3 Index: stable/11/contrib/libarchive/libarchive/archive_read_set_format.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_set_format.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_set_format.c (revision 368707) @@ -1,108 +1,117 @@ /*- * Copyright (c) 2003-2012 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" int archive_read_set_format(struct archive *_a, int code) { int r1, r2, slots, i; char str[10]; struct archive_read *a = (struct archive_read *)_a; if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK)) return r1; r1 = r2 = (ARCHIVE_OK); if (a->format) r2 = (ARCHIVE_WARN); switch (code & ARCHIVE_FORMAT_BASE_MASK) { case ARCHIVE_FORMAT_7ZIP: strcpy(str, "7zip"); break; case ARCHIVE_FORMAT_AR: strcpy(str, "ar"); break; case ARCHIVE_FORMAT_CAB: strcpy(str, "cab"); break; case ARCHIVE_FORMAT_CPIO: strcpy(str, "cpio"); break; + case ARCHIVE_FORMAT_EMPTY: + strcpy(str, "empty"); + break; case ARCHIVE_FORMAT_ISO9660: strcpy(str, "iso9660"); break; case ARCHIVE_FORMAT_LHA: strcpy(str, "lha"); break; case ARCHIVE_FORMAT_MTREE: strcpy(str, "mtree"); break; case ARCHIVE_FORMAT_RAR: strcpy(str, "rar"); break; case ARCHIVE_FORMAT_RAR_V5: strcpy(str, "rar5"); break; + case ARCHIVE_FORMAT_RAW: + strcpy(str, "raw"); + break; case ARCHIVE_FORMAT_TAR: strcpy(str, "tar"); + break; + case ARCHIVE_FORMAT_WARC: + strcpy(str, "warc"); break; case ARCHIVE_FORMAT_XAR: strcpy(str, "xar"); break; case ARCHIVE_FORMAT_ZIP: strcpy(str, "zip"); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Invalid format code specified"); return (ARCHIVE_FATAL); } slots = sizeof(a->formats) / sizeof(a->formats[0]); a->format = &(a->formats[0]); for (i = 0; i < slots; i++, a->format++) { if (!a->format->name || !strcmp(a->format->name, str)) break; } if (!a->format->name || strcmp(a->format->name, str)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: Unable to set format"); r1 = (ARCHIVE_FATAL); } return (r1 < r2) ? r1 : r2; } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_filter_by_code.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_filter_by_code.c (nonexistent) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_filter_by_code.c (revision 368707) @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2020 Martin Matuska + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#include "archive.h" +#include "archive_private.h" + +int +archive_read_support_filter_by_code(struct archive *a, int filter_code) +{ + archive_check_magic(a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_filter_by_code"); + + switch (filter_code) { + case ARCHIVE_FILTER_NONE: + return archive_read_support_filter_none(a); + break; + case ARCHIVE_FILTER_GZIP: + return archive_read_support_filter_gzip(a); + break; + case ARCHIVE_FILTER_BZIP2: + return archive_read_support_filter_bzip2(a); + break; + case ARCHIVE_FILTER_COMPRESS: + return archive_read_support_filter_compress(a); + break; + case ARCHIVE_FILTER_LZMA: + return archive_read_support_filter_lzma(a); + break; + case ARCHIVE_FILTER_XZ: + return archive_read_support_filter_xz(a); + break; + case ARCHIVE_FILTER_UU: + return archive_read_support_filter_uu(a); + break; + case ARCHIVE_FILTER_RPM: + return archive_read_support_filter_rpm(a); + break; + case ARCHIVE_FILTER_LZIP: + return archive_read_support_filter_lzip(a); + break; + case ARCHIVE_FILTER_LRZIP: + return archive_read_support_filter_lrzip(a); + break; + case ARCHIVE_FILTER_LZOP: + return archive_read_support_filter_lzop(a); + break; + case ARCHIVE_FILTER_GRZIP: + return archive_read_support_filter_grzip(a); + break; + case ARCHIVE_FILTER_LZ4: + return archive_read_support_filter_lz4(a); + break; + case ARCHIVE_FILTER_ZSTD: + return archive_read_support_filter_zstd(a); + break; + } + return (ARCHIVE_FATAL); +} Property changes on: stable/11/contrib/libarchive/libarchive/archive_read_support_filter_by_code.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_by_code.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_by_code.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_by_code.c (revision 368707) @@ -1,77 +1,92 @@ /*- * Copyright (c) 2003-2011 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); +#ifdef HAVE_ERRNO_H +#include +#endif + #include "archive.h" #include "archive_private.h" int archive_read_support_format_by_code(struct archive *a, int format_code) { archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_by_code"); switch (format_code & ARCHIVE_FORMAT_BASE_MASK) { case ARCHIVE_FORMAT_7ZIP: return archive_read_support_format_7zip(a); break; case ARCHIVE_FORMAT_AR: return archive_read_support_format_ar(a); break; case ARCHIVE_FORMAT_CAB: return archive_read_support_format_cab(a); break; case ARCHIVE_FORMAT_CPIO: return archive_read_support_format_cpio(a); break; + case ARCHIVE_FORMAT_EMPTY: + return archive_read_support_format_empty(a); + break; case ARCHIVE_FORMAT_ISO9660: return archive_read_support_format_iso9660(a); break; case ARCHIVE_FORMAT_LHA: return archive_read_support_format_lha(a); break; case ARCHIVE_FORMAT_MTREE: return archive_read_support_format_mtree(a); break; case ARCHIVE_FORMAT_RAR: return archive_read_support_format_rar(a); break; case ARCHIVE_FORMAT_RAR_V5: return archive_read_support_format_rar5(a); break; + case ARCHIVE_FORMAT_RAW: + return archive_read_support_format_raw(a); + break; case ARCHIVE_FORMAT_TAR: return archive_read_support_format_tar(a); break; + case ARCHIVE_FORMAT_WARC: + return archive_read_support_format_warc(a); + break; case ARCHIVE_FORMAT_XAR: return archive_read_support_format_xar(a); break; case ARCHIVE_FORMAT_ZIP: return archive_read_support_format_zip(a); break; } + archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER, + "Invalid format code specified"); return (ARCHIVE_FATAL); } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_cab.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_cab.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_cab.c (revision 368707) @@ -1,3227 +1,3227 @@ /*- * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" struct lzx_dec { /* Decoding status. */ int state; /* * Window to see last decoded data, from 32KBi to 2MBi. */ int w_size; int w_mask; /* Window buffer, which is a loop buffer. */ unsigned char *w_buff; /* The insert position to the window. */ int w_pos; /* The position where we can copy decoded code from the window. */ int copy_pos; /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; /* Translation reversal for x86 processor CALL byte sequence(E8). * This is used for LZX only. */ uint32_t translation_size; char translation; char block_type; #define VERBATIM_BLOCK 1 #define ALIGNED_OFFSET_BLOCK 2 #define UNCOMPRESSED_BLOCK 3 size_t block_size; size_t block_bytes_avail; /* Repeated offset. */ int r0, r1, r2; unsigned char rbytes[4]; int rbytes_avail; int length_header; int position_slot; int offset_bits; struct lzx_pos_tbl { int base; int footer_bits; } *pos_tbl; /* * Bit stream reader. */ struct lzx_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; unsigned char odd; char have_odd; } br; /* * Huffman coding. */ struct huffman { int len_size; int freq[17]; unsigned char *bitlen; /* * Use a index table. It's faster than searching a huffman * coding tree, which is a binary tree. But a use of a large * index table causes L1 cache read miss many times. */ int max_bits; int tbl_bits; int tree_used; /* Direct access table. */ uint16_t *tbl; } at, lt, mt, pt; int loop; int error; }; static const int slots[] = { 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 }; #define SLOT_BASE 15 #define SLOT_MAX 21/*->25*/ struct lzx_stream { const unsigned char *next_in; int64_t avail_in; int64_t total_in; unsigned char *next_out; int64_t avail_out; int64_t total_out; struct lzx_dec *ds; }; /* * Cabinet file definitions. */ /* CFHEADER offset */ #define CFHEADER_signature 0 #define CFHEADER_cbCabinet 8 #define CFHEADER_coffFiles 16 #define CFHEADER_versionMinor 24 #define CFHEADER_versionMajor 25 #define CFHEADER_cFolders 26 #define CFHEADER_cFiles 28 #define CFHEADER_flags 30 #define CFHEADER_setID 32 #define CFHEADER_iCabinet 34 #define CFHEADER_cbCFHeader 36 #define CFHEADER_cbCFFolder 38 #define CFHEADER_cbCFData 39 /* CFFOLDER offset */ #define CFFOLDER_coffCabStart 0 #define CFFOLDER_cCFData 4 #define CFFOLDER_typeCompress 6 #define CFFOLDER_abReserve 8 /* CFFILE offset */ #define CFFILE_cbFile 0 #define CFFILE_uoffFolderStart 4 #define CFFILE_iFolder 8 #define CFFILE_date_time 10 #define CFFILE_attribs 14 /* CFDATA offset */ #define CFDATA_csum 0 #define CFDATA_cbData 4 #define CFDATA_cbUncomp 6 static const char * const compression_name[] = { "NONE", "MSZIP", "Quantum", "LZX", }; struct cfdata { /* Sum value of this CFDATA. */ uint32_t sum; uint16_t compressed_size; uint16_t compressed_bytes_remaining; uint16_t uncompressed_size; uint16_t uncompressed_bytes_remaining; /* To know how many bytes we have decompressed. */ uint16_t uncompressed_avail; /* Offset from the beginning of compressed data of this CFDATA */ uint16_t read_offset; int64_t unconsumed; /* To keep memory image of this CFDATA to compute the sum. */ size_t memimage_size; unsigned char *memimage; /* Result of calculation of sum. */ uint32_t sum_calculated; unsigned char sum_extra[4]; int sum_extra_avail; const void *sum_ptr; }; struct cffolder { uint32_t cfdata_offset_in_cab; uint16_t cfdata_count; uint16_t comptype; #define COMPTYPE_NONE 0x0000 #define COMPTYPE_MSZIP 0x0001 #define COMPTYPE_QUANTUM 0x0002 #define COMPTYPE_LZX 0x0003 uint16_t compdata; const char *compname; /* At the time reading CFDATA */ struct cfdata cfdata; int cfdata_index; /* Flags to mark progress of decompression. */ char decompress_init; }; struct cffile { uint32_t uncompressed_size; uint32_t offset; time_t mtime; uint16_t folder; #define iFoldCONTINUED_FROM_PREV 0xFFFD #define iFoldCONTINUED_TO_NEXT 0xFFFE #define iFoldCONTINUED_PREV_AND_NEXT 0xFFFF unsigned char attr; #define ATTR_RDONLY 0x01 #define ATTR_NAME_IS_UTF 0x80 struct archive_string pathname; }; struct cfheader { /* Total bytes of all file size in a Cabinet. */ uint32_t total_bytes; uint32_t files_offset; uint16_t folder_count; uint16_t file_count; uint16_t flags; #define PREV_CABINET 0x0001 #define NEXT_CABINET 0x0002 #define RESERVE_PRESENT 0x0004 uint16_t setid; uint16_t cabinet; /* Version number. */ unsigned char major; unsigned char minor; unsigned char cffolder; unsigned char cfdata; /* All folders in a cabinet. */ struct cffolder *folder_array; /* All files in a cabinet. */ struct cffile *file_array; int file_index; }; struct cab { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; struct cffolder *entry_cffolder; struct cffile *entry_cffile; struct cfdata *entry_cfdata; /* Offset from beginning of a cabinet file. */ int64_t cab_offset; struct cfheader cfheader; struct archive_wstring ws; /* Flag to mark progress that an archive was read their first header.*/ char found_header; char end_of_archive; char end_of_entry; char end_of_entry_cleanup; char read_data_invoked; int64_t bytes_skipped; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; int init_default_conversion; struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; char format_name[64]; #ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif struct lzx_stream xstrm; }; static int archive_read_format_cab_bid(struct archive_read *, int); static int archive_read_format_cab_options(struct archive_read *, const char *, const char *); static int archive_read_format_cab_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cab_read_data_skip(struct archive_read *); static int archive_read_format_cab_cleanup(struct archive_read *); static int cab_skip_sfx(struct archive_read *); static time_t cab_dos_time(const unsigned char *); static int cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int cab_read_header(struct archive_read *); static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t); static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t); static void cab_checksum_update(struct archive_read *, size_t); static int cab_checksum_finish(struct archive_read *); static int cab_next_cfdata(struct archive_read *); static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_deflate(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_lzx(struct archive_read *, ssize_t *); static int64_t cab_consume_cfdata(struct archive_read *, int64_t); static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t); static int lzx_decode_init(struct lzx_stream *, int); static int lzx_read_blocks(struct lzx_stream *, int); static int lzx_decode_blocks(struct lzx_stream *, int); static void lzx_decode_free(struct lzx_stream *); static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t); static void lzx_cleanup_bitstream(struct lzx_stream *); static int lzx_decode(struct lzx_stream *, int); static int lzx_read_pre_tree(struct lzx_stream *); static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int); static int lzx_huffman_init(struct huffman *, size_t, int); static void lzx_huffman_free(struct huffman *); static int lzx_make_huffman_table(struct huffman *); static inline int lzx_decode_huffman(struct huffman *, unsigned); int archive_read_support_format_cab(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cab *cab; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cab"); cab = (struct cab *)calloc(1, sizeof(*cab)); if (cab == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate CAB data"); return (ARCHIVE_FATAL); } archive_string_init(&cab->ws); archive_wstring_ensure(&cab->ws, 256); r = __archive_read_register_format(a, cab, "cab", archive_read_format_cab_bid, archive_read_format_cab_options, archive_read_format_cab_read_header, archive_read_format_cab_read_data, archive_read_format_cab_read_data_skip, NULL, archive_read_format_cab_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cab); return (ARCHIVE_OK); } static int find_cab_magic(const char *p) { switch (p[4]) { case 0: /* * Note: Self-Extraction program has 'MSCF' string in their * program. If we were finding 'MSCF' string only, we got * wrong place for Cabinet header, thus, we have to check * following four bytes which are reserved and must be set * to zero. */ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) return 0; return 5; case 'F': return 1; case 'C': return 2; case 'S': return 3; case 'M': return 4; default: return 5; } } static int archive_read_format_cab_bid(struct archive_read *a, int best_bid) { const char *p; ssize_t bytes_avail, offset, window; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 64) return (-1); if ((p = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) return (64); /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward * up to 128k for a 'MSCF' marker. */ if (p[0] == 'M' && p[1] == 'Z') { offset = 0; window = 4096; while (offset < (1024 * 128)) { const char *h = __archive_read_ahead(a, offset + window, &bytes_avail); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 128) return (0); continue; } p = h + offset; while (p + 8 < h + bytes_avail) { int next; if ((next = find_cab_magic(p)) == 0) return (64); p += next; } offset = p - h; } } return (0); } static int archive_read_format_cab_options(struct archive_read *a, const char *key, const char *val) { struct cab *cab; int ret = ARCHIVE_FAILED; cab = (struct cab *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "cab: hdrcharset option needs a character-set name"); else { cab->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cab->sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int cab_skip_sfx(struct archive_read *a) { const char *p, *q; size_t skip; ssize_t bytes, window; window = 4096; for (;;) { const char *h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining size are less than window. */ window >>= 1; if (window < 128) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } continue; } p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the cab header. */ while (p + 8 < q) { int next; if ((next = find_cab_magic(p)) == 0) { skip = p - h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += next; } skip = p - h; __archive_read_consume(a, skip); } } static int truncated_error(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB header"); return (ARCHIVE_FATAL); } static ssize_t cab_strnlen(const unsigned char *p, size_t maxlen) { size_t i; for (i = 0; i <= maxlen; i++) { if (p[i] == 0) break; } if (i > maxlen) return (-1);/* invalid */ return ((ssize_t)i); } /* Read bytes as much as remaining. */ static const void * cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail) { const void *p; while (min > 0) { p = __archive_read_ahead(a, min, avail); if (p != NULL) return (p); min--; } return (NULL); } /* Convert a path separator '\' -> '/' */ static int cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr) { size_t i; int mb; /* Easy check if we have '\' in multi-byte string. */ mb = 0; for (i = 0; i < archive_strlen(fn); i++) { if (fn->s[i] == '\\') { if (mb) { /* This may be second byte of multi-byte * character. */ break; } fn->s[i] = '/'; mb = 0; } else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF)) mb = 1; else mb = 0; } if (i == archive_strlen(fn)) return (0); return (-1); } /* * Replace a character '\' with '/' in wide character. */ static void cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry) { const wchar_t *wp; size_t i; /* If a conversion to wide character failed, force the replacement. */ if ((wp = archive_entry_pathname_w(entry)) != NULL) { archive_wstrcpy(&(cab->ws), wp); for (i = 0; i < archive_strlen(&(cab->ws)); i++) { if (cab->ws.s[i] == L'\\') cab->ws.s[i] = L'/'; } archive_entry_copy_pathname_w(entry, cab->ws.s); } } /* * Read CFHEADER, CFFOLDER and CFFILE. */ static int cab_read_header(struct archive_read *a) { const unsigned char *p; struct cab *cab; struct cfheader *hd; size_t bytes, used; ssize_t len; int64_t skip; int err, i; int cur_folder, prev_folder; uint32_t offset32; a->archive.archive_format = ARCHIVE_FORMAT_CAB; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "CAB"; if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); cab = (struct cab *)(a->format->data); if (cab->found_header == 0 && p[0] == 'M' && p[1] == 'Z') { /* This is an executable? Must be self-extracting... */ err = cab_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); /* Re-read header after processing the SFX. */ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); } cab->cab_offset = 0; /* * Read CFHEADER. */ hd = &cab->cfheader; if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' || p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet); hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles); hd->minor = p[CFHEADER_versionMinor]; hd->major = p[CFHEADER_versionMajor]; hd->folder_count = archive_le16dec(p + CFHEADER_cFolders); if (hd->folder_count == 0) goto invalid; hd->file_count = archive_le16dec(p + CFHEADER_cFiles); if (hd->file_count == 0) goto invalid; hd->flags = archive_le16dec(p + CFHEADER_flags); hd->setid = archive_le16dec(p + CFHEADER_setID); hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet); used = CFHEADER_iCabinet + 2; if (hd->flags & RESERVE_PRESENT) { uint16_t cfheader; cfheader = archive_le16dec(p + CFHEADER_cbCFHeader); if (cfheader > 60000U) goto invalid; hd->cffolder = p[CFHEADER_cbCFFolder]; hd->cfdata = p[CFHEADER_cbCFData]; used += 4;/* cbCFHeader, cbCFFolder and cbCFData */ used += cfheader;/* abReserve */ } else hd->cffolder = 0;/* Avoid compiling warning. */ if (hd->flags & PREV_CABINET) { /* How many bytes are used for szCabinetPrev. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskPrev. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; } if (hd->flags & NEXT_CABINET) { /* How many bytes are used for szCabinetNext. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskNext. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; } __archive_read_consume(a, used); cab->cab_offset += used; used = 0; /* * Read CFFOLDER. */ hd->folder_array = (struct cffolder *)calloc( hd->folder_count, sizeof(struct cffolder)); if (hd->folder_array == NULL) goto nomem; bytes = 8; if (hd->flags & RESERVE_PRESENT) bytes += hd->cffolder; bytes *= hd->folder_count; if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL) return (truncated_error(a)); offset32 = 0; for (i = 0; i < hd->folder_count; i++) { struct cffolder *folder = &(hd->folder_array[i]); folder->cfdata_offset_in_cab = archive_le32dec(p + CFFOLDER_coffCabStart); folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData); folder->comptype = archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F; folder->compdata = archive_le16dec(p+CFFOLDER_typeCompress) >> 8; /* Get a compression name. */ if (folder->comptype < sizeof(compression_name) / sizeof(compression_name[0])) folder->compname = compression_name[folder->comptype]; else folder->compname = "UNKNOWN"; p += 8; used += 8; if (hd->flags & RESERVE_PRESENT) { p += hd->cffolder;/* abReserve */ used += hd->cffolder; } /* * Sanity check if each data is acceptable. */ if (offset32 >= folder->cfdata_offset_in_cab) goto invalid; offset32 = folder->cfdata_offset_in_cab; /* Set a request to initialize zlib for the CFDATA of * this folder. */ folder->decompress_init = 0; } __archive_read_consume(a, used); cab->cab_offset += used; /* * Read CFFILE. */ /* Seek read pointer to the offset of CFFILE if needed. */ skip = (int64_t)hd->files_offset - cab->cab_offset; if (skip < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFFILE %jd < %jd", (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip) { __archive_read_consume(a, skip); cab->cab_offset += skip; } /* Allocate memory for CFDATA */ hd->file_array = (struct cffile *)calloc( hd->file_count, sizeof(struct cffile)); if (hd->file_array == NULL) goto nomem; prev_folder = -1; for (i = 0; i < hd->file_count; i++) { struct cffile *file = &(hd->file_array[i]); ssize_t avail; if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) return (truncated_error(a)); file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile); file->offset = archive_le32dec(p + CFFILE_uoffFolderStart); file->folder = archive_le16dec(p + CFFILE_iFolder); file->mtime = cab_dos_time(p + CFFILE_date_time); file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs); __archive_read_consume(a, 16); cab->cab_offset += 16; if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p, avail-1)) <= 0) goto invalid; /* Copy a pathname. */ archive_string_init(&(file->pathname)); archive_strncpy(&(file->pathname), p, len); __archive_read_consume(a, len + 1); cab->cab_offset += len + 1; /* * Sanity check if each data is acceptable. */ if (file->uncompressed_size > 0x7FFF8000) goto invalid;/* Too large */ if ((int64_t)file->offset + (int64_t)file->uncompressed_size > ARCHIVE_LITERAL_LL(0x7FFF8000)) goto invalid;/* Too large */ switch (file->folder) { case iFoldCONTINUED_TO_NEXT: /* This must be last file in a folder. */ if (i != hd->file_count -1) goto invalid; cur_folder = hd->folder_count -1; break; case iFoldCONTINUED_PREV_AND_NEXT: /* This must be only one file in a folder. */ if (hd->file_count != 1) goto invalid; /* FALL THROUGH */ case iFoldCONTINUED_FROM_PREV: /* This must be first file in a folder. */ if (i != 0) goto invalid; prev_folder = cur_folder = 0; offset32 = file->offset; break; default: if (file->folder >= hd->folder_count) goto invalid; cur_folder = file->folder; break; } /* Dot not back track. */ if (cur_folder < prev_folder) goto invalid; if (cur_folder != prev_folder) offset32 = 0; prev_folder = cur_folder; /* Make sure there are not any blanks from last file * contents. */ if (offset32 != file->offset) goto invalid; offset32 += file->uncompressed_size; /* CFDATA is available for file contents. */ if (file->uncompressed_size > 0 && hd->folder_array[cur_folder].cfdata_count == 0) goto invalid; } if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Multivolume cabinet file is unsupported"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CAB header"); return (ARCHIVE_FATAL); nomem: archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for CAB data"); return (ARCHIVE_FATAL); } static int archive_read_format_cab_read_header(struct archive_read *a, struct archive_entry *entry) { struct cab *cab; struct cfheader *hd; struct cffolder *prev_folder; struct cffile *file; struct archive_string_conv *sconv; int err = ARCHIVE_OK, r; cab = (struct cab *)(a->format->data); if (cab->found_header == 0) { err = cab_read_header(a); if (err < ARCHIVE_WARN) return (err); /* We've found the header. */ cab->found_header = 1; } hd = &cab->cfheader; if (hd->file_index >= hd->file_count) { cab->end_of_archive = 1; return (ARCHIVE_EOF); } file = &hd->file_array[hd->file_index++]; cab->end_of_entry = 0; cab->end_of_entry_cleanup = 0; cab->entry_compressed_bytes_read = 0; cab->entry_uncompressed_bytes_read = 0; cab->entry_unconsumed = 0; cab->entry_cffile = file; /* * Choose a proper folder. */ prev_folder = cab->entry_cffolder; switch (file->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_PREV_AND_NEXT: cab->entry_cffolder = &hd->folder_array[0]; break; case iFoldCONTINUED_TO_NEXT: cab->entry_cffolder = &hd->folder_array[hd->folder_count-1]; break; default: cab->entry_cffolder = &hd->folder_array[file->folder]; break; } /* If a cffolder of this file is changed, reset a cfdata to read * file contents from next cfdata. */ if (prev_folder != cab->entry_cffolder) cab->entry_cfdata = NULL; /* If a pathname is UTF-8, prepare a string conversion object * for UTF-8 and use it. */ if (file->attr & ATTR_NAME_IS_UTF) { if (cab->sconv_utf8 == NULL) { cab->sconv_utf8 = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (cab->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = cab->sconv_utf8; } else if (cab->sconv != NULL) { /* Choose the conversion specified by the option. */ sconv = cab->sconv; } else { /* Choose the default conversion. */ if (!cab->init_default_conversion) { cab->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cab->init_default_conversion = 1; } sconv = cab->sconv_default; } /* * Set a default value and common data */ r = cab_convert_path_separator_1(&(file->pathname), file->attr); if (archive_entry_copy_pathname_l(entry, file->pathname.s, archive_strlen(&(file->pathname)), sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(sconv)); err = ARCHIVE_WARN; } if (r < 0) { /* Convert a path separator '\' -> '/' */ cab_convert_path_separator_2(cab, entry); } archive_entry_set_size(entry, file->uncompressed_size); if (file->attr & ATTR_RDONLY) archive_entry_set_mode(entry, AE_IFREG | 0555); else archive_entry_set_mode(entry, AE_IFREG | 0666); archive_entry_set_mtime(entry, file->mtime, 0); cab->entry_bytes_remaining = file->uncompressed_size; cab->entry_offset = 0; /* We don't need compress data. */ if (file->uncompressed_size == 0) cab->end_of_entry_cleanup = cab->end_of_entry = 1; /* Set up a more descriptive format name. */ sprintf(cab->format_name, "CAB %d.%d (%s)", hd->major, hd->minor, cab->entry_cffolder->compname); a->archive.archive_format_name = cab->format_name; return (err); } static int archive_read_format_cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct cab *cab = (struct cab *)(a->format->data); int r; switch (cab->entry_cffile->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_PREV_AND_NEXT: *buff = NULL; *size = 0; *offset = 0; archive_clear_error(&a->archive); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore this file split in multivolume."); return (ARCHIVE_FAILED); default: break; } if (cab->read_data_invoked == 0) { if (cab->bytes_skipped) { if (cab->entry_cfdata == NULL) { r = cab_next_cfdata(a); if (r < 0) return (r); } if (cab_consume_cfdata(a, cab->bytes_skipped) < 0) return (ARCHIVE_FATAL); cab->bytes_skipped = 0; } cab->read_data_invoked = 1; } if (cab->entry_unconsumed) { /* Consume as much as the compressor actually used. */ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed); cab->entry_unconsumed = 0; if (r < 0) return (r); } if (cab->end_of_archive || cab->end_of_entry) { if (!cab->end_of_entry_cleanup) { /* End-of-entry cleanup done. */ cab->end_of_entry_cleanup = 1; } *offset = cab->entry_offset; *size = 0; *buff = NULL; return (ARCHIVE_EOF); } return (cab_read_data(a, buff, size, offset)); } static uint32_t cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; unsigned u32num; uint32_t sum; u32num = (unsigned)bytes / 4; sum = seed; b = p; for (;u32num > 0; --u32num) { sum ^= archive_le32dec(b); b += 4; } return (sum); } static uint32_t cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; uint32_t sum; uint32_t t; sum = cab_checksum_cfdata_4(p, bytes, seed); b = p; b += bytes & ~3; t = 0; switch (bytes & 3) { case 3: t |= ((uint32_t)(*b++)) << 16; /* FALL THROUGH */ case 2: t |= ((uint32_t)(*b++)) << 8; /* FALL THROUGH */ case 1: t |= *b; /* FALL THROUGH */ default: break; } sum ^= t; return (sum); } static void cab_checksum_update(struct archive_read *a, size_t bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; const unsigned char *p; size_t sumbytes; if (cfdata->sum == 0 || cfdata->sum_ptr == NULL) return; /* * Calculate the sum of this CFDATA. * Make sure CFDATA must be calculated in four bytes. */ p = cfdata->sum_ptr; sumbytes = bytes; if (cfdata->sum_extra_avail) { while (cfdata->sum_extra_avail < 4 && sumbytes > 0) { cfdata->sum_extra[ cfdata->sum_extra_avail++] = *p++; sumbytes--; } if (cfdata->sum_extra_avail == 4) { cfdata->sum_calculated = cab_checksum_cfdata_4( cfdata->sum_extra, 4, cfdata->sum_calculated); cfdata->sum_extra_avail = 0; } } if (sumbytes) { int odd = sumbytes & 3; if (sumbytes - odd > 0) cfdata->sum_calculated = cab_checksum_cfdata_4( p, sumbytes - odd, cfdata->sum_calculated); if (odd) memcpy(cfdata->sum_extra, p + sumbytes - odd, odd); cfdata->sum_extra_avail = odd; } cfdata->sum_ptr = NULL; } static int cab_checksum_finish(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; int l; /* Do not need to compute a sum. */ if (cfdata->sum == 0) return (ARCHIVE_OK); /* * Calculate the sum of remaining CFDATA. */ if (cfdata->sum_extra_avail) { cfdata->sum_calculated = cab_checksum_cfdata(cfdata->sum_extra, cfdata->sum_extra_avail, cfdata->sum_calculated); cfdata->sum_extra_avail = 0; } l = 4; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; cfdata->sum_calculated = cab_checksum_cfdata( cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated); if (cfdata->sum_calculated != cfdata->sum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Checksum error CFDATA[%d] %x:%x in %d bytes", + "Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes", cab->entry_cffolder->cfdata_index -1, cfdata->sum, cfdata->sum_calculated, cfdata->compressed_size); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } /* * Read CFDATA if needed. */ static int cab_next_cfdata(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; /* There are remaining bytes in current CFDATA, use it first. */ if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0) return (ARCHIVE_OK); if (cfdata == NULL) { int64_t skip; cab->entry_cffolder->cfdata_index = 0; /* Seek read pointer to the offset of CFDATA if needed. */ skip = cab->entry_cffolder->cfdata_offset_in_cab - cab->cab_offset; if (skip < 0) { int folder_index; switch (cab->entry_cffile->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_PREV_AND_NEXT: folder_index = 0; break; case iFoldCONTINUED_TO_NEXT: folder_index = cab->cfheader.folder_count-1; break; default: folder_index = cab->entry_cffile->folder; break; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFDATA in folder(%d) %jd < %jd", folder_index, (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip > 0) { if (__archive_read_consume(a, skip) < 0) return (ARCHIVE_FATAL); cab->cab_offset = cab->entry_cffolder->cfdata_offset_in_cab; } } /* * Read a CFDATA. */ if (cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) { const unsigned char *p; int l; cfdata = &(cab->entry_cffolder->cfdata); cab->entry_cffolder->cfdata_index++; cab->entry_cfdata = cfdata; cfdata->sum_calculated = 0; cfdata->sum_extra_avail = 0; cfdata->sum_ptr = NULL; l = 8; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; if ((p = __archive_read_ahead(a, l, NULL)) == NULL) return (truncated_error(a)); cfdata->sum = archive_le32dec(p + CFDATA_csum); cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData); cfdata->compressed_bytes_remaining = cfdata->compressed_size; cfdata->uncompressed_size = archive_le16dec(p + CFDATA_cbUncomp); cfdata->uncompressed_bytes_remaining = cfdata->uncompressed_size; cfdata->uncompressed_avail = 0; cfdata->read_offset = 0; cfdata->unconsumed = 0; /* * Sanity check if data size is acceptable. */ if (cfdata->compressed_size == 0 || cfdata->compressed_size > (0x8000+6144)) goto invalid; if (cfdata->uncompressed_size > 0x8000) goto invalid; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: break; case iFoldCONTINUED_FROM_PREV: default: goto invalid; } } /* If CFDATA is not last in a folder, an uncompressed * size must be 0x8000(32KBi) */ if ((cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) && cfdata->uncompressed_size != 0x8000) goto invalid; /* A compressed data size and an uncompressed data size must * be the same in no compression mode. */ if (cab->entry_cffolder->comptype == COMPTYPE_NONE && cfdata->compressed_size != cfdata->uncompressed_size) goto invalid; /* * Save CFDATA image for sum check. */ if (cfdata->memimage_size < (size_t)l) { free(cfdata->memimage); cfdata->memimage = malloc(l); if (cfdata->memimage == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for CAB data"); return (ARCHIVE_FATAL); } cfdata->memimage_size = l; } memcpy(cfdata->memimage, p, l); /* Consume bytes as much as we used. */ __archive_read_consume(a, l); cab->cab_offset += l; } else if (cab->entry_cffolder->cfdata_count > 0) { /* Run out of all CFDATA in a folder. */ cfdata->compressed_size = 0; cfdata->uncompressed_size = 0; cfdata->compressed_bytes_remaining = 0; cfdata->uncompressed_bytes_remaining = 0; } else { /* Current folder does not have any CFDATA. */ cfdata = &(cab->entry_cffolder->cfdata); cab->entry_cfdata = cfdata; memset(cfdata, 0, sizeof(*cfdata)); } return (ARCHIVE_OK); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } /* * Read ahead CFDATA. */ static const void * cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); int err; err = cab_next_cfdata(a); if (err < ARCHIVE_OK) { *avail = err; return (NULL); } switch (cab->entry_cffolder->comptype) { case COMPTYPE_NONE: return (cab_read_ahead_cfdata_none(a, avail)); case COMPTYPE_MSZIP: return (cab_read_ahead_cfdata_deflate(a, avail)); case COMPTYPE_LZX: return (cab_read_ahead_cfdata_lzx(a, avail)); default: /* Unsupported compression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported CAB compression : %s", cab->entry_cffolder->compname); *avail = ARCHIVE_FAILED; return (NULL); } } /* * Read ahead CFDATA as uncompressed data. */ static const void * cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; cfdata = cab->entry_cfdata; /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ d = __archive_read_ahead(a, 1, avail); if (*avail <= 0) { *avail = truncated_error(a); return (NULL); } if (*avail > cfdata->uncompressed_bytes_remaining) *avail = cfdata->uncompressed_bytes_remaining; cfdata->uncompressed_avail = cfdata->uncompressed_size; cfdata->unconsumed = *avail; cfdata->sum_ptr = d; return (d); } /* * Read ahead CFDATA as deflate data. */ #ifdef HAVE_ZLIB_H static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; int r, mszip; uint16_t uavail; char eod = 0; cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { cab->uncompressed_buffer_size = 0x8000; cab->uncompressed_buffer = (unsigned char *)malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for CAB reader"); *avail = ARCHIVE_FATAL; return (NULL); } } uavail = cfdata->uncompressed_avail; if (uavail == cfdata->uncompressed_size) { d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; return (d); } if (!cab->entry_cffolder->decompress_init) { cab->stream.next_in = NULL; cab->stream.avail_in = 0; cab->stream.total_in = 0; cab->stream.next_out = NULL; cab->stream.avail_out = 0; cab->stream.total_out = 0; if (cab->stream_valid) r = inflateReset(&cab->stream); else r = inflateInit2(&cab->stream, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize deflate decompression."); *avail = ARCHIVE_FATAL; return (NULL); } /* Stream structure has been set up. */ cab->stream_valid = 1; /* We've initialized decompression for this stream. */ cab->entry_cffolder->decompress_init = 1; } if (cfdata->compressed_bytes_remaining == cfdata->compressed_size) mszip = 2; else mszip = 0; eod = 0; cab->stream.total_out = uavail; /* * We always uncompress all data in current CFDATA. */ while (!eod && cab->stream.total_out < cfdata->uncompressed_size) { ssize_t bytes_avail; cab->stream.next_out = cab->uncompressed_buffer + cab->stream.total_out; cab->stream.avail_out = cfdata->uncompressed_size - cab->stream.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } if (bytes_avail > cfdata->compressed_bytes_remaining) bytes_avail = cfdata->compressed_bytes_remaining; /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the * next_in pointer, only reads it). The result: this ugly * cast to remove 'const'. */ cab->stream.next_in = (Bytef *)(uintptr_t)d; cab->stream.avail_in = (uInt)bytes_avail; cab->stream.total_in = 0; /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */ if (mszip > 0) { if (bytes_avail <= 0) goto nomszip; if (bytes_avail <= mszip) { if (mszip == 2) { if (cab->stream.next_in[0] != 0x43) goto nomszip; if (bytes_avail > 1 && cab->stream.next_in[1] != 0x4b) goto nomszip; } else if (cab->stream.next_in[0] != 0x4b) goto nomszip; cfdata->unconsumed = bytes_avail; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata( a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } mszip -= (int)bytes_avail; continue; } if (mszip == 1 && cab->stream.next_in[0] != 0x4b) goto nomszip; else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 || cab->stream.next_in[1] != 0x4b)) goto nomszip; cab->stream.next_in += mszip; cab->stream.avail_in -= mszip; cab->stream.total_in += mszip; mszip = 0; } r = inflate(&cab->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: eod = 1; break; default: goto zlibfailed; } cfdata->unconsumed = cab->stream.total_in; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } uavail = (uint16_t)cab->stream.total_out; if (uavail < cfdata->uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid uncompressed size (%d < %d)", uavail, cfdata->uncompressed_size); *avail = ARCHIVE_FATAL; return (NULL); } /* * Note: I suspect there is a bug in makecab.exe because, in rare * case, compressed bytes are still remaining regardless we have * gotten all uncompressed bytes, which size is recorded in CFDATA, * as much as we need, and we have to use the garbage so as to * correctly compute the sum of CFDATA accordingly. */ if (cfdata->compressed_bytes_remaining > 0) { ssize_t bytes_avail; d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } cfdata->unconsumed = cfdata->compressed_bytes_remaining; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } /* * Set dictionary data for decompressing of next CFDATA, which * in the same folder. This is why we always do decompress CFDATA * even if beginning CFDATA or some of CFDATA are not used in * skipping file data. */ if (cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) { r = inflateReset(&cab->stream); if (r != Z_OK) goto zlibfailed; r = inflateSetDictionary(&cab->stream, cab->uncompressed_buffer, cfdata->uncompressed_size); if (r != Z_OK) goto zlibfailed; } d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; cfdata->uncompressed_avail = uavail; return (d); zlibfailed: switch (r) { case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for deflate decompression"); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Deflate decompression failed (%d)", r); break; } *avail = ARCHIVE_FATAL; return (NULL); nomszip: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "CFDATA incorrect(no MSZIP signature)"); *avail = ARCHIVE_FATAL; return (NULL); } #else /* HAVE_ZLIB_H */ static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { *avail = ARCHIVE_FATAL; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "libarchive compiled without deflate support (no libz)"); return (NULL); } #endif /* HAVE_ZLIB_H */ static const void * cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; int r; uint16_t uavail; cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { cab->uncompressed_buffer_size = 0x8000; cab->uncompressed_buffer = (unsigned char *)malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for CAB reader"); *avail = ARCHIVE_FATAL; return (NULL); } } uavail = cfdata->uncompressed_avail; if (uavail == cfdata->uncompressed_size) { d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; return (d); } if (!cab->entry_cffolder->decompress_init) { r = lzx_decode_init(&cab->xstrm, cab->entry_cffolder->compdata); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize LZX decompression."); *avail = ARCHIVE_FATAL; return (NULL); } /* We've initialized decompression for this stream. */ cab->entry_cffolder->decompress_init = 1; } /* Clean up remaining bits of previous CFDATA. */ lzx_cleanup_bitstream(&cab->xstrm); cab->xstrm.total_out = uavail; while (cab->xstrm.total_out < cfdata->uncompressed_size) { ssize_t bytes_avail; cab->xstrm.next_out = cab->uncompressed_buffer + cab->xstrm.total_out; cab->xstrm.avail_out = cfdata->uncompressed_size - cab->xstrm.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB file data"); *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_avail > cfdata->compressed_bytes_remaining) bytes_avail = cfdata->compressed_bytes_remaining; cab->xstrm.next_in = d; cab->xstrm.avail_in = bytes_avail; cab->xstrm.total_in = 0; r = lzx_decode(&cab->xstrm, cfdata->compressed_bytes_remaining == bytes_avail); switch (r) { case ARCHIVE_OK: case ARCHIVE_EOF: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LZX decompression failed (%d)", r); *avail = ARCHIVE_FATAL; return (NULL); } cfdata->unconsumed = cab->xstrm.total_in; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } uavail = (uint16_t)cab->xstrm.total_out; /* * Make sure a read pointer advances to next CFDATA. */ if (cfdata->compressed_bytes_remaining > 0) { ssize_t bytes_avail; d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } cfdata->unconsumed = cfdata->compressed_bytes_remaining; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } /* * Translation reversal of x86 processor CALL byte sequence(E8). */ lzx_translation(&cab->xstrm, cab->uncompressed_buffer, cfdata->uncompressed_size, (cab->entry_cffolder->cfdata_index-1) * 0x8000); d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; cfdata->uncompressed_avail = uavail; return (d); } /* * Consume CFDATA. * We always decompress CFDATA to consume CFDATA as much as we need * in uncompressed bytes because all CFDATA in a folder are related * so we do not skip any CFDATA without decompressing. * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for * the CFFILE is remaining bytes of previous Multivolume CAB file. */ static int64_t cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; int64_t cbytes, rbytes; int err; rbytes = cab_minimum_consume_cfdata(a, consumed_bytes); if (rbytes < 0) return (ARCHIVE_FATAL); cfdata = cab->entry_cfdata; while (rbytes > 0) { ssize_t avail; if (cfdata->compressed_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } cbytes = cfdata->uncompressed_bytes_remaining; if (cbytes > rbytes) cbytes = rbytes; rbytes -= cbytes; if (cfdata->uncompressed_avail == 0 && (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT || cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) { /* We have not read any data yet. */ if (cbytes == cfdata->uncompressed_bytes_remaining) { /* Skip whole current CFDATA. */ __archive_read_consume(a, cfdata->compressed_size); cab->cab_offset += cfdata->compressed_size; cfdata->compressed_bytes_remaining = 0; cfdata->uncompressed_bytes_remaining = 0; err = cab_next_cfdata(a); if (err < 0) return (err); cfdata = cab->entry_cfdata; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_FROM_PREV: rbytes = 0; break; default: break; } } continue; } cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; break; } else if (cbytes == 0) { err = cab_next_cfdata(a); if (err < 0) return (err); cfdata = cab->entry_cfdata; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_FROM_PREV: return (ARCHIVE_FATAL); default: break; } } continue; } while (cbytes > 0) { (void)cab_read_ahead_cfdata(a, &avail); if (avail <= 0) return (ARCHIVE_FATAL); if (avail > cbytes) avail = (ssize_t)cbytes; if (cab_minimum_consume_cfdata(a, avail) < 0) return (ARCHIVE_FATAL); cbytes -= avail; } } return (consumed_bytes); } /* * Consume CFDATA as much as we have already gotten and * compute the sum of CFDATA. */ static int64_t cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; int64_t cbytes, rbytes; int err; cfdata = cab->entry_cfdata; rbytes = consumed_bytes; if (cab->entry_cffolder->comptype == COMPTYPE_NONE) { if (consumed_bytes < cfdata->unconsumed) cbytes = consumed_bytes; else cbytes = cfdata->unconsumed; rbytes -= cbytes; cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; cfdata->unconsumed -= cbytes; } else { cbytes = cfdata->uncompressed_avail - cfdata->read_offset; if (cbytes > 0) { if (consumed_bytes < cbytes) cbytes = consumed_bytes; rbytes -= cbytes; cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; } if (cfdata->unconsumed) { cbytes = cfdata->unconsumed; cfdata->unconsumed = 0; } else cbytes = 0; } if (cbytes) { /* Compute the sum. */ cab_checksum_update(a, (size_t)cbytes); /* Consume as much as the compressor actually used. */ __archive_read_consume(a, cbytes); cab->cab_offset += cbytes; cfdata->compressed_bytes_remaining -= (uint16_t)cbytes; if (cfdata->compressed_bytes_remaining == 0) { err = cab_checksum_finish(a); if (err < 0) return (err); } } return (rbytes); } /* * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * cab->end_of_entry if it consumes all of the data. */ static int cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct cab *cab = (struct cab *)(a->format->data); ssize_t bytes_avail; if (cab->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = cab->entry_offset; cab->end_of_entry = 1; return (ARCHIVE_OK); } *buff = cab_read_ahead_cfdata(a, &bytes_avail); if (bytes_avail <= 0) { *buff = NULL; *size = 0; *offset = 0; if (bytes_avail == 0 && cab->entry_cfdata->uncompressed_size == 0) { /* All of CFDATA in a folder has been handled. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } else return ((int)bytes_avail); } if (bytes_avail > cab->entry_bytes_remaining) bytes_avail = (ssize_t)cab->entry_bytes_remaining; *size = bytes_avail; *offset = cab->entry_offset; cab->entry_offset += bytes_avail; cab->entry_bytes_remaining -= bytes_avail; if (cab->entry_bytes_remaining == 0) cab->end_of_entry = 1; cab->entry_unconsumed = bytes_avail; if (cab->entry_cffolder->comptype == COMPTYPE_NONE) { /* Don't consume more than current entry used. */ if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed) cab->entry_cfdata->unconsumed = cab->entry_unconsumed; } return (ARCHIVE_OK); } static int archive_read_format_cab_read_data_skip(struct archive_read *a) { struct cab *cab; int64_t bytes_skipped; int r; cab = (struct cab *)(a->format->data); if (cab->end_of_archive) return (ARCHIVE_EOF); if (!cab->read_data_invoked) { cab->bytes_skipped += cab->entry_bytes_remaining; cab->entry_bytes_remaining = 0; /* This entry is finished and done. */ cab->end_of_entry_cleanup = cab->end_of_entry = 1; return (ARCHIVE_OK); } if (cab->entry_unconsumed) { /* Consume as much as the compressor actually used. */ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed); cab->entry_unconsumed = 0; if (r < 0) return (r); } else if (cab->entry_cfdata == NULL) { r = cab_next_cfdata(a); if (r < 0) return (r); } /* if we've already read to end of data, we're done. */ if (cab->end_of_entry_cleanup) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* If the compression type is none(uncompressed), we've already * consumed data as much as the current entry size. */ if (cab->entry_cffolder->comptype == COMPTYPE_NONE && cab->entry_cfdata != NULL) cab->entry_cfdata->unconsumed = 0; /* This entry is finished and done. */ cab->end_of_entry_cleanup = cab->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_cab_cleanup(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfheader *hd = &cab->cfheader; int i; if (hd->folder_array != NULL) { for (i = 0; i < hd->folder_count; i++) free(hd->folder_array[i].cfdata.memimage); free(hd->folder_array); } if (hd->file_array != NULL) { for (i = 0; i < cab->cfheader.file_count; i++) archive_string_free(&(hd->file_array[i].pathname)); free(hd->file_array); } #ifdef HAVE_ZLIB_H if (cab->stream_valid) inflateEnd(&cab->stream); #endif lzx_decode_free(&cab->xstrm); archive_wstring_free(&cab->ws); free(cab->uncompressed_buffer); free(cab); (a->format->data) = NULL; return (ARCHIVE_OK); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t cab_dos_time(const unsigned char *p) { int msTime, msDate; struct tm ts; msDate = archive_le16dec(p); msTime = archive_le16dec(p+2); memset(&ts, 0, sizeof(ts)); ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ ts.tm_mday = msDate & 0x1f; /* Day of month. */ ts.tm_hour = (msTime >> 11) & 0x1f; ts.tm_min = (msTime >> 5) & 0x3f; ts.tm_sec = (msTime << 1) & 0x3e; ts.tm_isdst = -1; return (mktime(&ts)); } /***************************************************************** * * LZX decompression code. * *****************************************************************/ /* * Initialize LZX decoder. * * Returns ARCHIVE_OK if initialization was successful. * Returns ARCHIVE_FAILED if w_bits has unsupported value. * Returns ARCHIVE_FATAL if initialization failed; memory allocation * error occurred. */ static int lzx_decode_init(struct lzx_stream *strm, int w_bits) { struct lzx_dec *ds; int slot, w_size, w_slot; int base, footer; int base_inc[18]; if (strm->ds == NULL) { strm->ds = calloc(1, sizeof(*strm->ds)); if (strm->ds == NULL) return (ARCHIVE_FATAL); } ds = strm->ds; ds->error = ARCHIVE_FAILED; /* Allow bits from 15(32KBi) up to 21(2MBi) */ if (w_bits < SLOT_BASE || w_bits > SLOT_MAX) return (ARCHIVE_FAILED); ds->error = ARCHIVE_FATAL; /* * Alloc window */ w_size = ds->w_size; w_slot = slots[w_bits - SLOT_BASE]; ds->w_size = 1U << w_bits; ds->w_mask = ds->w_size -1; if (ds->w_buff == NULL || w_size != ds->w_size) { free(ds->w_buff); ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); free(ds->pos_tbl); ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot); if (ds->pos_tbl == NULL) return (ARCHIVE_FATAL); lzx_huffman_free(&(ds->mt)); } for (footer = 0; footer < 18; footer++) base_inc[footer] = 1 << footer; base = footer = 0; for (slot = 0; slot < w_slot; slot++) { int n; if (footer == 0) base = slot; else base += base_inc[footer]; if (footer < 17) { footer = -2; for (n = base; n; n >>= 1) footer++; if (footer <= 0) footer = 0; } ds->pos_tbl[slot].base = base; ds->pos_tbl[slot].footer_bits = footer; } ds->w_pos = 0; ds->state = 0; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; ds->r0 = ds->r1 = ds->r2 = 1; /* Initialize aligned offset tree. */ if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize pre-tree. */ if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize Main tree. */ if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize Length tree. */ if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->error = 0; return (ARCHIVE_OK); } /* * Release LZX decoder. */ static void lzx_decode_free(struct lzx_stream *strm) { if (strm->ds == NULL) return; free(strm->ds->w_buff); free(strm->ds->pos_tbl); lzx_huffman_free(&(strm->ds->at)); lzx_huffman_free(&(strm->ds->pt)); lzx_huffman_free(&(strm->ds->mt)); lzx_huffman_free(&(strm->ds->lt)); free(strm->ds); strm->ds = NULL; } /* * E8 Call Translation reversal. */ static void lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset) { struct lzx_dec *ds = strm->ds; unsigned char *b, *end; if (!ds->translation || size <= 10) return; b = p; end = b + size - 10; while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) { size_t i = b - (unsigned char *)p; int32_t cp, displacement, value; cp = (int32_t)(offset + (uint32_t)i); value = archive_le32dec(&b[1]); if (value >= -cp && value < (int32_t)ds->translation_size) { if (value >= 0) displacement = value - cp; else displacement = value + ds->translation_size; archive_le32enc(&b[1], (uint32_t)displacement); } b += 5; } } /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define lzx_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define lzx_br_bits(br, n) \ (((uint32_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define lzx_br_bits_forced(br, n) \ (((uint32_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : we met that strm->next_in is empty, we have to get following * bytes. */ #define lzx_br_read_ahead_0(strm, br, n) \ (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br)) /* True : the cache buffer has some bits as much as we need. * False : there are no enough bits in the cache buffer to be used, * we have to get following bytes if we could. */ #define lzx_br_read_ahead(strm, br, n) \ (lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n))) /* Notify how many bits we consumed. */ #define lzx_br_consume(br, n) ((br)->cache_avail -= (n)) #define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f) #define lzx_br_is_unaligned(br) ((br)->cache_avail & 0x0f) static const uint32_t cache_masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) { /* * x86 processor family can read misaligned data without an access error. */ int n = CACHE_BITS - br->cache_avail; for (;;) { switch (n >> 4) { case 4: if (strm->avail_in >= 8) { br->cache_buffer = ((uint64_t)strm->next_in[1]) << 56 | ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[3]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | ((uint32_t)strm->next_in[5]) << 24 | ((uint32_t)strm->next_in[4]) << 16 | ((uint32_t)strm->next_in[7]) << 8 | (uint32_t)strm->next_in[6]; strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); } break; case 3: if (strm->avail_in >= 6) { br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[0]) << 32 | ((uint32_t)strm->next_in[3]) << 24 | ((uint32_t)strm->next_in[2]) << 16 | ((uint32_t)strm->next_in[5]) << 8 | (uint32_t)strm->next_in[4]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); } break; case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } if (strm->avail_in < 2) { /* There is not enough compressed data to * fill up the cache buffer. */ if (strm->avail_in == 1) { br->odd = *strm->next_in++; strm->avail_in--; br->have_odd = 1; } return (0); } br->cache_buffer = (br->cache_buffer << 16) | archive_le16dec(strm->next_in); strm->next_in += 2; strm->avail_in -= 2; br->cache_avail += 16; n -= 16; } } static void lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br) { int n = CACHE_BITS - br->cache_avail; if (br->have_odd && n >= 16 && strm->avail_in > 0) { br->cache_buffer = (br->cache_buffer << 16) | ((uint16_t)(*strm->next_in)) << 8 | br->odd; strm->next_in++; strm->avail_in--; br->cache_avail += 16; br->have_odd = 0; } } static void lzx_cleanup_bitstream(struct lzx_stream *strm) { strm->ds->br.cache_avail = 0; strm->ds->br.have_odd = 0; } /* * Decode LZX. * * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty. * Please set available buffer and call this function again. * 2. Returns ARCHIVE_EOF if decompression has been completed. * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data * is broken or you do not set 'last' flag properly. */ #define ST_RD_TRANSLATION 0 #define ST_RD_TRANSLATION_SIZE 1 #define ST_RD_BLOCK_TYPE 2 #define ST_RD_BLOCK_SIZE 3 #define ST_RD_ALIGNMENT 4 #define ST_RD_R0 5 #define ST_RD_R1 6 #define ST_RD_R2 7 #define ST_COPY_UNCOMP1 8 #define ST_COPY_UNCOMP2 9 #define ST_RD_ALIGNED_OFFSET 10 #define ST_RD_VERBATIM 11 #define ST_RD_PRE_MAIN_TREE_256 12 #define ST_MAIN_TREE_256 13 #define ST_RD_PRE_MAIN_TREE_REM 14 #define ST_MAIN_TREE_REM 15 #define ST_RD_PRE_LENGTH_TREE 16 #define ST_LENGTH_TREE 17 #define ST_MAIN 18 #define ST_LENGTH 19 #define ST_OFFSET 20 #define ST_REAL_POS 21 #define ST_COPY 22 static int lzx_decode(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; int64_t avail_in; int r; if (ds->error) return (ds->error); avail_in = strm->avail_in; lzx_br_fixup(strm, &(ds->br)); do { if (ds->state < ST_MAIN) r = lzx_read_blocks(strm, last); else { int64_t bytes_written = strm->avail_out; r = lzx_decode_blocks(strm, last); bytes_written -= strm->avail_out; strm->next_out += bytes_written; strm->total_out += bytes_written; } } while (r == 100); strm->total_in += avail_in - strm->avail_in; return (r); } static int lzx_read_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int i, r; for (;;) { switch (ds->state) { case ST_RD_TRANSLATION: if (!lzx_br_read_ahead(strm, br, 1)) { ds->state = ST_RD_TRANSLATION; if (last) goto failed; return (ARCHIVE_OK); } ds->translation = lzx_br_bits(br, 1); lzx_br_consume(br, 1); /* FALL THROUGH */ case ST_RD_TRANSLATION_SIZE: if (ds->translation) { if (!lzx_br_read_ahead(strm, br, 32)) { ds->state = ST_RD_TRANSLATION_SIZE; if (last) goto failed; return (ARCHIVE_OK); } ds->translation_size = lzx_br_bits(br, 16); lzx_br_consume(br, 16); ds->translation_size <<= 16; ds->translation_size |= lzx_br_bits(br, 16); lzx_br_consume(br, 16); } /* FALL THROUGH */ case ST_RD_BLOCK_TYPE: if (!lzx_br_read_ahead(strm, br, 3)) { ds->state = ST_RD_BLOCK_TYPE; if (last) goto failed; return (ARCHIVE_OK); } ds->block_type = lzx_br_bits(br, 3); lzx_br_consume(br, 3); /* Check a block type. */ switch (ds->block_type) { case VERBATIM_BLOCK: case ALIGNED_OFFSET_BLOCK: case UNCOMPRESSED_BLOCK: break; default: goto failed;/* Invalid */ } /* FALL THROUGH */ case ST_RD_BLOCK_SIZE: if (!lzx_br_read_ahead(strm, br, 24)) { ds->state = ST_RD_BLOCK_SIZE; if (last) goto failed; return (ARCHIVE_OK); } ds->block_size = lzx_br_bits(br, 8); lzx_br_consume(br, 8); ds->block_size <<= 16; ds->block_size |= lzx_br_bits(br, 16); lzx_br_consume(br, 16); if (ds->block_size == 0) goto failed; ds->block_bytes_avail = ds->block_size; if (ds->block_type != UNCOMPRESSED_BLOCK) { if (ds->block_type == VERBATIM_BLOCK) ds->state = ST_RD_VERBATIM; else ds->state = ST_RD_ALIGNED_OFFSET; break; } /* FALL THROUGH */ case ST_RD_ALIGNMENT: /* * Handle an Uncompressed Block. */ /* Skip padding to align following field on * 16-bit boundary. */ if (lzx_br_is_unaligned(br)) lzx_br_consume_unaligned_bits(br); else { if (lzx_br_read_ahead(strm, br, 16)) lzx_br_consume(br, 16); else { ds->state = ST_RD_ALIGNMENT; if (last) goto failed; return (ARCHIVE_OK); } } /* Preparation to read repeated offsets R0,R1 and R2. */ ds->rbytes_avail = 0; ds->state = ST_RD_R0; /* FALL THROUGH */ case ST_RD_R0: case ST_RD_R1: case ST_RD_R2: do { uint16_t u16; /* Drain bits in the cache buffer of * bit-stream. */ if (lzx_br_has(br, 32)) { u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes, u16); u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes+2, u16); ds->rbytes_avail = 4; } else if (lzx_br_has(br, 16)) { u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes, u16); ds->rbytes_avail = 2; } if (ds->rbytes_avail < 4 && ds->br.have_odd) { ds->rbytes[ds->rbytes_avail++] = ds->br.odd; ds->br.have_odd = 0; } while (ds->rbytes_avail < 4) { if (strm->avail_in <= 0) { if (last) goto failed; return (ARCHIVE_OK); } ds->rbytes[ds->rbytes_avail++] = *strm->next_in++; strm->avail_in--; } ds->rbytes_avail = 0; if (ds->state == ST_RD_R0) { ds->r0 = archive_le32dec(ds->rbytes); if (ds->r0 < 0) goto failed; ds->state = ST_RD_R1; } else if (ds->state == ST_RD_R1) { ds->r1 = archive_le32dec(ds->rbytes); if (ds->r1 < 0) goto failed; ds->state = ST_RD_R2; } else if (ds->state == ST_RD_R2) { ds->r2 = archive_le32dec(ds->rbytes); if (ds->r2 < 0) goto failed; /* We've gotten all repeated offsets. */ ds->state = ST_COPY_UNCOMP1; } } while (ds->state != ST_COPY_UNCOMP1); /* FALL THROUGH */ case ST_COPY_UNCOMP1: /* * Copy bytes form next_in to next_out directly. */ while (ds->block_bytes_avail) { int l; if (strm->avail_out <= 0) /* Output buffer is empty. */ return (ARCHIVE_OK); if (strm->avail_in <= 0) { /* Input buffer is empty. */ if (last) goto failed; return (ARCHIVE_OK); } l = (int)ds->block_bytes_avail; if (l > ds->w_size - ds->w_pos) l = ds->w_size - ds->w_pos; if (l > strm->avail_out) l = (int)strm->avail_out; if (l > strm->avail_in) l = (int)strm->avail_in; memcpy(strm->next_out, strm->next_in, l); memcpy(&(ds->w_buff[ds->w_pos]), strm->next_in, l); strm->next_in += l; strm->avail_in -= l; strm->next_out += l; strm->avail_out -= l; strm->total_out += l; ds->w_pos = (ds->w_pos + l) & ds->w_mask; ds->block_bytes_avail -= l; } /* FALL THROUGH */ case ST_COPY_UNCOMP2: /* Re-align; skip padding byte. */ if (ds->block_size & 1) { if (strm->avail_in <= 0) { /* Input buffer is empty. */ ds->state = ST_COPY_UNCOMP2; if (last) goto failed; return (ARCHIVE_OK); } strm->next_in++; strm->avail_in --; } /* This block ended. */ ds->state = ST_RD_BLOCK_TYPE; return (ARCHIVE_EOF); /********************/ case ST_RD_ALIGNED_OFFSET: /* * Read Aligned offset tree. */ if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) { ds->state = ST_RD_ALIGNED_OFFSET; if (last) goto failed; return (ARCHIVE_OK); } memset(ds->at.freq, 0, sizeof(ds->at.freq)); for (i = 0; i < ds->at.len_size; i++) { ds->at.bitlen[i] = lzx_br_bits(br, 3); ds->at.freq[ds->at.bitlen[i]]++; lzx_br_consume(br, 3); } if (!lzx_make_huffman_table(&ds->at)) goto failed; /* FALL THROUGH */ case ST_RD_VERBATIM: ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_256: /* * Read Pre-tree for first 256 elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_MAIN_TREE_256: /* * Get path lengths of first 256 elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, 256); if (r < 0) goto failed; else if (!r) { ds->state = ST_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_REM: /* * Read Pre-tree for remaining elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 256; /* FALL THROUGH */ case ST_MAIN_TREE_REM: /* * Get path lengths of remaining elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, -1); if (r < 0) goto failed; else if (!r) { ds->state = ST_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->mt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_LENGTH_TREE: /* * Read Pre-tree for remaining elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_LENGTH_TREE: /* * Get path lengths of remaining elements of main tree. */ r = lzx_read_bitlen(strm, &ds->lt, -1); if (r < 0) goto failed; else if (!r) { ds->state = ST_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->lt)) goto failed; ds->state = ST_MAIN; return (100); } } failed: return (ds->error = ARCHIVE_FAILED); } static int lzx_decode_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br bre = ds->br; struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt); const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl; unsigned char *noutp = strm->next_out; unsigned char *endp = noutp + strm->avail_out; unsigned char *w_buff = ds->w_buff; unsigned char *at_bitlen = at->bitlen; unsigned char *lt_bitlen = lt->bitlen; unsigned char *mt_bitlen = mt->bitlen; size_t block_bytes_avail = ds->block_bytes_avail; int at_max_bits = at->max_bits; int lt_max_bits = lt->max_bits; int mt_max_bits = mt->max_bits; int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos; int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; int length_header = ds->length_header; int offset_bits = ds->offset_bits; int position_slot = ds->position_slot; int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2; int state = ds->state; char block_type = ds->block_type; for (;;) { switch (state) { case ST_MAIN: for (;;) { if (block_bytes_avail == 0) { /* This block ended. */ ds->state = ST_RD_BLOCK_TYPE; ds->br = bre; ds->block_bytes_avail = block_bytes_avail; ds->copy_len = copy_len; ds->copy_pos = copy_pos; ds->length_header = length_header; ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->w_pos = w_pos; strm->avail_out = endp - noutp; return (ARCHIVE_EOF); } if (noutp >= endp) /* Output buffer is empty. */ goto next_data; if (!lzx_br_read_ahead(strm, &bre, mt_max_bits)) { if (!last) goto next_data; /* Remaining bits are less than * maximum bits(mt.max_bits) but maybe * it still remains as much as we need, * so we should try to use it with * dummy bits. */ c = lzx_decode_huffman(mt, lzx_br_bits_forced( &bre, mt_max_bits)); lzx_br_consume(&bre, mt_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzx_decode_huffman(mt, lzx_br_bits(&bre, mt_max_bits)); lzx_br_consume(&bre, mt_bitlen[c]); } if (c > UCHAR_MAX) break; /* * 'c' is exactly literal code. */ /* Save a decoded code to reference it * afterward. */ w_buff[w_pos] = c; w_pos = (w_pos + 1) & w_mask; /* Store the decoded code to output buffer. */ *noutp++ = c; block_bytes_avail--; } /* * Get a match code, its length and offset. */ c -= UCHAR_MAX + 1; length_header = c & 7; position_slot = c >> 3; /* FALL THROUGH */ case ST_LENGTH: /* * Get a length. */ if (length_header == 7) { if (!lzx_br_read_ahead(strm, &bre, lt_max_bits)) { if (!last) { state = ST_LENGTH; goto next_data; } c = lzx_decode_huffman(lt, lzx_br_bits_forced( &bre, lt_max_bits)); lzx_br_consume(&bre, lt_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzx_decode_huffman(lt, lzx_br_bits(&bre, lt_max_bits)); lzx_br_consume(&bre, lt_bitlen[c]); } copy_len = c + 7 + 2; } else copy_len = length_header + 2; if ((size_t)copy_len > block_bytes_avail) goto failed; /* * Get an offset. */ switch (position_slot) { case 0: /* Use repeated offset 0. */ copy_pos = r0; state = ST_REAL_POS; continue; case 1: /* Use repeated offset 1. */ copy_pos = r1; /* Swap repeated offset. */ r1 = r0; r0 = copy_pos; state = ST_REAL_POS; continue; case 2: /* Use repeated offset 2. */ copy_pos = r2; /* Swap repeated offset. */ r2 = r0; r0 = copy_pos; state = ST_REAL_POS; continue; default: offset_bits = pos_tbl[position_slot].footer_bits; break; } /* FALL THROUGH */ case ST_OFFSET: /* * Get the offset, which is a distance from * current window position. */ if (block_type == ALIGNED_OFFSET_BLOCK && offset_bits >= 3) { int offbits = offset_bits - 3; if (!lzx_br_read_ahead(strm, &bre, offbits)) { state = ST_OFFSET; if (last) goto failed; goto next_data; } copy_pos = lzx_br_bits(&bre, offbits) << 3; /* Get an aligned number. */ if (!lzx_br_read_ahead(strm, &bre, offbits + at_max_bits)) { if (!last) { state = ST_OFFSET; goto next_data; } lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits_forced(&bre, at_max_bits)); lzx_br_consume(&bre, at_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits(&bre, at_max_bits)); lzx_br_consume(&bre, at_bitlen[c]); } /* Add an aligned number. */ copy_pos += c; } else { if (!lzx_br_read_ahead(strm, &bre, offset_bits)) { state = ST_OFFSET; if (last) goto failed; goto next_data; } copy_pos = lzx_br_bits(&bre, offset_bits); lzx_br_consume(&bre, offset_bits); } copy_pos += pos_tbl[position_slot].base -2; /* Update repeated offset LRU queue. */ r2 = r1; r1 = r0; r0 = copy_pos; /* FALL THROUGH */ case ST_REAL_POS: /* * Compute a real position in window. */ copy_pos = (w_pos - copy_pos) & w_mask; /* FALL THROUGH */ case ST_COPY: /* * Copy several bytes as extracted data from the window * into the output buffer. */ for (;;) { const unsigned char *s; int l; l = copy_len; if (copy_pos > w_pos) { if (l > w_size - copy_pos) l = w_size - copy_pos; } else { if (l > w_size - w_pos) l = w_size - w_pos; } if (noutp + l >= endp) l = (int)(endp - noutp); s = w_buff + copy_pos; if (l >= 8 && ((copy_pos + l < w_pos) || (w_pos + l < copy_pos))) { memcpy(w_buff + w_pos, s, l); memcpy(noutp, s, l); } else { unsigned char *d; int li; d = w_buff + w_pos; for (li = 0; li < l; li++) noutp[li] = d[li] = s[li]; } noutp += l; copy_pos = (copy_pos + l) & w_mask; w_pos = (w_pos + l) & w_mask; block_bytes_avail -= l; if (copy_len <= l) /* A copy of current pattern ended. */ break; copy_len -= l; if (noutp >= endp) { /* Output buffer is empty. */ state = ST_COPY; goto next_data; } } state = ST_MAIN; break; } } failed: return (ds->error = ARCHIVE_FAILED); next_data: ds->br = bre; ds->block_bytes_avail = block_bytes_avail; ds->copy_len = copy_len; ds->copy_pos = copy_pos; ds->length_header = length_header; ds->offset_bits = offset_bits; ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->state = state; ds->w_pos = w_pos; strm->avail_out = endp - noutp; return (ARCHIVE_OK); } static int lzx_read_pre_tree(struct lzx_stream *strm) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int i; if (ds->loop == 0) memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); for (i = ds->loop; i < ds->pt.len_size; i++) { if (!lzx_br_read_ahead(strm, br, 4)) { ds->loop = i; return (0); } ds->pt.bitlen[i] = lzx_br_bits(br, 4); ds->pt.freq[ds->pt.bitlen[i]]++; lzx_br_consume(br, 4); } ds->loop = i; return (1); } /* * Read a bunch of bit-lengths from pre-tree. */ static int lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int c, i, j, ret, same; unsigned rbits; i = ds->loop; if (i == 0) memset(d->freq, 0, sizeof(d->freq)); ret = 0; if (end < 0) end = d->len_size; while (i < end) { ds->loop = i; if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits)) goto getdata; rbits = lzx_br_bits(br, ds->pt.max_bits); c = lzx_decode_huffman(&(ds->pt), rbits); switch (c) { case 17:/* several zero lengths, from 4 to 19. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 4) + 4; if (i + same > end) return (-1);/* Invalid */ lzx_br_consume(br, 4); for (j = 0; j < same; j++) d->bitlen[i++] = 0; break; case 18:/* many zero lengths, from 20 to 51. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 5) + 20; if (i + same > end) return (-1);/* Invalid */ lzx_br_consume(br, 5); memset(d->bitlen + i, 0, same); i += same; break; case 19:/* a few same lengths. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+1+ds->pt.max_bits)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 1) + 4; if (i + same > end) return (-1); lzx_br_consume(br, 1); rbits = lzx_br_bits(br, ds->pt.max_bits); c = lzx_decode_huffman(&(ds->pt), rbits); lzx_br_consume(br, ds->pt.bitlen[c]); c = (d->bitlen[i] - c + 17) % 17; if (c < 0) return (-1);/* Invalid */ for (j = 0; j < same; j++) d->bitlen[i++] = c; d->freq[c] += same; break; default: lzx_br_consume(br, ds->pt.bitlen[c]); c = (d->bitlen[i] - c + 17) % 17; if (c < 0) return (-1);/* Invalid */ d->freq[c]++; d->bitlen[i++] = c; break; } } ret = 1; getdata: ds->loop = i; return (ret); } static int lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) { if (hf->bitlen == NULL || hf->len_size != (int)len_size) { free(hf->bitlen); hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); hf->len_size = (int)len_size; } else memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0])); if (hf->tbl == NULL) { hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); hf->tbl_bits = tbl_bits; } return (ARCHIVE_OK); } static void lzx_huffman_free(struct huffman *hf) { free(hf->bitlen); free(hf->tbl); } /* * Make a huffman coding table. */ static int lzx_make_huffman_table(struct huffman *hf) { uint16_t *tbl; const unsigned char *bitlen; int bitptn[17], weight[17]; int i, maxbits = 0, ptn, tbl_size, w; int len_avail; /* * Initialize bit patterns. */ ptn = 0; for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) { bitptn[i] = ptn; weight[i] = w; if (hf->freq[i]) { ptn += hf->freq[i] * w; maxbits = i; } } if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits) return (0);/* Invalid */ hf->max_bits = maxbits; /* * Cut out extra bits which we won't house in the table. * This preparation reduces the same calculation in the for-loop * making the table. */ if (maxbits < 16) { int ebits = 16 - maxbits; for (i = 1; i <= maxbits; i++) { bitptn[i] >>= ebits; weight[i] >>= ebits; } } /* * Make the table. */ tbl_size = 1 << hf->tbl_bits; tbl = hf->tbl; bitlen = hf->bitlen; len_avail = hf->len_size; hf->tree_used = 0; for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; if (len > tbl_size) return (0); ptn = bitptn[len]; cnt = weight[len]; /* Calculate next bit pattern */ if ((bitptn[len] = ptn + cnt) > tbl_size) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); while (--cnt >= 0) p[cnt] = (uint16_t)i; } return (1); } static inline int lzx_decode_huffman(struct huffman *hf, unsigned rbits) { int c; c = hf->tbl[rbits]; if (c < hf->len_size) return (c); return (0); } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_empty.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_empty.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_empty.c (revision 368707) @@ -1,96 +1,96 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" static int archive_read_format_empty_bid(struct archive_read *, int); static int archive_read_format_empty_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_empty_read_header(struct archive_read *, struct archive_entry *); int archive_read_support_format_empty(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_empty"); r = __archive_read_register_format(a, NULL, - NULL, + "empty", archive_read_format_empty_bid, NULL, archive_read_format_empty_read_header, archive_read_format_empty_read_data, NULL, NULL, NULL, NULL, NULL); return (r); } static int archive_read_format_empty_bid(struct archive_read *a, int best_bid) { if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) == NULL) return (1); return (-1); } static int archive_read_format_empty_read_header(struct archive_read *a, struct archive_entry *entry) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ a->archive.archive_format = ARCHIVE_FORMAT_EMPTY; a->archive.archive_format_name = "Empty file"; return (ARCHIVE_EOF); } static int archive_read_format_empty_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { (void)a; /* UNUSED */ (void)buff; /* UNUSED */ (void)size; /* UNUSED */ (void)offset; /* UNUSED */ return (ARCHIVE_EOF); } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_mtree.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_mtree.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_mtree.c (revision 368707) @@ -1,2025 +1,2138 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #include /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #include "archive.h" #include "archive_entry.h" +#include "archive_entry_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" #include "archive_string.h" #include "archive_pack_dev.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #define MTREE_HAS_DEVICE 0x0001 #define MTREE_HAS_FFLAGS 0x0002 #define MTREE_HAS_GID 0x0004 #define MTREE_HAS_GNAME 0x0008 #define MTREE_HAS_MTIME 0x0010 #define MTREE_HAS_NLINK 0x0020 #define MTREE_HAS_PERM 0x0040 #define MTREE_HAS_SIZE 0x0080 #define MTREE_HAS_TYPE 0x0100 #define MTREE_HAS_UID 0x0200 #define MTREE_HAS_UNAME 0x0400 #define MTREE_HAS_OPTIONAL 0x0800 #define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ #define MAX_LINE_LEN (1024 * 1024) struct mtree_option { struct mtree_option *next; char *value; }; struct mtree_entry { struct archive_rb_node rbnode; struct mtree_entry *next_dup; struct mtree_entry *next; struct mtree_option *options; char *name; char full; char used; }; struct mtree { struct archive_string line; size_t buffsize; char *buff; int64_t offset; int fd; int archive_format; const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; struct archive_rb_tree entry_rbtree; struct archive_string current_dir; struct archive_string contents_name; struct archive_entry_linkresolver *resolver; struct archive_rb_tree rbtree; int64_t cur_size; char checkfs; }; static int bid_keycmp(const char *, const char *, ssize_t); static int cleanup(struct archive_read *); static int detect_form(struct archive_read *, int *); static int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static void parse_escapes(char *, struct mtree_entry *); static int parse_line(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static int parse_keyword(struct archive_read *, struct mtree *, struct archive_entry *, struct mtree_option *, int *); static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); static int skip(struct archive_read *a); static int read_header(struct archive_read *, struct archive_entry *); static int64_t mtree_atol(char **, int base); +#ifndef HAVE_STRNLEN +static size_t mtree_strnlen(const char *, size_t); +#endif /* * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them * here. TODO: Move this to configure time, but be careful * about cross-compile environments. */ static int64_t get_time_t_max(void) { #if defined(TIME_T_MAX) return TIME_T_MAX; #else /* ISO C allows time_t to be a floating-point type, but POSIX requires an integer type. The following should work on any system that follows the POSIX conventions. */ if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (~(time_t)0); } else { /* Time_t is signed. */ /* Assume it's the same as int64_t or int32_t */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MAX; } else { return (time_t)INT32_MAX; } } #endif } static int64_t get_time_t_min(void) { #if defined(TIME_T_MIN) return TIME_T_MIN; #else if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (time_t)0; } else { /* Time_t is signed. */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MIN; } else { return (time_t)INT32_MIN; } } #endif } +#ifdef HAVE_STRNLEN +#define mtree_strnlen(a,b) strnlen(a,b) +#else +static size_t +mtree_strnlen(const char *p, size_t maxlen) +{ + size_t i; + + for (i = 0; i <= maxlen; i++) { + if (p[i] == 0) + break; + } + if (i > maxlen) + return (-1);/* invalid */ + return (i); +} +#endif + static int archive_read_format_mtree_options(struct archive_read *a, const char *key, const char *val) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (strcmp(key, "checkfs") == 0) { /* Allows to read information missing from the mtree from the file system */ if (val == NULL || val[0] == 0) { mtree->checkfs = 0; } else { mtree->checkfs = 1; } return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static void free_options(struct mtree_option *head) { struct mtree_option *next; for (; head != NULL; head = next) { next = head->next; free(head->value); free(head); } } static int mtree_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct mtree_entry *e1 = (const struct mtree_entry *)n1; const struct mtree_entry *e2 = (const struct mtree_entry *)n2; return (strcmp(e1->name, e2->name)); } static int mtree_cmp_key(const struct archive_rb_node *n, const void *key) { const struct mtree_entry *e = (const struct mtree_entry *)n; return (strcmp(e->name, key)); } int archive_read_support_format_mtree(struct archive *_a) { static const struct archive_rb_tree_ops rb_ops = { mtree_cmp_node, mtree_cmp_key, }; struct archive_read *a = (struct archive_read *)_a; struct mtree *mtree; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_mtree"); mtree = (struct mtree *)calloc(1, sizeof(*mtree)); if (mtree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } mtree->checkfs = 0; mtree->fd = -1; __archive_rb_tree_init(&mtree->rbtree, &rb_ops); r = __archive_read_register_format(a, mtree, "mtree", mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(mtree); return (ARCHIVE_OK); } static int cleanup(struct archive_read *a) { struct mtree *mtree; struct mtree_entry *p, *q; mtree = (struct mtree *)(a->format->data); p = mtree->entries; while (p != NULL) { q = p->next; free(p->name); free_options(p->options); free(p); p = q; } archive_string_free(&mtree->line); archive_string_free(&mtree->current_dir); archive_string_free(&mtree->contents_name); archive_entry_linkresolver_free(mtree->resolver); free(mtree->buff); free(mtree); (a->format->data) = NULL; return (ARCHIVE_OK); } static ssize_t get_line_size(const char *b, ssize_t avail, ssize_t *nlsize) { ssize_t len; len = 0; while (len < avail) { switch (*b) { case '\0':/* Non-ascii character or control character. */ if (nlsize != NULL) *nlsize = 0; return (-1); case '\r': if (avail-len > 1 && b[1] == '\n') { if (nlsize != NULL) *nlsize = 2; return (len+2); } /* FALL THROUGH */ case '\n': if (nlsize != NULL) *nlsize = 1; return (len+1); default: b++; len++; break; } } if (nlsize != NULL) *nlsize = 0; return (avail); } /* * <---------------- ravail ---------------------> * <-- diff ------> <--- avail -----------------> * <---- len -----------> * | Previous lines | line being parsed nl extra | * ^ * b * */ static ssize_t next_line(struct archive_read *a, const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl) { ssize_t len; int quit; quit = 0; if (*avail == 0) { *nl = 0; len = 0; } else len = get_line_size(*b, *avail, nl); /* * Read bytes more while it does not reach the end of line. */ while (*nl == 0 && len == *avail && !quit) { ssize_t diff = *ravail - *avail; size_t nbytes_req = (*ravail+1023) & ~1023U; ssize_t tested; /* * Place an arbitrary limit on the line length. * mtree is almost free-form input and without line length limits, * it can consume a lot of memory. */ if (len >= MAX_LINE_LEN) return (-1); /* Increase reading bytes if it is not enough to at least * new two lines. */ if (nbytes_req < (size_t)*ravail + 160) nbytes_req <<= 1; *b = __archive_read_ahead(a, nbytes_req, avail); if (*b == NULL) { if (*ravail >= *avail) return (0); /* Reading bytes reaches the end of file. */ *b = __archive_read_ahead(a, *avail, avail); quit = 1; } *ravail = *avail; *b += diff; *avail -= diff; tested = len;/* Skip some bytes we already determinated. */ len = get_line_size(*b + len, *avail - len, nl); if (len >= 0) len += tested; } return (len); } /* * Compare characters with a mtree keyword. * Returns the length of a mtree keyword if matched. * Returns 0 if not matched. */ static int bid_keycmp(const char *p, const char *key, ssize_t len) { int match_len = 0; while (len > 0 && *p && *key) { if (*p == *key) { --len; ++p; ++key; ++match_len; continue; } return (0);/* Not match */ } if (*key != '\0') return (0);/* Not match */ /* A following character should be specified characters */ if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' || p[0] == '\n' || p[0] == '\r' || (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))) return (match_len); return (0);/* Not match */ } /* * Test whether the characters 'p' has is mtree keyword. * Returns the length of a detected keyword. * Returns 0 if any keywords were not found. */ static int bid_keyword(const char *p, ssize_t len) { static const char * const keys_c[] = { "content", "contents", "cksum", NULL }; static const char * const keys_df[] = { "device", "flags", NULL }; static const char * const keys_g[] = { "gid", "gname", NULL }; static const char * const keys_il[] = { "ignore", "inode", "link", NULL }; static const char * const keys_m[] = { "md5", "md5digest", "mode", NULL }; static const char * const keys_no[] = { "nlink", "nochange", "optional", NULL }; static const char * const keys_r[] = { "resdevice", "rmd160", "rmd160digest", NULL }; static const char * const keys_s[] = { "sha1", "sha1digest", "sha256", "sha256digest", "sha384", "sha384digest", "sha512", "sha512digest", "size", NULL }; static const char * const keys_t[] = { "tags", "time", "type", NULL }; static const char * const keys_u[] = { "uid", "uname", NULL }; const char * const *keys; int i; switch (*p) { case 'c': keys = keys_c; break; case 'd': case 'f': keys = keys_df; break; case 'g': keys = keys_g; break; case 'i': case 'l': keys = keys_il; break; case 'm': keys = keys_m; break; case 'n': case 'o': keys = keys_no; break; case 'r': keys = keys_r; break; case 's': keys = keys_s; break; case 't': keys = keys_t; break; case 'u': keys = keys_u; break; default: return (0);/* Unknown key */ } for (i = 0; keys[i] != NULL; i++) { int l = bid_keycmp(p, keys[i], len); if (l > 0) return (l); } return (0);/* Unknown key */ } /* * Test whether there is a set of mtree keywords. * Returns the number of keyword. * Returns -1 if we got incorrect sequence. * This function expects a set of "keyword=value". * When "unset" is specified, expects a set of "keyword". */ static int bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path) { int l; int keycnt = 0; while (len > 0 && *p) { int blank = 0; /* Test whether there are blank characters in the line. */ while (len >0 && (*p == ' ' || *p == '\t')) { ++p; --len; blank = 1; } if (*p == '\n' || *p == '\r') break; if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')) break; if (!blank && !last_is_path) /* No blank character. */ return (-1); if (last_is_path && len == 0) return (keycnt); if (unset) { l = bid_keycmp(p, "all", len); if (l > 0) return (1); } /* Test whether there is a correct key in the line. */ l = bid_keyword(p, len); if (l == 0) return (-1);/* Unknown keyword was found. */ p += l; len -= l; keycnt++; /* Skip value */ if (*p == '=') { int value = 0; ++p; --len; while (len > 0 && *p != ' ' && *p != '\t') { ++p; --len; value = 1; } /* A keyword should have a its value unless * "/unset" operation. */ if (!unset && value == 0) return (-1); } } return (keycnt); } static int bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path) { int f = 0; static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ /* !"$%&'()*+,-./ EXCLUSION:( )(#) */ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ /* 0123456789:;<>? EXCLUSION:(=) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ /* PQRSTUVWXYZ[\]^_ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; ssize_t ll; const char *pp = p; const char * const pp_end = pp + len; *last_is_path = 0; /* * Skip the path-name which is quoted. */ for (;pp < pp_end; ++pp) { if (!safe_char[*(const unsigned char *)pp]) { if (*pp != ' ' && *pp != '\t' && *pp != '\r' && *pp != '\n') f = 0; break; } f = 1; } ll = pp_end - pp; /* If a path-name was not found at the first, try to check * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates, * which places the path-name at the last. */ if (f == 0) { const char *pb = p + len - nl; int name_len = 0; int slash; /* The form D accepts only a single line for an entry. */ if (pb-2 >= p && pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) return (-1); if (pb-1 >= p && pb[-1] == '\\') return (-1); slash = 0; while (p <= --pb && *pb != ' ' && *pb != '\t') { if (!safe_char[*(const unsigned char *)pb]) return (-1); name_len++; /* The pathname should have a slash in this * format. */ if (*pb == '/') slash = 1; } if (name_len == 0 || slash == 0) return (-1); /* If '/' is placed at the first in this field, this is not * a valid filename. */ if (pb[1] == '/') return (-1); ll = len - nl - name_len; pp = p; *last_is_path = 1; } return (bid_keyword_list(pp, ll, 0, *last_is_path)); } #define MAX_BID_ENTRY 3 static int mtree_bid(struct archive_read *a, int best_bid) { const char *signature = "#mtree"; const char *p; (void)best_bid; /* UNUSED */ /* Now let's look at the actual header and see if it matches. */ p = __archive_read_ahead(a, strlen(signature), NULL); if (p == NULL) return (-1); if (memcmp(p, signature, strlen(signature)) == 0) return (8 * (int)strlen(signature)); /* * There is not a mtree signature. Let's try to detect mtree format. */ return (detect_form(a, NULL)); } static int detect_form(struct archive_read *a, int *is_form_d) { const char *p; ssize_t avail, ravail; ssize_t detected_bytes = 0, len, nl; int entry_cnt = 0, multiline = 0; int form_D = 0;/* The archive is generated by `NetBSD mtree -D' * (In this source we call it `form D') . */ if (is_form_d != NULL) *is_form_d = 0; p = __archive_read_ahead(a, 1, &avail); if (p == NULL) return (-1); ravail = avail; for (;;) { len = next_line(a, &p, &avail, &ravail, &nl); /* The terminal character of the line should be * a new line character, '\r\n' or '\n'. */ if (len <= 0 || nl == 0) break; if (!multiline) { /* Leading whitespace is never significant, * ignore it. */ while (len > 0 && (*p == ' ' || *p == '\t')) { ++p; --avail; --len; } /* Skip comment or empty line. */ if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') { p += len; avail -= len; continue; } } else { /* A continuance line; the terminal * character of previous line was '\' character. */ if (bid_keyword_list(p, len, 0, 0) <= 0) break; if (multiline == 1) detected_bytes += len; if (p[len-nl-1] != '\\') { if (multiline == 1 && ++entry_cnt >= MAX_BID_ENTRY) break; multiline = 0; } p += len; avail -= len; continue; } if (p[0] != '/') { int last_is_path, keywords; keywords = bid_entry(p, len, nl, &last_is_path); if (keywords >= 0) { detected_bytes += len; if (form_D == 0) { if (last_is_path) form_D = 1; else if (keywords > 0) /* This line is not `form D'. */ form_D = -1; } else if (form_D == 1) { if (!last_is_path && keywords > 0) /* This this is not `form D' * and We cannot accept mixed * format. */ break; } if (!last_is_path && p[len-nl-1] == '\\') /* This line continues. */ multiline = 1; else { /* We've got plenty of correct lines * to assume that this file is a mtree * format. */ if (++entry_cnt >= MAX_BID_ENTRY) break; } } else break; } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (bid_keyword_list(p+4, len-4, 0, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else break; /* Test next line. */ p += len; avail -= len; } if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) { if (is_form_d != NULL) { if (form_D == 1) *is_form_d = 1; } return (32); } return (0); } /* * The extended mtree format permits multiple lines specifying * attributes for each file. For those entries, only the last line * is actually used. Practically speaking, that means we have * to read the entire mtree file into memory up front. * * The parsing is done in two steps. First, it is decided if a line * changes the global defaults and if it is, processed accordingly. * Otherwise, the options of the line are merged with the current * global options. */ static int add_option(struct archive_read *a, struct mtree_option **global, const char *value, size_t len) { struct mtree_option *opt; if ((opt = malloc(sizeof(*opt))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } if ((opt->value = malloc(len + 1)) == NULL) { free(opt); archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(opt->value, value, len); opt->value[len] = '\0'; opt->next = *global; *global = opt; return (ARCHIVE_OK); } static void remove_option(struct mtree_option **global, const char *value, size_t len) { struct mtree_option *iter, *last; last = NULL; for (iter = *global; iter != NULL; last = iter, iter = iter->next) { if (strncmp(iter->value, value, len) == 0 && (iter->value[len] == '\0' || iter->value[len] == '=')) break; } if (iter == NULL) return; if (last == NULL) *global = iter->next; else last->next = iter->next; free(iter->value); free(iter); } static int process_global_set(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next, *eq; size_t len; int r; line += 4; for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq > next) len = next - line; else len = eq - line; remove_option(global, line, len); r = add_option(a, global, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int process_global_unset(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next; size_t len; line += 6; if (strchr(line, '=') != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "/unset shall not contain `='"); return ARCHIVE_FATAL; } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; len = strcspn(line, " \t\r\n"); if (len == 3 && strncmp(line, "all", 3) == 0) { free_options(*global); *global = NULL; } else { remove_option(global, line, len); } line += len; } } static int process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_option **global, const char *line, ssize_t line_len, struct mtree_entry **last_entry, int is_form_d) { struct mtree_entry *entry; struct mtree_option *iter; const char *next, *eq, *name, *end; size_t name_len, len; int r, i; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } entry->next = NULL; entry->options = NULL; entry->name = NULL; entry->used = 0; entry->full = 0; /* Add this entry to list. */ if (*last_entry == NULL) mtree->entries = entry; else (*last_entry)->next = entry; *last_entry = entry; if (is_form_d) { /* Filename is last item on line. */ /* Adjust line_len to trim trailing whitespace */ while (line_len > 0) { char last_character = line[line_len - 1]; if (last_character == '\r' || last_character == '\n' || last_character == '\t' || last_character == ' ') { line_len--; } else { break; } } /* Name starts after the last whitespace separator */ name = line; for (i = 0; i < line_len; i++) { if (line[i] == '\r' || line[i] == '\n' || line[i] == '\t' || line[i] == ' ') { name = line + i + 1; } } name_len = line + line_len - name; end = name; } else { /* Filename is first item on line */ name_len = strcspn(line, " \t\r\n"); name = line; line += name_len; end = line + line_len; } /* name/name_len is the name within the line. */ /* line..end brackets the entire line except the name */ if ((entry->name = malloc(name_len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(entry->name, name, name_len); entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); entry->next_dup = NULL; if (entry->full) { if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) { struct mtree_entry *alt; alt = (struct mtree_entry *)__archive_rb_tree_find_node( &mtree->rbtree, entry->name); while (alt->next_dup) alt = alt->next_dup; alt->next_dup = entry; } } for (iter = *global; iter != NULL; iter = iter->next) { r = add_option(a, &entry->options, iter->value, strlen(iter->value)); if (r != ARCHIVE_OK) return (r); } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); if (next >= end) return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq == NULL || eq > next) len = next - line; else len = eq - line; remove_option(&entry->options, line, len); r = add_option(a, &entry->options, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; uintmax_t counter; char *p, *s; struct mtree_option *global; struct mtree_entry *last_entry; int r, is_form_d; mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; global = NULL; last_entry = NULL; (void)detect_form(a, &is_form_d); for (counter = 1; ; ++counter) { r = ARCHIVE_OK; len = readline(a, mtree, &p, 65536); if (len == 0) { mtree->this_entry = mtree->entries; free_options(global); return (ARCHIVE_OK); } if (len < 0) { free_options(global); return ((int)len); } /* Leading whitespace is never significant, ignore it. */ while (*p == ' ' || *p == '\t') { ++p; --len; } /* Skip content lines and blank lines. */ if (*p == '#') continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; /* Non-printable characters are not allowed */ for (s = p;s < p + len - 1; s++) { if (!isprint(*s)) { r = ARCHIVE_FATAL; break; } } if (r != ARCHIVE_OK) break; if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (p[4] != ' ' && p[4] != '\t') break; r = process_global_set(a, &global, p); } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (p[6] != ' ' && p[6] != '\t') break; r = process_global_unset(a, &global, p); } else break; if (r != ARCHIVE_OK) { free_options(global); return r; } } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't parse line %ju", counter); free_options(global); return (ARCHIVE_FATAL); } /* * Read in the entire mtree file into memory on the first request. * Then use the next unused file to satisfy each header request. */ static int read_header(struct archive_read *a, struct archive_entry *entry) { struct mtree *mtree; char *p; int r, use_next; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } if (mtree->entries == NULL) { mtree->resolver = archive_entry_linkresolver_new(); if (mtree->resolver == NULL) return ARCHIVE_FATAL; archive_entry_linkresolver_set_strategy(mtree->resolver, ARCHIVE_FORMAT_MTREE); r = read_mtree(a, mtree); if (r != ARCHIVE_OK) return (r); } a->archive.archive_format = mtree->archive_format; a->archive.archive_format_name = mtree->archive_format_name; for (;;) { if (mtree->this_entry == NULL) return (ARCHIVE_EOF); if (strcmp(mtree->this_entry->name, "..") == 0) { mtree->this_entry->used = 1; if (archive_strlen(&mtree->current_dir) > 0) { /* Roll back current path. */ p = mtree->current_dir.s + mtree->current_dir.length - 1; while (p >= mtree->current_dir.s && *p != '/') --p; if (p >= mtree->current_dir.s) --p; mtree->current_dir.length = p - mtree->current_dir.s + 1; } } if (!mtree->this_entry->used) { use_next = 0; r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); if (use_next == 0) return (r); } mtree->this_entry = mtree->this_entry->next; } } /* * A single file can have multiple lines contribute specifications. * Parse as many lines as necessary, then pull additional information * from a backing file on disk as necessary. */ static int parse_file(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mentry, int *use_next) { const char *path; struct stat st_storage, *st; struct mtree_entry *mp; struct archive_entry *sparse_entry; int r = ARCHIVE_OK, r1, parsed_kws; mentry->used = 1; /* Initialize reasonable defaults. */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_size(entry, 0); archive_string_empty(&mtree->contents_name); /* Parse options from this line. */ parsed_kws = 0; r = parse_line(a, entry, mtree, mentry, &parsed_kws); if (mentry->full) { archive_entry_copy_pathname(entry, mentry->name); /* * "Full" entries are allowed to have multiple lines * and those lines aren't required to be adjacent. We * don't support multiple lines for "relative" entries * nor do we make any attempt to merge data from * separate "relative" and "full" entries. (Merging * "relative" and "full" entries would require dealing * with pathname canonicalization, which is a very * tricky subject.) */ mp = (struct mtree_entry *)__archive_rb_tree_find_node( &mtree->rbtree, mentry->name); for (; mp; mp = mp->next_dup) { if (mp->full && !mp->used) { /* Later lines override earlier ones. */ mp->used = 1; r1 = parse_line(a, entry, mtree, mp, &parsed_kws); if (r1 < r) r = r1; } } } else { /* * Relative entries require us to construct * the full path and possibly update the * current directory. */ size_t n = archive_strlen(&mtree->current_dir); if (n > 0) archive_strcat(&mtree->current_dir, "/"); archive_strcat(&mtree->current_dir, mentry->name); archive_entry_copy_pathname(entry, mtree->current_dir.s); if (archive_entry_filetype(entry) != AE_IFDIR) mtree->current_dir.length = n; } if (mtree->checkfs) { /* * Try to open and stat the file to get the real size * and other file info. It would be nice to avoid * this here so that getting a listing of an mtree * wouldn't require opening every referenced contents * file. But then we wouldn't know the actual * contents size, so I don't see a really viable way * around this. (Also, we may want to someday pull * other unspecified info from the contents file on * disk.) */ mtree->fd = -1; if (archive_strlen(&mtree->contents_name) > 0) path = mtree->contents_name.s; else path = archive_entry_pathname(entry); if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(mtree->fd); if (mtree->fd == -1 && (errno != ENOENT || archive_strlen(&mtree->contents_name) > 0)) { archive_set_error(&a->archive, errno, "Can't open %s", path); r = ARCHIVE_WARN; } } st = &st_storage; if (mtree->fd >= 0) { if (fstat(mtree->fd, st) == -1) { archive_set_error(&a->archive, errno, "Could not fstat %s", path); r = ARCHIVE_WARN; /* If we can't stat it, don't keep it open. */ close(mtree->fd); mtree->fd = -1; st = NULL; } } else if (lstat(path, st) == -1) { st = NULL; } /* * Check for a mismatch between the type in the specification * and the type of the contents object on disk. */ if (st != NULL) { if (((st->st_mode & S_IFMT) == S_IFREG && archive_entry_filetype(entry) == AE_IFREG) #ifdef S_IFLNK ||((st->st_mode & S_IFMT) == S_IFLNK && archive_entry_filetype(entry) == AE_IFLNK) #endif #ifdef S_IFSOCK ||((st->st_mode & S_IFSOCK) == S_IFSOCK && archive_entry_filetype(entry) == AE_IFSOCK) #endif #ifdef S_IFCHR ||((st->st_mode & S_IFMT) == S_IFCHR && archive_entry_filetype(entry) == AE_IFCHR) #endif #ifdef S_IFBLK ||((st->st_mode & S_IFMT) == S_IFBLK && archive_entry_filetype(entry) == AE_IFBLK) #endif ||((st->st_mode & S_IFMT) == S_IFDIR && archive_entry_filetype(entry) == AE_IFDIR) #ifdef S_IFIFO ||((st->st_mode & S_IFMT) == S_IFIFO && archive_entry_filetype(entry) == AE_IFIFO) #endif ) { /* Types match. */ } else { /* Types don't match; bail out gracefully. */ if (mtree->fd >= 0) close(mtree->fd); mtree->fd = -1; if (parsed_kws & MTREE_HAS_OPTIONAL) { /* It's not an error for an optional * entry to not match disk. */ *use_next = 1; } else if (r == ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "mtree specification has different" " type for %s", archive_entry_pathname(entry)); r = ARCHIVE_WARN; } return (r); } } /* * If there is a contents file on disk, pick some of the * metadata from that file. For most of these, we only * set it from the contents if it wasn't already parsed * from the specification. */ if (st != NULL) { if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) && (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK)) archive_entry_set_rdev(entry, st->st_rdev); if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_gid(entry, st->st_gid); if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_uid(entry, st->st_uid); if ((parsed_kws & MTREE_HAS_MTIME) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime*1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec*1000); #else archive_entry_set_mtime(entry, st->st_mtime, 0); #endif } if ((parsed_kws & MTREE_HAS_NLINK) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_nlink(entry, st->st_nlink); if ((parsed_kws & MTREE_HAS_PERM) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_perm(entry, st->st_mode); if ((parsed_kws & MTREE_HAS_SIZE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_size(entry, st->st_size); archive_entry_set_ino(entry, st->st_ino); archive_entry_set_dev(entry, st->st_dev); archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); } else if (parsed_kws & MTREE_HAS_OPTIONAL) { /* * Couldn't open the entry, stat it or the on-disk type * didn't match. If this entry is optional, just * ignore it and read the next header entry. */ *use_next = 1; return ARCHIVE_OK; } } mtree->cur_size = archive_entry_size(entry); mtree->offset = 0; return r; } /* * Each line contains a sequence of keywords. */ static int parse_line(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws) { struct mtree_option *iter; int r = ARCHIVE_OK, r1; for (iter = mp->options; iter != NULL; iter = iter->next) { r1 = parse_keyword(a, mtree, entry, iter, parsed_kws); if (r1 < r) r = r1; } if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Missing type keyword in mtree specification"); return (ARCHIVE_WARN); } return (r); } /* * Device entries have one of the following forms: * - raw dev_t * - format,major,minor[,subdevice] * When parsing succeeded, `pdev' will contain the appropriate dev_t value. */ /* strsep() is not in C90, but strcspn() is. */ /* Taken from http://unixpapa.com/incnote/string.html */ static char * la_strsep(char **sp, const char *sep) { char *p, *s; if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL); s = *sp; p = s + strcspn(s, sep); if (*p != '\0') *p++ = '\0'; *sp = p; return(s); } static int parse_device(dev_t *pdev, struct archive *a, char *val) { #define MAX_PACK_ARGS 3 unsigned long numbers[MAX_PACK_ARGS]; char *p, *dev; int argc; pack_t *pack; dev_t result; const char *error = NULL; memset(pdev, 0, sizeof(*pdev)); if ((dev = strchr(val, ',')) != NULL) { /* * Device's major/minor are given in a specified format. * Decode and pack it accordingly. */ *dev++ = '\0'; if ((pack = pack_find(val)) == NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown format `%s'", val); return ARCHIVE_WARN; } argc = 0; while ((p = la_strsep(&dev, ",")) != NULL) { if (*p == '\0') { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Missing number"); return ARCHIVE_WARN; } if (argc >= MAX_PACK_ARGS) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Too many arguments"); return ARCHIVE_WARN; } numbers[argc++] = (unsigned long)mtree_atol(&p, 0); } if (argc < 2) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Not enough arguments"); return ARCHIVE_WARN; } result = (*pack)(argc, numbers, &error); if (error != NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "%s", error); return ARCHIVE_WARN; } } else { /* file system raw value. */ result = (dev_t)mtree_atol(&val, 0); } *pdev = result; return ARCHIVE_OK; #undef MAX_PACK_ARGS } +static int +parse_hex_nibble(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; +#if 0 + /* XXX: Is uppercase something we should support? */ + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; +#endif + + return -1; +} + +static int +parse_digest(struct archive_read *a, struct archive_entry *entry, + const char *digest, int type) +{ + unsigned char digest_buf[64]; + int high, low; + size_t i, j, len; + + switch (type) { + case ARCHIVE_ENTRY_DIGEST_MD5: + len = sizeof(entry->digest.md5); + break; + case ARCHIVE_ENTRY_DIGEST_RMD160: + len = sizeof(entry->digest.rmd160); + break; + case ARCHIVE_ENTRY_DIGEST_SHA1: + len = sizeof(entry->digest.sha1); + break; + case ARCHIVE_ENTRY_DIGEST_SHA256: + len = sizeof(entry->digest.sha256); + break; + case ARCHIVE_ENTRY_DIGEST_SHA384: + len = sizeof(entry->digest.sha384); + break; + case ARCHIVE_ENTRY_DIGEST_SHA512: + len = sizeof(entry->digest.sha512); + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: Unknown digest type"); + return ARCHIVE_FATAL; + } + + if (len > sizeof(digest_buf)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: Digest storage too large"); + return ARCHIVE_FATAL; + } + + len *= 2; + + if (mtree_strnlen(digest, len+1) != len) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "incorrect digest length, ignoring"); + return ARCHIVE_WARN; + } + + for (i = 0, j = 0; i < len; i += 2, j++) { + high = parse_hex_nibble(digest[i]); + low = parse_hex_nibble(digest[i+1]); + if (high == -1 || low == -1) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "invalid digest data, ignoring"); + return ARCHIVE_WARN; + } + + digest_buf[j] = high << 4 | low; + } + + return archive_entry_set_digest(entry, type, digest_buf); +} + /* * Parse a single keyword and its value. */ static int parse_keyword(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws) { char *val, *key; key = opt->value; if (*key == '\0') return (ARCHIVE_OK); if (strcmp(key, "nochange") == 0) { *parsed_kws |= MTREE_HAS_NOCHANGE; return (ARCHIVE_OK); } if (strcmp(key, "optional") == 0) { *parsed_kws |= MTREE_HAS_OPTIONAL; return (ARCHIVE_OK); } if (strcmp(key, "ignore") == 0) { /* * The mtree processing is not recursive, so * recursion will only happen for explicitly listed * entries. */ return (ARCHIVE_OK); } val = strchr(key, '='); if (val == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed attribute \"%s\" (%d)", key, key[0]); return (ARCHIVE_WARN); } *val = '\0'; ++val; switch (key[0]) { case 'c': if (strcmp(key, "content") == 0 || strcmp(key, "contents") == 0) { parse_escapes(val, NULL); archive_strcpy(&mtree->contents_name, val); break; } if (strcmp(key, "cksum") == 0) break; __LA_FALLTHROUGH; case 'd': if (strcmp(key, "device") == 0) { /* stat(2) st_rdev field, e.g. the major/minor IDs * of a char/block special file */ int r; dev_t dev; *parsed_kws |= MTREE_HAS_DEVICE; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_rdev(entry, dev); return r; } __LA_FALLTHROUGH; case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); break; } __LA_FALLTHROUGH; case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol(&val, 10)); break; } if (strcmp(key, "gname") == 0) { *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); break; } __LA_FALLTHROUGH; case 'i': if (strcmp(key, "inode") == 0) { archive_entry_set_ino(entry, mtree_atol(&val, 10)); break; } __LA_FALLTHROUGH; case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); break; } __LA_FALLTHROUGH; case 'm': - if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) - break; + if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) { + return parse_digest(a, entry, val, + ARCHIVE_ENTRY_DIGEST_MD5); + } if (strcmp(key, "mode") == 0) { if (val[0] >= '0' && val[0] <= '7') { *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, (mode_t)mtree_atol(&val, 8)); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic or non-octal mode \"%s\" unsupported", val); return ARCHIVE_WARN; } break; } __LA_FALLTHROUGH; case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; archive_entry_set_nlink(entry, (unsigned int)mtree_atol(&val, 10)); break; } __LA_FALLTHROUGH; case 'r': if (strcmp(key, "resdevice") == 0) { /* stat(2) st_dev field, e.g. the device ID where the * inode resides */ int r; dev_t dev; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_dev(entry, dev); return r; } if (strcmp(key, "rmd160") == 0 || - strcmp(key, "rmd160digest") == 0) - break; + strcmp(key, "rmd160digest") == 0) { + return parse_digest(a, entry, val, + ARCHIVE_ENTRY_DIGEST_RMD160); + } __LA_FALLTHROUGH; case 's': - if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) - break; + if (strcmp(key, "sha1") == 0 || + strcmp(key, "sha1digest") == 0) { + return parse_digest(a, entry, val, + ARCHIVE_ENTRY_DIGEST_SHA1); + } if (strcmp(key, "sha256") == 0 || - strcmp(key, "sha256digest") == 0) - break; + strcmp(key, "sha256digest") == 0) { + return parse_digest(a, entry, val, + ARCHIVE_ENTRY_DIGEST_SHA256); + } if (strcmp(key, "sha384") == 0 || - strcmp(key, "sha384digest") == 0) - break; + strcmp(key, "sha384digest") == 0) { + return parse_digest(a, entry, val, + ARCHIVE_ENTRY_DIGEST_SHA384); + } if (strcmp(key, "sha512") == 0 || - strcmp(key, "sha512digest") == 0) - break; + strcmp(key, "sha512digest") == 0) { + return parse_digest(a, entry, val, + ARCHIVE_ENTRY_DIGEST_SHA512); + } if (strcmp(key, "size") == 0) { archive_entry_set_size(entry, mtree_atol(&val, 10)); break; } __LA_FALLTHROUGH; case 't': if (strcmp(key, "tags") == 0) { /* * Comma delimited list of tags. * Ignore the tags for now, but the interface * should be extended to allow inclusion/exclusion. */ break; } if (strcmp(key, "time") == 0) { int64_t m; int64_t my_time_t_max = get_time_t_max(); int64_t my_time_t_min = get_time_t_min(); long ns = 0; *parsed_kws |= MTREE_HAS_MTIME; m = mtree_atol(&val, 10); /* Replicate an old mtree bug: * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { ++val; ns = (long)mtree_atol(&val, 10); if (ns < 0) ns = 0; else if (ns > 999999999) ns = 999999999; } if (m > my_time_t_max) m = my_time_t_max; else if (m < my_time_t_min) m = my_time_t_min; archive_entry_set_mtime(entry, (time_t)m, ns); break; } if (strcmp(key, "type") == 0) { switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { archive_entry_set_filetype(entry, AE_IFBLK); break; } __LA_FALLTHROUGH; case 'c': if (strcmp(val, "char") == 0) { archive_entry_set_filetype(entry, AE_IFCHR); break; } __LA_FALLTHROUGH; case 'd': if (strcmp(val, "dir") == 0) { archive_entry_set_filetype(entry, AE_IFDIR); break; } __LA_FALLTHROUGH; case 'f': if (strcmp(val, "fifo") == 0) { archive_entry_set_filetype(entry, AE_IFIFO); break; } if (strcmp(val, "file") == 0) { archive_entry_set_filetype(entry, AE_IFREG); break; } __LA_FALLTHROUGH; case 'l': if (strcmp(val, "link") == 0) { archive_entry_set_filetype(entry, AE_IFLNK); break; } __LA_FALLTHROUGH; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized file type \"%s\"; " "assuming \"file\"", val); archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_WARN); } *parsed_kws |= MTREE_HAS_TYPE; break; } __LA_FALLTHROUGH; case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol(&val, 10)); break; } if (strcmp(key, "uname") == 0) { *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); break; } __LA_FALLTHROUGH; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { size_t bytes_to_read; ssize_t bytes_read; struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd < 0) { *buff = NULL; *offset = 0; *size = 0; return (ARCHIVE_EOF); } if (mtree->buff == NULL) { mtree->buffsize = 64 * 1024; mtree->buff = malloc(mtree->buffsize); if (mtree->buff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } *buff = mtree->buff; *offset = mtree->offset; if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset) bytes_to_read = (size_t)(mtree->cur_size - mtree->offset); else bytes_to_read = mtree->buffsize; bytes_read = read(mtree->fd, mtree->buff, bytes_to_read); if (bytes_read < 0) { archive_set_error(&a->archive, errno, "Can't read"); return (ARCHIVE_WARN); } if (bytes_read == 0) { *size = 0; return (ARCHIVE_EOF); } mtree->offset += bytes_read; *size = bytes_read; return (ARCHIVE_OK); } /* Skip does nothing except possibly close the contents file. */ static int skip(struct archive_read *a) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } return (ARCHIVE_OK); } /* * Since parsing backslash sequences always makes strings shorter, * we can always do this conversion in-place. */ static void parse_escapes(char *src, struct mtree_entry *mentry) { char *dest = src; char c; if (mentry != NULL && strcmp(src, ".") == 0) mentry->full = 1; while (*src != '\0') { c = *src++; if (c == '/' && mentry != NULL) mentry->full = 1; if (c == '\\') { switch (src[0]) { case '0': if (src[1] < '0' || src[1] > '7') { c = 0; ++src; break; } /* FALLTHROUGH */ case '1': case '2': case '3': if (src[1] >= '0' && src[1] <= '7' && src[2] >= '0' && src[2] <= '7') { c = (src[0] - '0') << 6; c |= (src[1] - '0') << 3; c |= (src[2] - '0'); src += 3; } break; case 'a': c = '\a'; ++src; break; case 'b': c = '\b'; ++src; break; case 'f': c = '\f'; ++src; break; case 'n': c = '\n'; ++src; break; case 'r': c = '\r'; ++src; break; case 's': c = ' '; ++src; break; case 't': c = '\t'; ++src; break; case 'v': c = '\v'; ++src; break; case '\\': c = '\\'; ++src; break; } } *dest++ = c; } *dest = '\0'; } /* Parse a hex digit. */ static int parsedigit(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') return c - 'a'; else if (c >= 'A' && c <= 'F') return c - 'A'; else return -1; } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t mtree_atol(char **p, int base) { int64_t l, limit; int digit, last_digit_limit; if (base == 0) { if (**p != '0') base = 10; else if ((*p)[1] == 'x' || (*p)[1] == 'X') { *p += 2; base = 16; } else { base = 8; } } if (**p == '-') { limit = INT64_MIN / base; last_digit_limit = INT64_MIN % base; ++(*p); l = 0; digit = parsedigit(**p); while (digit >= 0 && digit < base) { if (l < limit || (l == limit && digit > last_digit_limit)) return INT64_MIN; l = (l * base) - digit; digit = parsedigit(*++(*p)); } return l; } else { limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; l = 0; digit = parsedigit(**p); while (digit >= 0 && digit < base) { if (l > limit || (l == limit && digit > last_digit_limit)) return INT64_MAX; l = (l * base) + digit; digit = parsedigit(*++(*p)); } return l; } } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. */ static ssize_t readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; const void *t; void *nl; char *u; /* Accumulate line in a line buffer. */ for (;;) { /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (t == NULL) return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); nl = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read to end exactly there. */ if (nl != NULL) { bytes_read = ((const char *)nl) - ((const char *)t) + 1; } if (total_size + bytes_read + 1 > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&mtree->line, total_size + bytes_read + 1) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } /* Append new bytes to string. */ memcpy(mtree->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; mtree->line.s[total_size] = '\0'; for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { /* Ends with unescaped newline. */ *start = mtree->line.s; return total_size; } else if (u[0] == '#') { /* Ends with comment sequence #...\n */ if (nl == NULL) { /* But we've not found the \n yet */ break; } } else if (u[0] == '\\') { if (u[1] == '\n') { /* Trim escaped newline. */ total_size -= 2; mtree->line.s[total_size] = '\0'; break; } else if (u[1] != '\0') { /* Skip the two-char escape sequence */ ++u; } } } find_off = u - mtree->line.s; } } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_rar.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_rar.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_rar.c (revision 368707) @@ -1,2999 +1,3005 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Andres Mejia * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #include #include #ifdef HAVE_ZLIB_H #include /* crc32 */ #endif #include "archive.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" /* RAR signature, also known as the mark header */ #define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00" /* Header types */ #define MARK_HEAD 0x72 #define MAIN_HEAD 0x73 #define FILE_HEAD 0x74 #define COMM_HEAD 0x75 #define AV_HEAD 0x76 #define SUB_HEAD 0x77 #define PROTECT_HEAD 0x78 #define SIGN_HEAD 0x79 #define NEWSUB_HEAD 0x7a #define ENDARC_HEAD 0x7b /* Main Header Flags */ #define MHD_VOLUME 0x0001 #define MHD_COMMENT 0x0002 #define MHD_LOCK 0x0004 #define MHD_SOLID 0x0008 #define MHD_NEWNUMBERING 0x0010 #define MHD_AV 0x0020 #define MHD_PROTECT 0x0040 #define MHD_PASSWORD 0x0080 #define MHD_FIRSTVOLUME 0x0100 #define MHD_ENCRYPTVER 0x0200 /* Flags common to all headers */ #define HD_MARKDELETION 0x4000 #define HD_ADD_SIZE_PRESENT 0x8000 /* File Header Flags */ #define FHD_SPLIT_BEFORE 0x0001 #define FHD_SPLIT_AFTER 0x0002 #define FHD_PASSWORD 0x0004 #define FHD_COMMENT 0x0008 #define FHD_SOLID 0x0010 #define FHD_LARGE 0x0100 #define FHD_UNICODE 0x0200 #define FHD_SALT 0x0400 #define FHD_VERSION 0x0800 #define FHD_EXTTIME 0x1000 #define FHD_EXTFLAGS 0x2000 /* File dictionary sizes */ #define DICTIONARY_SIZE_64 0x00 #define DICTIONARY_SIZE_128 0x20 #define DICTIONARY_SIZE_256 0x40 #define DICTIONARY_SIZE_512 0x60 #define DICTIONARY_SIZE_1024 0x80 #define DICTIONARY_SIZE_2048 0xA0 #define DICTIONARY_SIZE_4096 0xC0 #define FILE_IS_DIRECTORY 0xE0 #define DICTIONARY_MASK FILE_IS_DIRECTORY /* OS Flags */ #define OS_MSDOS 0 #define OS_OS2 1 #define OS_WIN32 2 #define OS_UNIX 3 #define OS_MAC_OS 4 #define OS_BEOS 5 /* Compression Methods */ #define COMPRESS_METHOD_STORE 0x30 /* LZSS */ #define COMPRESS_METHOD_FASTEST 0x31 #define COMPRESS_METHOD_FAST 0x32 #define COMPRESS_METHOD_NORMAL 0x33 /* PPMd Variant H */ #define COMPRESS_METHOD_GOOD 0x34 #define COMPRESS_METHOD_BEST 0x35 #define CRC_POLYNOMIAL 0xEDB88320 #define NS_UNIT 10000000 #define DICTIONARY_MAX_SIZE 0x400000 #define MAINCODE_SIZE 299 #define OFFSETCODE_SIZE 60 #define LOWOFFSETCODE_SIZE 17 #define LENGTHCODE_SIZE 28 #define HUFFMAN_TABLE_SIZE \ MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE #define MAX_SYMBOL_LENGTH 0xF #define MAX_SYMBOLS 20 /* * Considering L1,L2 cache miss and a calling of write system-call, * the best size of the output buffer(uncompressed buffer) is 128K. * If the structure of extracting process is changed, this value * might be researched again. */ #define UNP_BUFFER_SIZE (128 * 1024) /* Define this here for non-Windows platforms */ #if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)) #define FILE_ATTRIBUTE_DIRECTORY 0x10 #endif #undef minimum #define minimum(a, b) ((a)<(b)?(a):(b)) +/* Stack overflow check */ +#define MAX_COMPRESS_DEPTH 1024 + /* Fields common to all headers */ struct rar_header { char crc[2]; char type; char flags[2]; char size[2]; }; /* Fields common to all file headers */ struct rar_file_header { char pack_size[4]; char unp_size[4]; char host_os; char file_crc[4]; char file_time[4]; char unp_ver; char method; char name_size[2]; char file_attr[4]; }; struct huffman_tree_node { int branches[2]; }; struct huffman_table_entry { unsigned int length; int value; }; struct huffman_code { struct huffman_tree_node *tree; int numentries; int numallocatedentries; int minlength; int maxlength; int tablesize; struct huffman_table_entry *table; }; struct lzss { unsigned char *window; int mask; int64_t position; }; struct data_block_offsets { int64_t header_size; int64_t start_offset; int64_t end_offset; }; struct rar { /* Entries from main RAR header */ unsigned main_flags; unsigned long file_crc; char reserved1[2]; char reserved2[4]; char encryptver; /* File header entries */ char compression_method; unsigned file_flags; int64_t packed_size; int64_t unp_size; time_t mtime; long mnsec; mode_t mode; char *filename; char *filename_save; size_t filename_save_size; size_t filename_allocated; /* File header optional entries */ char salt[8]; time_t atime; long ansec; time_t ctime; long cnsec; time_t arctime; long arcnsec; /* Fields to help with tracking decompression of files. */ int64_t bytes_unconsumed; int64_t bytes_remaining; int64_t bytes_uncopied; int64_t offset; int64_t offset_outgoing; int64_t offset_seek; char valid; unsigned int unp_offset; unsigned int unp_buffer_size; unsigned char *unp_buffer; unsigned int dictionary_size; char start_new_block; char entry_eof; unsigned long crc_calculated; int found_first_header; char has_endarc_header; struct data_block_offsets *dbo; unsigned int cursor; unsigned int nodes; char filename_must_match; /* LZSS members */ struct huffman_code maincode; struct huffman_code offsetcode; struct huffman_code lowoffsetcode; struct huffman_code lengthcode; unsigned char lengthtable[HUFFMAN_TABLE_SIZE]; struct lzss lzss; char output_last_match; unsigned int lastlength; unsigned int lastoffset; unsigned int oldoffset[4]; unsigned int lastlowoffset; unsigned int numlowoffsetrepeats; int64_t filterstart; char start_new_table; /* PPMd Variant H members */ char ppmd_valid; char ppmd_eod; char is_ppmd_block; int ppmd_escape; CPpmd7 ppmd7_context; CPpmd7z_RangeDec range_dec; IByteIn bytein; /* * String conversion object. */ int init_default_conversion; struct archive_string_conv *sconv_default; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_utf8; struct archive_string_conv *sconv_utf16be; /* * Bit stream reader. */ struct rar_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; ssize_t avail_in; const unsigned char *next_in; } br; /* * Custom field to denote that this archive contains encrypted entries */ int has_encrypted_entries; }; static int archive_read_support_format_rar_capabilities(struct archive_read *); static int archive_read_format_rar_has_encrypted_entries(struct archive_read *); static int archive_read_format_rar_bid(struct archive_read *, int); static int archive_read_format_rar_options(struct archive_read *, const char *, const char *); static int archive_read_format_rar_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_rar_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_rar_read_data_skip(struct archive_read *a); static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t, int); static int archive_read_format_rar_cleanup(struct archive_read *); /* Support functions */ static int read_header(struct archive_read *, struct archive_entry *, char); static time_t get_time(int); static int read_exttime(const char *, struct rar *, const char *); static int read_symlink_stored(struct archive_read *, struct archive_entry *, struct archive_string_conv *); static int read_data_stored(struct archive_read *, const void **, size_t *, int64_t *); static int read_data_compressed(struct archive_read *, const void **, size_t *, - int64_t *); + int64_t *, size_t); static int rar_br_preparation(struct archive_read *, struct rar_br *); static int parse_codes(struct archive_read *); static void free_codes(struct archive_read *); static int read_next_symbol(struct archive_read *, struct huffman_code *); static int create_code(struct archive_read *, struct huffman_code *, unsigned char *, int, char); static int add_value(struct archive_read *, struct huffman_code *, int, int, int); static int new_node(struct huffman_code *); static int make_table(struct archive_read *, struct huffman_code *); static int make_table_recurse(struct archive_read *, struct huffman_code *, int, struct huffman_table_entry *, int, int); static int64_t expand(struct archive_read *, int64_t); static int copy_from_lzss_window(struct archive_read *, const void **, int64_t, int); static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *); /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define rar_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define rar_br_bits(br, n) \ (((uint32_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define rar_br_bits_forced(br, n) \ (((uint32_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : there is no data in the stream. */ #define rar_br_read_ahead(a, br, n) \ ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n))) /* Notify how many bits we consumed. */ #define rar_br_consume(br, n) ((br)->cache_avail -= (n)) #define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7) static const uint32_t cache_masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int rar_br_fillup(struct archive_read *a, struct rar_br *br) { struct rar *rar = (struct rar *)(a->format->data); int n = CACHE_BITS - br->cache_avail; for (;;) { switch (n >> 3) { case 8: if (br->avail_in >= 8) { br->cache_buffer = ((uint64_t)br->next_in[0]) << 56 | ((uint64_t)br->next_in[1]) << 48 | ((uint64_t)br->next_in[2]) << 40 | ((uint64_t)br->next_in[3]) << 32 | ((uint32_t)br->next_in[4]) << 24 | ((uint32_t)br->next_in[5]) << 16 | ((uint32_t)br->next_in[6]) << 8 | (uint32_t)br->next_in[7]; br->next_in += 8; br->avail_in -= 8; br->cache_avail += 8 * 8; rar->bytes_unconsumed += 8; rar->bytes_remaining -= 8; return (1); } break; case 7: if (br->avail_in >= 7) { br->cache_buffer = (br->cache_buffer << 56) | ((uint64_t)br->next_in[0]) << 48 | ((uint64_t)br->next_in[1]) << 40 | ((uint64_t)br->next_in[2]) << 32 | ((uint32_t)br->next_in[3]) << 24 | ((uint32_t)br->next_in[4]) << 16 | ((uint32_t)br->next_in[5]) << 8 | (uint32_t)br->next_in[6]; br->next_in += 7; br->avail_in -= 7; br->cache_avail += 7 * 8; rar->bytes_unconsumed += 7; rar->bytes_remaining -= 7; return (1); } break; case 6: if (br->avail_in >= 6) { br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)br->next_in[0]) << 40 | ((uint64_t)br->next_in[1]) << 32 | ((uint32_t)br->next_in[2]) << 24 | ((uint32_t)br->next_in[3]) << 16 | ((uint32_t)br->next_in[4]) << 8 | (uint32_t)br->next_in[5]; br->next_in += 6; br->avail_in -= 6; br->cache_avail += 6 * 8; rar->bytes_unconsumed += 6; rar->bytes_remaining -= 6; return (1); } break; case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } if (br->avail_in <= 0) { if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor * actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); if (br->next_in == NULL) return (0); if (br->avail_in == 0) return (0); } br->cache_buffer = (br->cache_buffer << 8) | *br->next_in++; br->avail_in--; br->cache_avail += 8; n -= 8; rar->bytes_unconsumed++; rar->bytes_remaining--; } } static int rar_br_preparation(struct archive_read *a, struct rar_br *br) { struct rar *rar = (struct rar *)(a->format->data); if (rar->bytes_remaining > 0) { br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); if (br->next_in == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } if (br->cache_avail == 0) (void)rar_br_fillup(a, br); } return (ARCHIVE_OK); } /* Find last bit set */ static inline int rar_fls(unsigned int word) { word |= (word >> 1); word |= (word >> 2); word |= (word >> 4); word |= (word >> 8); word |= (word >> 16); return word - (word >> 1); } /* LZSS functions */ static inline int64_t lzss_position(struct lzss *lzss) { return lzss->position; } static inline int lzss_mask(struct lzss *lzss) { return lzss->mask; } static inline int lzss_size(struct lzss *lzss) { return lzss->mask + 1; } static inline int lzss_offset_for_position(struct lzss *lzss, int64_t pos) { return (int)(pos & lzss->mask); } static inline unsigned char * lzss_pointer_for_position(struct lzss *lzss, int64_t pos) { return &lzss->window[lzss_offset_for_position(lzss, pos)]; } static inline int lzss_current_offset(struct lzss *lzss) { return lzss_offset_for_position(lzss, lzss->position); } static inline uint8_t * lzss_current_pointer(struct lzss *lzss) { return lzss_pointer_for_position(lzss, lzss->position); } static inline void lzss_emit_literal(struct rar *rar, uint8_t literal) { *lzss_current_pointer(&rar->lzss) = literal; rar->lzss.position++; } static inline void lzss_emit_match(struct rar *rar, int offset, int length) { int dstoffs = lzss_current_offset(&rar->lzss); int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss); int l, li, remaining; unsigned char *d, *s; remaining = length; while (remaining > 0) { l = remaining; if (dstoffs > srcoffs) { if (l > lzss_size(&rar->lzss) - dstoffs) l = lzss_size(&rar->lzss) - dstoffs; } else { if (l > lzss_size(&rar->lzss) - srcoffs) l = lzss_size(&rar->lzss) - srcoffs; } d = &(rar->lzss.window[dstoffs]); s = &(rar->lzss.window[srcoffs]); if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs)) memcpy(d, s, l); else { for (li = 0; li < l; li++) d[li] = s[li]; } remaining -= l; dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss)); srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss)); } rar->lzss.position += length; } static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); Byte b; if (!rar_br_read_ahead(a, br, 8)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return 0; } b = rar_br_bits(br, 8); rar_br_consume(br, 8); return b; } int archive_read_support_format_rar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct rar *rar; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar"); rar = (struct rar *)calloc(sizeof(*rar), 1); if (rar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data"); return (ARCHIVE_FATAL); } /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; r = __archive_read_register_format(a, rar, "rar", archive_read_format_rar_bid, archive_read_format_rar_options, archive_read_format_rar_read_header, archive_read_format_rar_read_data, archive_read_format_rar_read_data_skip, archive_read_format_rar_seek_data, archive_read_format_rar_cleanup, archive_read_support_format_rar_capabilities, archive_read_format_rar_has_encrypted_entries); if (r != ARCHIVE_OK) free(rar); return (r); } static int archive_read_support_format_rar_capabilities(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_rar_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct rar * rar = (struct rar *)_a->format->data; if (rar) { return rar->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_rar_bid(struct archive_read *a, int best_bid) { const char *p; /* If there's already a bid > 30, we'll never win. */ if (best_bid > 30) return (-1); if ((p = __archive_read_ahead(a, 7, NULL)) == NULL) return (-1); if (memcmp(p, RAR_SIGNATURE, 7) == 0) return (30); if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { /* This is a PE file */ ssize_t offset = 0x10000; ssize_t window = 4096; ssize_t bytes_avail; while (offset + window <= (1024 * 128)) { const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) return (0); continue; } p = buff + offset; while (p + 7 < buff + bytes_avail) { if (memcmp(p, RAR_SIGNATURE, 7) == 0) return (30); p += 0x10; } offset = p - buff; } } return (0); } static int skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, total; ssize_t bytes, window; total = 0; window = 4096; while (total + window <= (1024 * 128)) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) goto fatal; continue; } if (bytes < 0x40) goto fatal; p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the RAR header. */ while (p + 7 < q) { if (memcmp(p, RAR_SIGNATURE, 7) == 0) { skip = p - (const char *)h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += 0x10; } skip = p - (const char *)h; __archive_read_consume(a, skip); total += skip; } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out RAR header"); return (ARCHIVE_FATAL); } static int archive_read_format_rar_options(struct archive_read *a, const char *key, const char *val) { struct rar *rar; int ret = ARCHIVE_FAILED; rar = (struct rar *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "rar: hdrcharset option needs a character-set name"); else { rar->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (rar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_read_format_rar_read_header(struct archive_read *a, struct archive_entry *entry) { const void *h; const char *p; struct rar *rar; size_t skip; char head_type; int ret; unsigned flags; unsigned long crc32_expected; a->archive.archive_format = ARCHIVE_FORMAT_RAR; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "RAR"; rar = (struct rar *)(a->format->data); /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { rar->has_encrypted_entries = 0; } /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if * this fails. */ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (ARCHIVE_EOF); p = h; if (rar->found_first_header == 0 && ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) { /* This is an executable ? Must be self-extracting... */ ret = skip_sfx(a); if (ret < ARCHIVE_WARN) return (ret); } rar->found_first_header = 1; while (1) { unsigned long crc32_val; if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; head_type = p[2]; switch(head_type) { case MARK_HEAD: if (memcmp(p, RAR_SIGNATURE, 7) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid marker header"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 7); break; case MAIN_HEAD: rar->main_flags = archive_le16dec(p + 3); skip = archive_le16dec(p + 5); if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, skip, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1)); memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1), sizeof(rar->reserved2)); if (rar->main_flags & MHD_ENCRYPTVER) { if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } rar->encryptver = *(p + 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)); } /* Main header is password encrypted, so we cannot read any file names or any other info about files from the header. */ if (rar->main_flags & MHD_PASSWORD) { archive_entry_set_is_metadata_encrypted(entry, 1); archive_entry_set_is_data_encrypted(entry, 1); rar->has_encrypted_entries = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR encryption support unavailable."); return (ARCHIVE_FATAL); } crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); if ((crc32_val & 0xffff) != archive_le16dec(p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } __archive_read_consume(a, skip); break; case FILE_HEAD: return read_header(a, entry, head_type); case COMM_HEAD: case AV_HEAD: case SUB_HEAD: case PROTECT_HEAD: case SIGN_HEAD: case ENDARC_HEAD: flags = archive_le16dec(p + 3); skip = archive_le16dec(p + 5); if (skip < 7) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size too small"); return (ARCHIVE_FATAL); } if (flags & HD_ADD_SIZE_PRESENT) { if (skip < 7 + 4) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size too small"); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, skip, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; skip += archive_le32dec(p + 7); } /* Skip over the 2-byte CRC at the beginning of the header. */ crc32_expected = archive_le16dec(p); __archive_read_consume(a, 2); skip -= 2; /* Skim the entire header and compute the CRC. */ crc32_val = 0; while (skip > 0) { size_t to_read = skip; ssize_t did_read; if (to_read > 32 * 1024) { to_read = 32 * 1024; } if ((h = __archive_read_ahead(a, to_read, &did_read)) == NULL) { return (ARCHIVE_FATAL); } p = h; crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned)did_read); __archive_read_consume(a, did_read); skip -= did_read; } if ((crc32_val & 0xffff) != crc32_expected) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } if (head_type == ENDARC_HEAD) return (ARCHIVE_EOF); break; case NEWSUB_HEAD: if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN) return ret; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file"); return (ARCHIVE_FATAL); } } } static int archive_read_format_rar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct rar *rar = (struct rar *)(a->format->data); int ret; if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { rar->has_encrypted_entries = 0; } if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } *buff = NULL; if (rar->entry_eof || rar->offset_seek >= rar->unp_size) { *size = 0; *offset = rar->offset; if (*offset < rar->unp_size) *offset = rar->unp_size; return (ARCHIVE_EOF); } switch (rar->compression_method) { case COMPRESS_METHOD_STORE: ret = read_data_stored(a, buff, size, offset); break; case COMPRESS_METHOD_FASTEST: case COMPRESS_METHOD_FAST: case COMPRESS_METHOD_NORMAL: case COMPRESS_METHOD_GOOD: case COMPRESS_METHOD_BEST: - ret = read_data_compressed(a, buff, size, offset); + ret = read_data_compressed(a, buff, size, offset, 0); if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) { __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->start_new_table = 1; rar->ppmd_valid = 0; } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported compression method for RAR file."); ret = ARCHIVE_FATAL; break; } return (ret); } static int archive_read_format_rar_read_data_skip(struct archive_read *a) { struct rar *rar; int64_t bytes_skipped; int ret; rar = (struct rar *)(a->format->data); if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } if (rar->bytes_remaining > 0) { bytes_skipped = __archive_read_consume(a, rar->bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); } /* Compressed data to skip must be read from each header in a multivolume * archive. */ if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER) { ret = archive_read_format_rar_read_header(a, a->entry); if (ret == (ARCHIVE_EOF)) ret = archive_read_format_rar_read_header(a, a->entry); if (ret != (ARCHIVE_OK)) return ret; return archive_read_format_rar_read_data_skip(a); } return (ARCHIVE_OK); } static int64_t archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, int whence) { int64_t client_offset, ret; unsigned int i; struct rar *rar = (struct rar *)(a->format->data); if (rar->compression_method == COMPRESS_METHOD_STORE) { /* Modify the offset for use with SEEK_SET */ switch (whence) { case SEEK_CUR: client_offset = rar->offset_seek; break; case SEEK_END: client_offset = rar->unp_size; break; case SEEK_SET: default: client_offset = 0; } client_offset += offset; if (client_offset < 0) { /* Can't seek past beginning of data block */ return -1; } else if (client_offset > rar->unp_size) { /* * Set the returned offset but only seek to the end of * the data block. */ rar->offset_seek = client_offset; client_offset = rar->unp_size; } client_offset += rar->dbo[0].start_offset; i = 0; while (i < rar->cursor) { i++; client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset; } if (rar->main_flags & MHD_VOLUME) { /* Find the appropriate offset among the multivolume archive */ while (1) { if (client_offset < rar->dbo[rar->cursor].start_offset && rar->file_flags & FHD_SPLIT_BEFORE) { /* Search backwards for the correct data block */ if (rar->cursor == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Attempt to seek past beginning of RAR data block"); return (ARCHIVE_FAILED); } rar->cursor--; client_offset -= rar->dbo[rar->cursor+1].start_offset - rar->dbo[rar->cursor].end_offset; if (client_offset < rar->dbo[rar->cursor].start_offset) continue; ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor].header_size, SEEK_SET); if (ret < (ARCHIVE_OK)) return ret; ret = archive_read_format_rar_read_header(a, a->entry); if (ret != (ARCHIVE_OK)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error during seek of RAR file"); return (ARCHIVE_FAILED); } rar->cursor--; break; } else if (client_offset > rar->dbo[rar->cursor].end_offset && rar->file_flags & FHD_SPLIT_AFTER) { /* Search forward for the correct data block */ rar->cursor++; if (rar->cursor < rar->nodes && client_offset > rar->dbo[rar->cursor].end_offset) { client_offset += rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor-1].end_offset; continue; } rar->cursor--; ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset, SEEK_SET); if (ret < (ARCHIVE_OK)) return ret; ret = archive_read_format_rar_read_header(a, a->entry); if (ret == (ARCHIVE_EOF)) { rar->has_endarc_header = 1; ret = archive_read_format_rar_read_header(a, a->entry); } if (ret != (ARCHIVE_OK)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error during seek of RAR file"); return (ARCHIVE_FAILED); } client_offset += rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor-1].end_offset; continue; } break; } } ret = __archive_read_seek(a, client_offset, SEEK_SET); if (ret < (ARCHIVE_OK)) return ret; rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret; i = rar->cursor; while (i > 0) { i--; ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset; } ret -= rar->dbo[0].start_offset; /* Always restart reading the file after a seek */ __archive_reset_read_data(&a->archive); rar->bytes_unconsumed = 0; rar->offset = 0; /* * If a seek past the end of file was requested, return the requested * offset. */ if (ret == rar->unp_size && rar->offset_seek > rar->unp_size) return rar->offset_seek; /* Return the new offset */ rar->offset_seek = ret; return rar->offset_seek; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seeking of compressed RAR files is unsupported"); } return (ARCHIVE_FAILED); } static int archive_read_format_rar_cleanup(struct archive_read *a) { struct rar *rar; rar = (struct rar *)(a->format->data); free_codes(a); free(rar->filename); free(rar->filename_save); free(rar->dbo); free(rar->unp_buffer); free(rar->lzss.window); __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); free(rar); (a->format->data) = NULL; return (ARCHIVE_OK); } static int read_header(struct archive_read *a, struct archive_entry *entry, char head_type) { const void *h; const char *p, *endp; struct rar *rar; struct rar_header rar_header; struct rar_file_header file_header; int64_t header_size; unsigned filename_size, end; char *filename; char *strp; char packed_size[8]; char unp_size[8]; int ttime; struct archive_string_conv *sconv, *fn_sconv; unsigned long crc32_val; int ret = (ARCHIVE_OK), ret2; rar = (struct rar *)(a->format->data); /* Setup a string conversion object for non-rar-unicode filenames. */ sconv = rar->opt_sconv; if (sconv == NULL) { if (!rar->init_default_conversion) { rar->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); rar->init_default_conversion = 1; } sconv = rar->sconv_default; } if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; memcpy(&rar_header, p, sizeof(rar_header)); rar->file_flags = archive_le16dec(rar_header.flags); header_size = archive_le16dec(rar_header.size); if (header_size < (int64_t)sizeof(file_header) + 7) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2); __archive_read_consume(a, 7); if (!(rar->file_flags & FHD_SOLID)) { rar->compression_method = 0; rar->packed_size = 0; rar->unp_size = 0; rar->mtime = 0; rar->ctime = 0; rar->atime = 0; rar->arctime = 0; rar->mode = 0; memset(&rar->salt, 0, sizeof(rar->salt)); rar->atime = 0; rar->ansec = 0; rar->ctime = 0; rar->cnsec = 0; rar->mtime = 0; rar->mnsec = 0; rar->arctime = 0; rar->arcnsec = 0; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR solid archive support unavailable."); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) return (ARCHIVE_FATAL); /* File Header CRC check. */ crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7)); if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } /* If no CRC error, Go on parsing File Header. */ p = h; endp = p + header_size - 7; memcpy(&file_header, p, sizeof(file_header)); p += sizeof(file_header); rar->compression_method = file_header.method; ttime = archive_le32dec(file_header.file_time); rar->mtime = get_time(ttime); rar->file_crc = archive_le32dec(file_header.file_crc); if (rar->file_flags & FHD_PASSWORD) { archive_entry_set_is_data_encrypted(entry, 1); rar->has_encrypted_entries = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR encryption support unavailable."); /* Since it is only the data part itself that is encrypted we can at least extract information about the currently processed entry and don't need to return ARCHIVE_FATAL here. */ /*return (ARCHIVE_FATAL);*/ } if (rar->file_flags & FHD_LARGE) { memcpy(packed_size, file_header.pack_size, 4); memcpy(packed_size + 4, p, 4); /* High pack size */ p += 4; memcpy(unp_size, file_header.unp_size, 4); memcpy(unp_size + 4, p, 4); /* High unpack size */ p += 4; rar->packed_size = archive_le64dec(&packed_size); rar->unp_size = archive_le64dec(&unp_size); } else { rar->packed_size = archive_le32dec(file_header.pack_size); rar->unp_size = archive_le32dec(file_header.unp_size); } if (rar->packed_size < 0 || rar->unp_size < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid sizes specified."); return (ARCHIVE_FATAL); } rar->bytes_remaining = rar->packed_size; /* TODO: RARv3 subblocks contain comments. For now the complete block is * consumed at the end. */ if (head_type == NEWSUB_HEAD) { size_t distance = p - (const char *)h; header_size += rar->packed_size; /* Make sure we have the extended data. */ if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; endp = p + header_size - 7; p += distance; } filename_size = archive_le16dec(file_header.name_size); if (p + filename_size > endp) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filename size"); return (ARCHIVE_FATAL); } if (rar->filename_allocated < filename_size * 2 + 2) { char *newptr; size_t newsize = filename_size * 2 + 2; newptr = realloc(rar->filename, newsize); if (newptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } rar->filename = newptr; rar->filename_allocated = newsize; } filename = rar->filename; memcpy(filename, p, filename_size); filename[filename_size] = '\0'; if (rar->file_flags & FHD_UNICODE) { if (filename_size != strlen(filename)) { unsigned char highbyte, flagbits, flagbyte; unsigned fn_end, offset; end = filename_size; fn_end = filename_size * 2; filename_size = 0; offset = (unsigned)strlen(filename) + 1; highbyte = *(p + offset++); flagbits = 0; flagbyte = 0; while (offset < end && filename_size < fn_end) { if (!flagbits) { flagbyte = *(p + offset++); flagbits = 8; } flagbits -= 2; switch((flagbyte >> flagbits) & 3) { case 0: filename[filename_size++] = '\0'; filename[filename_size++] = *(p + offset++); break; case 1: filename[filename_size++] = highbyte; filename[filename_size++] = *(p + offset++); break; case 2: filename[filename_size++] = *(p + offset + 1); filename[filename_size++] = *(p + offset); offset += 2; break; case 3: { char extra, high; uint8_t length = *(p + offset++); if (length & 0x80) { extra = *(p + offset++); high = (char)highbyte; } else extra = high = 0; length = (length & 0x7f) + 2; while (length && filename_size < fn_end) { unsigned cp = filename_size >> 1; filename[filename_size++] = high; filename[filename_size++] = p[cp] + extra; length--; } } break; } } if (filename_size > fn_end) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filename"); return (ARCHIVE_FATAL); } filename[filename_size++] = '\0'; /* * Do not increment filename_size here as the computations below * add the space for the terminating NUL explicitly. */ filename[filename_size] = '\0'; /* Decoded unicode form is UTF-16BE, so we have to update a string * conversion object for it. */ if (rar->sconv_utf16be == NULL) { rar->sconv_utf16be = archive_string_conversion_from_charset( &a->archive, "UTF-16BE", 1); if (rar->sconv_utf16be == NULL) return (ARCHIVE_FATAL); } fn_sconv = rar->sconv_utf16be; strp = filename; while (memcmp(strp, "\x00\x00", 2)) { if (!memcmp(strp, "\x00\\", 2)) *(strp + 1) = '/'; strp += 2; } p += offset; } else { /* * If FHD_UNICODE is set but no unicode data, this file name form * is UTF-8, so we have to update a string conversion object for * it accordingly. */ if (rar->sconv_utf8 == NULL) { rar->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); if (rar->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } fn_sconv = rar->sconv_utf8; while ((strp = strchr(filename, '\\')) != NULL) *strp = '/'; p += filename_size; } } else { fn_sconv = sconv; while ((strp = strchr(filename, '\\')) != NULL) *strp = '/'; p += filename_size; } /* Split file in multivolume RAR. No more need to process header. */ if (rar->filename_save && filename_size == rar->filename_save_size && !memcmp(rar->filename, rar->filename_save, filename_size + 1)) { __archive_read_consume(a, header_size - 7); rar->cursor++; if (rar->cursor >= rar->nodes) { rar->nodes++; if ((rar->dbo = realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } rar->dbo[rar->cursor].header_size = header_size; rar->dbo[rar->cursor].start_offset = -1; rar->dbo[rar->cursor].end_offset = -1; } if (rar->dbo[rar->cursor].start_offset < 0) { rar->dbo[rar->cursor].start_offset = a->filter->position; rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset + rar->packed_size; } return ret; } else if (rar->filename_must_match) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mismatch of file parts split across multi-volume archive"); return (ARCHIVE_FATAL); } rar->filename_save = (char*)realloc(rar->filename_save, filename_size + 1); memcpy(rar->filename_save, rar->filename, filename_size + 1); rar->filename_save_size = filename_size; /* Set info for seeking */ free(rar->dbo); if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } rar->dbo[0].header_size = header_size; rar->dbo[0].start_offset = -1; rar->dbo[0].end_offset = -1; rar->cursor = 0; rar->nodes = 1; if (rar->file_flags & FHD_SALT) { if (p + 8 > endp) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } memcpy(rar->salt, p, 8); p += 8; } if (rar->file_flags & FHD_EXTTIME) { if (read_exttime(p, rar, endp) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header size"); return (ARCHIVE_FATAL); } } __archive_read_consume(a, header_size - 7); rar->dbo[0].start_offset = a->filter->position; rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size; switch(file_header.host_os) { case OS_MSDOS: case OS_OS2: case OS_WIN32: rar->mode = archive_le32dec(file_header.file_attr); if (rar->mode & FILE_ATTRIBUTE_DIRECTORY) rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else rar->mode = AE_IFREG; rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; break; case OS_UNIX: case OS_MAC_OS: case OS_BEOS: rar->mode = archive_le32dec(file_header.file_attr); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown file attributes from RAR file's host OS"); return (ARCHIVE_FATAL); } rar->bytes_uncopied = rar->bytes_unconsumed = 0; rar->lzss.position = rar->offset = 0; rar->offset_seek = 0; rar->dictionary_size = 0; rar->offset_outgoing = 0; rar->br.cache_avail = 0; rar->br.avail_in = 0; rar->crc_calculated = 0; rar->entry_eof = 0; rar->valid = 1; rar->is_ppmd_block = 0; rar->start_new_table = 1; free(rar->unp_buffer); rar->unp_buffer = NULL; rar->unp_offset = 0; rar->unp_buffer_size = UNP_BUFFER_SIZE; memset(rar->lengthtable, 0, sizeof(rar->lengthtable)); __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->ppmd_valid = rar->ppmd_eod = 0; /* Don't set any archive entries for non-file header types */ if (head_type == NEWSUB_HEAD) return ret; archive_entry_set_mtime(entry, rar->mtime, rar->mnsec); archive_entry_set_ctime(entry, rar->ctime, rar->cnsec); archive_entry_set_atime(entry, rar->atime, rar->ansec); archive_entry_set_size(entry, rar->unp_size); archive_entry_set_mode(entry, rar->mode); if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(fn_sconv)); ret = (ARCHIVE_WARN); } if (((rar->mode) & AE_IFMT) == AE_IFLNK) { /* Make sure a symbolic-link file does not have its body. */ rar->bytes_remaining = 0; archive_entry_set_size(entry, 0); /* Read a symbolic-link name. */ if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN)) return ret2; if (ret > ret2) ret = ret2; } if (rar->bytes_remaining == 0) rar->entry_eof = 1; return ret; } static time_t get_time(int ttime) { struct tm tm; tm.tm_sec = 2 * (ttime & 0x1f); tm.tm_min = (ttime >> 5) & 0x3f; tm.tm_hour = (ttime >> 11) & 0x1f; tm.tm_mday = (ttime >> 16) & 0x1f; tm.tm_mon = ((ttime >> 21) & 0x0f) - 1; tm.tm_year = ((ttime >> 25) & 0x7f) + 80; tm.tm_isdst = -1; return mktime(&tm); } static int read_exttime(const char *p, struct rar *rar, const char *endp) { unsigned rmode, flags, rem, j, count; int ttime, i; struct tm *tm; time_t t; long nsec; #if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) struct tm tmbuf; #endif #if defined(HAVE__LOCALTIME64_S) errno_t terr; __time64_t tmptime; #endif if (p + 2 > endp) return (-1); flags = archive_le16dec(p); p += 2; for (i = 3; i >= 0; i--) { t = 0; if (i == 3) t = rar->mtime; rmode = flags >> i * 4; if (rmode & 8) { if (!t) { if (p + 4 > endp) return (-1); ttime = archive_le32dec(p); t = get_time(ttime); p += 4; } rem = 0; count = rmode & 3; if (p + count > endp) return (-1); for (j = 0; j < count; j++) { rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8); p++; } #if defined(HAVE_LOCALTIME_R) tm = localtime_r(&t, &tmbuf); #elif defined(HAVE__LOCALTIME64_S) tmptime = t; terr = _localtime64_s(&tmbuf, &tmptime); if (terr) tm = NULL; else tm = &tmbuf; #else tm = localtime(&t); #endif nsec = tm->tm_sec + rem / NS_UNIT; if (rmode & 4) { tm->tm_sec++; t = mktime(tm); } if (i == 3) { rar->mtime = t; rar->mnsec = nsec; } else if (i == 2) { rar->ctime = t; rar->cnsec = nsec; } else if (i == 1) { rar->atime = t; rar->ansec = nsec; } else { rar->arctime = t; rar->arcnsec = nsec; } } } return (0); } static int read_symlink_stored(struct archive_read *a, struct archive_entry *entry, struct archive_string_conv *sconv) { const void *h; const char *p; struct rar *rar; int ret = (ARCHIVE_OK); rar = (struct rar *)(a->format->data); if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; if (archive_entry_copy_symlink_l(entry, p, (size_t)rar->packed_size, sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for link"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "link cannot be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); ret = (ARCHIVE_WARN); } __archive_read_consume(a, rar->packed_size); return ret; } static int read_data_stored(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct rar *rar; ssize_t bytes_avail; rar = (struct rar *)(a->format->data); if (rar->bytes_remaining == 0 && !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)) { *buff = NULL; *size = 0; *offset = rar->offset; if (rar->file_crc != rar->crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); return (ARCHIVE_FATAL); } rar->entry_eof = 1; return (ARCHIVE_EOF); } *buff = rar_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } *size = bytes_avail; *offset = rar->offset; rar->offset += bytes_avail; rar->offset_seek += bytes_avail; rar->bytes_remaining -= bytes_avail; rar->bytes_unconsumed = bytes_avail; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)bytes_avail); return (ARCHIVE_OK); } static int read_data_compressed(struct archive_read *a, const void **buff, size_t *size, - int64_t *offset) + int64_t *offset, size_t looper) { + if (looper++ > MAX_COMPRESS_DEPTH) + return (ARCHIVE_FATAL); + struct rar *rar; int64_t start, end, actualend; size_t bs; int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i; rar = (struct rar *)(a->format->data); do { if (!rar->valid) return (ARCHIVE_FATAL); if (rar->ppmd_eod || (rar->dictionary_size && rar->offset >= rar->unp_size)) { if (rar->unp_offset > 0) { /* * We have unprocessed extracted data. write it out. */ *buff = rar->unp_buffer; *size = rar->unp_offset; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); rar->unp_offset = 0; return (ARCHIVE_OK); } *buff = NULL; *size = 0; *offset = rar->offset; if (rar->file_crc != rar->crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); return (ARCHIVE_FATAL); } rar->entry_eof = 1; return (ARCHIVE_EOF); } if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0) { if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset)) bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; rar->bytes_uncopied -= bs; if (*buff != NULL) { rar->unp_offset = 0; *size = rar->unp_buffer_size; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); return (ret); } continue; } if (!rar->br.next_in && (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN) return (ret); if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN))) return (ret); if (rar->is_ppmd_block) { if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } if(sym != rar->ppmd_escape) { lzss_emit_literal(rar, sym); rar->bytes_uncopied++; } else { if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } switch(code) { case 0: rar->start_new_table = 1; - return read_data_compressed(a, buff, size, offset); + return read_data_compressed(a, buff, size, offset, looper); case 2: rar->ppmd_eod = 1;/* End Of ppmd Data. */ continue; case 3: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Parsing filters is unsupported."); return (ARCHIVE_FAILED); case 4: lzss_offset = 0; for (i = 2; i >= 0; i--) { if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } lzss_offset |= code << (i * 8); } if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } lzss_emit_match(rar, lzss_offset + 2, length + 32); rar->bytes_uncopied += length + 32; break; case 5: if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &rar->ppmd7_context, &rar->range_dec.p)) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); return (ARCHIVE_FATAL); } lzss_emit_match(rar, 1, length + 4); rar->bytes_uncopied += length + 4; break; default: lzss_emit_literal(rar, sym); rar->bytes_uncopied++; } } } else { start = rar->offset; end = start + rar->dictionary_size; rar->filterstart = INT64_MAX; if ((actualend = expand(a, end)) < 0) return ((int)actualend); rar->bytes_uncopied = actualend - start; if (rar->bytes_uncopied == 0) { /* Broken RAR files cause this case. * NOTE: If this case were possible on a normal RAR file * we would find out where it was actually bad and * what we would do to solve it. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file"); return (ARCHIVE_FATAL); } } if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset)) bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; rar->bytes_uncopied -= bs; /* * If *buff is NULL, it means unp_buffer is not full. * So we have to continue extracting a RAR file. */ } while (*buff == NULL); rar->unp_offset = 0; *size = rar->unp_buffer_size; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); return ret; } static int parse_codes(struct archive_read *a) { int i, j, val, n, r; unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags; unsigned int maxorder; struct huffman_code precode; struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); free_codes(a); /* Skip to the next byte */ rar_br_consume_unalined_bits(br); /* PPMd block flag */ if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0) { rar_br_consume(br, 1); if (!rar_br_read_ahead(a, br, 7)) goto truncated_data; ppmd_flags = rar_br_bits(br, 7); rar_br_consume(br, 7); /* Memory is allocated in MB */ if (ppmd_flags & 0x20) { if (!rar_br_read_ahead(a, br, 8)) goto truncated_data; rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20; rar_br_consume(br, 8); } if (ppmd_flags & 0x40) { if (!rar_br_read_ahead(a, br, 8)) goto truncated_data; rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8); rar_br_consume(br, 8); } else rar->ppmd_escape = 2; if (ppmd_flags & 0x20) { maxorder = (ppmd_flags & 0x1F) + 1; if(maxorder > 16) maxorder = 16 + (maxorder - 16) * 3; if (maxorder == 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } /* Make sure ppmd7_contest is freed before Ppmd7_Construct * because reading a broken file cause this abnormal sequence. */ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->bytein.a = a; rar->bytein.Read = &ppmd_read; __archive_ppmd7_functions.PpmdRAR_RangeDec_CreateVTable(&rar->range_dec); rar->range_dec.Stream = &rar->bytein; __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context); if (rar->dictionary_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid zero dictionary size"); return (ARCHIVE_FATAL); } if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context, rar->dictionary_size)) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); return (ARCHIVE_FATAL); } __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder); rar->ppmd_valid = 1; } else { if (!rar->ppmd_valid) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid PPMd sequence"); return (ARCHIVE_FATAL); } if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); return (ARCHIVE_FATAL); } } } else { rar_br_consume(br, 1); /* Keep existing table flag */ if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; if (!rar_br_bits(br, 1)) memset(rar->lengthtable, 0, sizeof(rar->lengthtable)); rar_br_consume(br, 1); memset(&bitlengths, 0, sizeof(bitlengths)); for (i = 0; i < MAX_SYMBOLS;) { if (!rar_br_read_ahead(a, br, 4)) goto truncated_data; bitlengths[i++] = rar_br_bits(br, 4); rar_br_consume(br, 4); if (bitlengths[i-1] == 0xF) { if (!rar_br_read_ahead(a, br, 4)) goto truncated_data; zerocount = rar_br_bits(br, 4); rar_br_consume(br, 4); if (zerocount) { i--; for (j = 0; j < zerocount + 2 && i < MAX_SYMBOLS; j++) bitlengths[i++] = 0; } } } memset(&precode, 0, sizeof(precode)); r = create_code(a, &precode, bitlengths, MAX_SYMBOLS, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) { free(precode.tree); free(precode.table); return (r); } for (i = 0; i < HUFFMAN_TABLE_SIZE;) { if ((val = read_next_symbol(a, &precode)) < 0) { free(precode.tree); free(precode.table); return (ARCHIVE_FATAL); } if (val < 16) { rar->lengthtable[i] = (rar->lengthtable[i] + val) & 0xF; i++; } else if (val < 18) { if (i == 0) { free(precode.tree); free(precode.table); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file."); return (ARCHIVE_FATAL); } if(val == 16) { if (!rar_br_read_ahead(a, br, 3)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 3) + 3; rar_br_consume(br, 3); } else { if (!rar_br_read_ahead(a, br, 7)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 7) + 11; rar_br_consume(br, 7); } for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++) { rar->lengthtable[i] = rar->lengthtable[i-1]; i++; } } else { if(val == 18) { if (!rar_br_read_ahead(a, br, 3)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 3) + 3; rar_br_consume(br, 3); } else { if (!rar_br_read_ahead(a, br, 7)) { free(precode.tree); free(precode.table); goto truncated_data; } n = rar_br_bits(br, 7) + 11; rar_br_consume(br, 7); } for(j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++) rar->lengthtable[i++] = 0; } } free(precode.tree); free(precode.table); r = create_code(a, &rar->maincode, &rar->lengthtable[0], MAINCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); r = create_code(a, &rar->offsetcode, &rar->lengthtable[MAINCODE_SIZE], OFFSETCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); r = create_code(a, &rar->lowoffsetcode, &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE], LOWOFFSETCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); r = create_code(a, &rar->lengthcode, &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE], LENGTHCODE_SIZE, MAX_SYMBOL_LENGTH); if (r != ARCHIVE_OK) return (r); } if (!rar->dictionary_size || !rar->lzss.window) { /* Seems as though dictionary sizes are not used. Even so, minimize * memory usage as much as possible. */ void *new_window; unsigned int new_size; if (rar->unp_size >= DICTIONARY_MAX_SIZE) new_size = DICTIONARY_MAX_SIZE; else new_size = rar_fls((unsigned int)rar->unp_size) << 1; if (new_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Zero window size is invalid."); return (ARCHIVE_FATAL); } new_window = realloc(rar->lzss.window, new_size); if (new_window == NULL) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for uncompressed data."); return (ARCHIVE_FATAL); } rar->lzss.window = (unsigned char *)new_window; rar->dictionary_size = new_size; memset(rar->lzss.window, 0, rar->dictionary_size); rar->lzss.mask = rar->dictionary_size - 1; } rar->start_new_table = 0; return (ARCHIVE_OK); truncated_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return (ARCHIVE_FATAL); } static void free_codes(struct archive_read *a) { struct rar *rar = (struct rar *)(a->format->data); free(rar->maincode.tree); free(rar->offsetcode.tree); free(rar->lowoffsetcode.tree); free(rar->lengthcode.tree); free(rar->maincode.table); free(rar->offsetcode.table); free(rar->lowoffsetcode.table); free(rar->lengthcode.table); memset(&rar->maincode, 0, sizeof(rar->maincode)); memset(&rar->offsetcode, 0, sizeof(rar->offsetcode)); memset(&rar->lowoffsetcode, 0, sizeof(rar->lowoffsetcode)); memset(&rar->lengthcode, 0, sizeof(rar->lengthcode)); } static int read_next_symbol(struct archive_read *a, struct huffman_code *code) { unsigned char bit; unsigned int bits; int length, value, node; struct rar *rar; struct rar_br *br; if (!code->table) { if (make_table(a, code) != (ARCHIVE_OK)) return -1; } rar = (struct rar *)(a->format->data); br = &(rar->br); /* Look ahead (peek) at bits */ if (!rar_br_read_ahead(a, br, code->tablesize)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return -1; } bits = rar_br_bits(br, code->tablesize); length = code->table[bits].length; value = code->table[bits].value; if (length < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid prefix code in bitstream"); return -1; } if (length <= code->tablesize) { /* Skip length bits */ rar_br_consume(br, length); return value; } /* Skip tablesize bits */ rar_br_consume(br, code->tablesize); node = value; while (!(code->tree[node].branches[0] == code->tree[node].branches[1])) { if (!rar_br_read_ahead(a, br, 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return -1; } bit = rar_br_bits(br, 1); rar_br_consume(br, 1); if (code->tree[node].branches[bit] < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid prefix code in bitstream"); return -1; } node = code->tree[node].branches[bit]; } return code->tree[node].branches[0]; } static int create_code(struct archive_read *a, struct huffman_code *code, unsigned char *lengths, int numsymbols, char maxlength) { int i, j, codebits = 0, symbolsleft = numsymbols; code->numentries = 0; code->numallocatedentries = 0; if (new_node(code) < 0) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for node data."); return (ARCHIVE_FATAL); } code->numentries = 1; code->minlength = INT_MAX; code->maxlength = INT_MIN; codebits = 0; for(i = 1; i <= maxlength; i++) { for(j = 0; j < numsymbols; j++) { if (lengths[j] != i) continue; if (add_value(a, code, j, codebits, i) != ARCHIVE_OK) return (ARCHIVE_FATAL); codebits++; if (--symbolsleft <= 0) break; } if (symbolsleft <= 0) break; codebits <<= 1; } return (ARCHIVE_OK); } static int add_value(struct archive_read *a, struct huffman_code *code, int value, int codebits, int length) { int lastnode, bitpos, bit; /* int repeatpos, repeatnode, nextnode; */ free(code->table); code->table = NULL; if(length > code->maxlength) code->maxlength = length; if(length < code->minlength) code->minlength = length; /* * Dead code, repeatpos was is -1 * repeatpos = -1; if (repeatpos == 0 || (repeatpos >= 0 && (((codebits >> (repeatpos - 1)) & 3) == 0 || ((codebits >> (repeatpos - 1)) & 3) == 3))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid repeat position"); return (ARCHIVE_FATAL); } */ lastnode = 0; for (bitpos = length - 1; bitpos >= 0; bitpos--) { bit = (codebits >> bitpos) & 1; /* Leaf node check */ if (code->tree[lastnode].branches[0] == code->tree[lastnode].branches[1]) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Prefix found"); return (ARCHIVE_FATAL); } /* * Dead code, repeatpos was -1, bitpos >=0 * if (bitpos == repeatpos) { * Open branch check * if (!(code->tree[lastnode].branches[bit] < 0)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid repeating code"); return (ARCHIVE_FATAL); } if ((repeatnode = new_node(code)) < 0) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for node data."); return (ARCHIVE_FATAL); } if ((nextnode = new_node(code)) < 0) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for node data."); return (ARCHIVE_FATAL); } * Set branches * code->tree[lastnode].branches[bit] = repeatnode; code->tree[repeatnode].branches[bit] = repeatnode; code->tree[repeatnode].branches[bit^1] = nextnode; lastnode = nextnode; bitpos++; * terminating bit already handled, skip it * } else { */ /* Open branch check */ if (code->tree[lastnode].branches[bit] < 0) { if (new_node(code) < 0) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for node data."); return (ARCHIVE_FATAL); } code->tree[lastnode].branches[bit] = code->numentries++; } /* set to branch */ lastnode = code->tree[lastnode].branches[bit]; /* } */ } if (!(code->tree[lastnode].branches[0] == -1 && code->tree[lastnode].branches[1] == -2)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Prefix found"); return (ARCHIVE_FATAL); } /* Set leaf value */ code->tree[lastnode].branches[0] = value; code->tree[lastnode].branches[1] = value; return (ARCHIVE_OK); } static int new_node(struct huffman_code *code) { void *new_tree; if (code->numallocatedentries == code->numentries) { int new_num_entries = 256; if (code->numentries > 0) { new_num_entries = code->numentries * 2; } new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree)); if (new_tree == NULL) return (-1); code->tree = (struct huffman_tree_node *)new_tree; code->numallocatedentries = new_num_entries; } code->tree[code->numentries].branches[0] = -1; code->tree[code->numentries].branches[1] = -2; return 1; } static int make_table(struct archive_read *a, struct huffman_code *code) { if (code->maxlength < code->minlength || code->maxlength > 10) code->tablesize = 10; else code->tablesize = code->maxlength; code->table = (struct huffman_table_entry *)calloc(1, sizeof(*code->table) * ((size_t)1 << code->tablesize)); return make_table_recurse(a, code, 0, code->table, 0, code->tablesize); } static int make_table_recurse(struct archive_read *a, struct huffman_code *code, int node, struct huffman_table_entry *table, int depth, int maxdepth) { int currtablesize, i, ret = (ARCHIVE_OK); if (!code->tree) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Huffman tree was not created."); return (ARCHIVE_FATAL); } if (node < 0 || node >= code->numentries) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid location to Huffman tree specified."); return (ARCHIVE_FATAL); } currtablesize = 1 << (maxdepth - depth); if (code->tree[node].branches[0] == code->tree[node].branches[1]) { for(i = 0; i < currtablesize; i++) { table[i].length = depth; table[i].value = code->tree[node].branches[0]; } } /* * Dead code, node >= 0 * else if (node < 0) { for(i = 0; i < currtablesize; i++) table[i].length = -1; } */ else { if(depth == maxdepth) { table[0].length = maxdepth + 1; table[0].value = node; } else { ret |= make_table_recurse(a, code, code->tree[node].branches[0], table, depth + 1, maxdepth); ret |= make_table_recurse(a, code, code->tree[node].branches[1], table + currtablesize / 2, depth + 1, maxdepth); } } return ret; } static int64_t expand(struct archive_read *a, int64_t end) { static const unsigned char lengthbases[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224 }; static const unsigned char lengthbits[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; static const int lengthb_min = minimum( (int)(sizeof(lengthbases)/sizeof(lengthbases[0])), (int)(sizeof(lengthbits)/sizeof(lengthbits[0])) ); static const unsigned int offsetbases[] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824, 655360, 720896, 786432, 851968, 917504, 983040, 1048576, 1310720, 1572864, 1835008, 2097152, 2359296, 2621440, 2883584, 3145728, 3407872, 3670016, 3932160 }; static const unsigned char offsetbits[] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 }; static const int offsetb_min = minimum( (int)(sizeof(offsetbases)/sizeof(offsetbases[0])), (int)(sizeof(offsetbits)/sizeof(offsetbits[0])) ); static const unsigned char shortbases[] = { 0, 4, 8, 16, 32, 64, 128, 192 }; static const unsigned char shortbits[] = { 2, 2, 3, 4, 5, 6, 6, 6 }; int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol; unsigned char newfile; struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); if (rar->filterstart < end) end = rar->filterstart; while (1) { if (rar->output_last_match && lzss_position(&rar->lzss) + rar->lastlength <= end) { lzss_emit_match(rar, rar->lastoffset, rar->lastlength); rar->output_last_match = 0; } if(rar->is_ppmd_block || rar->output_last_match || lzss_position(&rar->lzss) >= end) return lzss_position(&rar->lzss); if ((symbol = read_next_symbol(a, &rar->maincode)) < 0) return (ARCHIVE_FATAL); rar->output_last_match = 0; if (symbol < 256) { lzss_emit_literal(rar, symbol); continue; } else if (symbol == 256) { if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; newfile = !rar_br_bits(br, 1); rar_br_consume(br, 1); if(newfile) { rar->start_new_block = 1; if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; rar->start_new_table = rar_br_bits(br, 1); rar_br_consume(br, 1); return lzss_position(&rar->lzss); } else { if (parse_codes(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); continue; } } else if(symbol==257) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Parsing filters is unsupported."); return (ARCHIVE_FAILED); } else if(symbol==258) { if(rar->lastlength == 0) continue; offs = rar->lastoffset; len = rar->lastlength; } else if (symbol <= 262) { offsindex = symbol - 259; offs = rar->oldoffset[offsindex]; if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0) goto bad_data; if (lensymbol > lengthb_min) goto bad_data; len = lengthbases[lensymbol] + 2; if (lengthbits[lensymbol] > 0) { if (!rar_br_read_ahead(a, br, lengthbits[lensymbol])) goto truncated_data; len += rar_br_bits(br, lengthbits[lensymbol]); rar_br_consume(br, lengthbits[lensymbol]); } for (i = offsindex; i > 0; i--) rar->oldoffset[i] = rar->oldoffset[i-1]; rar->oldoffset[0] = offs; } else if(symbol<=270) { offs = shortbases[symbol-263] + 1; if(shortbits[symbol-263] > 0) { if (!rar_br_read_ahead(a, br, shortbits[symbol-263])) goto truncated_data; offs += rar_br_bits(br, shortbits[symbol-263]); rar_br_consume(br, shortbits[symbol-263]); } len = 2; for(i = 3; i > 0; i--) rar->oldoffset[i] = rar->oldoffset[i-1]; rar->oldoffset[0] = offs; } else { if (symbol-271 > lengthb_min) goto bad_data; len = lengthbases[symbol-271]+3; if(lengthbits[symbol-271] > 0) { if (!rar_br_read_ahead(a, br, lengthbits[symbol-271])) goto truncated_data; len += rar_br_bits(br, lengthbits[symbol-271]); rar_br_consume(br, lengthbits[symbol-271]); } if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0) goto bad_data; if (offssymbol > offsetb_min) goto bad_data; offs = offsetbases[offssymbol]+1; if(offsetbits[offssymbol] > 0) { if(offssymbol > 9) { if(offsetbits[offssymbol] > 4) { if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4)) goto truncated_data; offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4; rar_br_consume(br, offsetbits[offssymbol] - 4); } if(rar->numlowoffsetrepeats > 0) { rar->numlowoffsetrepeats--; offs += rar->lastlowoffset; } else { if ((lowoffsetsymbol = read_next_symbol(a, &rar->lowoffsetcode)) < 0) return (ARCHIVE_FATAL); if(lowoffsetsymbol == 16) { rar->numlowoffsetrepeats = 15; offs += rar->lastlowoffset; } else { offs += lowoffsetsymbol; rar->lastlowoffset = lowoffsetsymbol; } } } else { if (!rar_br_read_ahead(a, br, offsetbits[offssymbol])) goto truncated_data; offs += rar_br_bits(br, offsetbits[offssymbol]); rar_br_consume(br, offsetbits[offssymbol]); } } if (offs >= 0x40000) len++; if (offs >= 0x2000) len++; for(i = 3; i > 0; i--) rar->oldoffset[i] = rar->oldoffset[i-1]; rar->oldoffset[0] = offs; } rar->lastoffset = offs; rar->lastlength = len; rar->output_last_match = 1; } truncated_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; return (ARCHIVE_FATAL); bad_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } static int copy_from_lzss_window(struct archive_read *a, const void **buffer, int64_t startpos, int length) { int windowoffs, firstpart; struct rar *rar = (struct rar *)(a->format->data); if (!rar->unp_buffer) { if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for uncompressed data."); return (ARCHIVE_FATAL); } } windowoffs = lzss_offset_for_position(&rar->lzss, startpos); if(windowoffs + length <= lzss_size(&rar->lzss)) { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], length); } else if (length <= lzss_size(&rar->lzss)) { firstpart = lzss_size(&rar->lzss) - windowoffs; if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } if (firstpart < length) { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], firstpart); memcpy(&rar->unp_buffer[rar->unp_offset + firstpart], &rar->lzss.window[0], length - firstpart); } else { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], length); } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } rar->unp_offset += length; if (rar->unp_offset >= rar->unp_buffer_size) *buffer = rar->unp_buffer; else *buffer = NULL; return (ARCHIVE_OK); } static const void * rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) { struct rar *rar = (struct rar *)(a->format->data); const void *h = __archive_read_ahead(a, min, avail); int ret; if (avail) { if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested) *avail = a->archive.read_data_requested; if (*avail > rar->bytes_remaining) *avail = (ssize_t)rar->bytes_remaining; if (*avail < 0) return NULL; else if (*avail == 0 && rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER) { rar->filename_must_match = 1; ret = archive_read_format_rar_read_header(a, a->entry); if (ret == (ARCHIVE_EOF)) { rar->has_endarc_header = 1; ret = archive_read_format_rar_read_header(a, a->entry); } rar->filename_must_match = 0; if (ret != (ARCHIVE_OK)) return NULL; return rar_read_ahead(a, min, avail); } } return h; } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_rar5.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_rar5.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_rar5.c (revision 368707) @@ -1,4097 +1,4100 @@ /*- * Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #include "archive_endian.h" #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_ZLIB_H #include /* crc32 */ #endif #ifdef HAVE_LIMITS_H #include #endif #include "archive.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_entry_private.h" #ifdef HAVE_BLAKE2_H #include #else #include "archive_blake2.h" #endif /*#define CHECK_CRC_ON_SOLID_SKIP*/ /*#define DONT_FAIL_ON_CRC_ERROR*/ /*#define DEBUG*/ #define rar5_min(a, b) (((a) > (b)) ? (b) : (a)) #define rar5_max(a, b) (((a) > (b)) ? (a) : (b)) #define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X))) #if defined DEBUG #define DEBUG_CODE if(1) #define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0) #else #define DEBUG_CODE if(0) #endif /* Real RAR5 magic number is: * * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 * "Rar!→•☺·\x00" * * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't * want to put this magic sequence in each binary that uses libarchive, so * applications that scan through the file for this marker won't trigger on * this "false" one. * * The array itself is decrypted in `rar5_init` function. */ static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; static const size_t g_unpack_window_size = 0x20000; /* These could have been static const's, but they aren't, because of * Visual Studio. */ #define MAX_NAME_IN_CHARS 2048 #define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS) struct file_header { ssize_t bytes_remaining; ssize_t unpacked_size; int64_t last_offset; /* Used in sanity checks. */ int64_t last_size; /* Used in sanity checks. */ uint8_t solid : 1; /* Is this a solid stream? */ uint8_t service : 1; /* Is this file a service data? */ uint8_t eof : 1; /* Did we finish unpacking the file? */ uint8_t dir : 1; /* Is this file entry a directory? */ /* Optional time fields. */ uint64_t e_mtime; uint64_t e_ctime; uint64_t e_atime; uint32_t e_unix_ns; /* Optional hash fields. */ uint32_t stored_crc32; uint32_t calculated_crc32; uint8_t blake2sp[32]; blake2sp_state b2state; char has_blake2; /* Optional redir fields */ uint64_t redir_type; uint64_t redir_flags; ssize_t solid_window_size; /* Used in file format check. */ }; enum EXTRA { EX_CRYPT = 0x01, EX_HASH = 0x02, EX_HTIME = 0x03, EX_VERSION = 0x04, EX_REDIR = 0x05, EX_UOWNER = 0x06, EX_SUBDATA = 0x07 }; #define REDIR_SYMLINK_IS_DIR 1 enum REDIR_TYPE { REDIR_TYPE_NONE = 0, REDIR_TYPE_UNIXSYMLINK = 1, REDIR_TYPE_WINSYMLINK = 2, REDIR_TYPE_JUNCTION = 3, REDIR_TYPE_HARDLINK = 4, REDIR_TYPE_FILECOPY = 5, }; #define OWNER_USER_NAME 0x01 #define OWNER_GROUP_NAME 0x02 #define OWNER_USER_UID 0x04 #define OWNER_GROUP_GID 0x08 #define OWNER_MAXNAMELEN 256 enum FILTER_TYPE { FILTER_DELTA = 0, /* Generic pattern. */ FILTER_E8 = 1, /* Intel x86 code. */ FILTER_E8E9 = 2, /* Intel x86 code. */ FILTER_ARM = 3, /* ARM code. */ FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */ FILTER_RGB = 5, /* Color palette, not used in RARv5. */ FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */ FILTER_PPM = 7, /* Predictive pattern matching, not used in RARv5. */ FILTER_NONE = 8, }; struct filter_info { int type; int channels; int pos_r; int64_t block_start; ssize_t block_length; uint16_t width; }; struct data_ready { char used; const uint8_t* buf; size_t size; int64_t offset; }; struct cdeque { uint16_t beg_pos; uint16_t end_pos; uint16_t cap_mask; uint16_t size; size_t* arr; }; struct decode_table { uint32_t size; int32_t decode_len[16]; uint32_t decode_pos[16]; uint32_t quick_bits; uint8_t quick_len[1 << 10]; uint16_t quick_num[1 << 10]; uint16_t decode_num[306]; }; struct comp_state { /* Flag used to specify if unpacker needs to reinitialize the uncompression context. */ uint8_t initialized : 1; /* Flag used when applying filters. */ uint8_t all_filters_applied : 1; /* Flag used to skip file context reinitialization, used when unpacker is skipping through different multivolume archives. */ uint8_t switch_multivolume : 1; /* Flag used to specify if unpacker has processed the whole data block or just a part of it. */ uint8_t block_parsing_finished : 1; signed int notused : 4; int flags; /* Uncompression flags. */ int method; /* Uncompression algorithm method. */ int version; /* Uncompression algorithm version. */ ssize_t window_size; /* Size of window_buf. */ uint8_t* window_buf; /* Circular buffer used during decompression. */ uint8_t* filtered_buf; /* Buffer used when applying filters. */ const uint8_t* block_buf; /* Buffer used when merging blocks. */ size_t window_mask; /* Convenience field; window_size - 1. */ int64_t write_ptr; /* This amount of data has been unpacked in the window buffer. */ int64_t last_write_ptr; /* This amount of data has been stored in the output file. */ int64_t last_unstore_ptr; /* Counter of bytes extracted during unstoring. This is separate from last_write_ptr because of how SERVICE base blocks are handled during skipping in solid multiarchive archives. */ int64_t solid_offset; /* Additional offset inside the window buffer, used in unpacking solid archives. */ ssize_t cur_block_size; /* Size of current data block. */ int last_len; /* Flag used in lzss decompression. */ /* Decode tables used during lzss uncompression. */ #define HUFF_BC 20 struct decode_table bd; /* huffman bit lengths */ #define HUFF_NC 306 struct decode_table ld; /* literals */ #define HUFF_DC 64 struct decode_table dd; /* distances */ #define HUFF_LDC 16 struct decode_table ldd; /* lower bits of distances */ #define HUFF_RC 44 struct decode_table rd; /* repeating distances */ #define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC) /* Circular deque for storing filters. */ struct cdeque filters; int64_t last_block_start; /* Used for sanity checking. */ ssize_t last_block_length; /* Used for sanity checking. */ /* Distance cache used during lzss uncompression. */ int dist_cache[4]; /* Data buffer stack. */ struct data_ready dready[2]; }; /* Bit reader state. */ struct bit_reader { int8_t bit_addr; /* Current bit pointer inside current byte. */ int in_addr; /* Current byte pointer. */ }; /* RARv5 block header structure. Use bf_* functions to get values from * block_flags_u8 field. I.e. bf_byte_count, etc. */ struct compressed_block_header { /* block_flags_u8 contain fields encoded in little-endian bitfield: * * - table present flag (shr 7, and 1), * - last block flag (shr 6, and 1), * - byte_count (shr 3, and 7), * - bit_size (shr 0, and 7). */ uint8_t block_flags_u8; uint8_t block_cksum; }; /* RARv5 main header structure. */ struct main_header { /* Does the archive contain solid streams? */ uint8_t solid : 1; /* If this a multi-file archive? */ uint8_t volume : 1; uint8_t endarc : 1; uint8_t notused : 5; unsigned int vol_no; }; struct generic_header { uint8_t split_after : 1; uint8_t split_before : 1; uint8_t padding : 6; int size; int last_header_id; }; struct multivolume { unsigned int expected_vol_no; uint8_t* push_buf; }; /* Main context structure. */ struct rar5 { int header_initialized; /* Set to 1 if current file is positioned AFTER the magic value * of the archive file. This is used in header reading functions. */ int skipped_magic; /* Set to not zero if we're in skip mode (either by calling * rar5_data_skip function or when skipping over solid streams). * Set to 0 when in * extraction mode. This is used during checksum * calculation functions. */ int skip_mode; /* Set to not zero if we're in block merging mode (i.e. when switching * to another file in multivolume archive, last block from 1st archive * needs to be merged with 1st block from 2nd archive). This flag * guards against recursive use of the merging function, which doesn't * support recursive calls. */ int merge_mode; /* An offset to QuickOpen list. This is not supported by this unpacker, * because we're focusing on streaming interface. QuickOpen is designed * to make things quicker for non-stream interfaces, so it's not our * use case. */ uint64_t qlist_offset; /* An offset to additional Recovery data. This is not supported by this * unpacker. Recovery data are additional Reed-Solomon codes that could * be used to calculate bytes that are missing in archive or are * corrupted. */ uint64_t rr_offset; /* Various context variables grouped to different structures. */ struct generic_header generic; struct main_header main; struct comp_state cstate; struct file_header file; struct bit_reader bits; struct multivolume vol; /* The header of currently processed RARv5 block. Used in main * decompression logic loop. */ struct compressed_block_header last_block_hdr; }; /* Forward function declarations. */ static void rar5_signature(char *buf); static int verify_global_checksums(struct archive_read* a); static int rar5_read_data_skip(struct archive_read *a); static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS, }; /* Clears the contents of this circular deque. */ static void cdeque_clear(struct cdeque* d) { d->size = 0; d->beg_pos = 0; d->end_pos = 0; } /* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32, * 64, 256, etc. When the user will add another item above current capacity, * the circular deque will overwrite the oldest entry. */ static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) { if(d == NULL || max_capacity_power_of_2 == 0) return CDE_PARAM; d->cap_mask = max_capacity_power_of_2 - 1; d->arr = NULL; if((max_capacity_power_of_2 & d->cap_mask) != 0) return CDE_PARAM; cdeque_clear(d); d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); return d->arr ? CDE_OK : CDE_ALLOC; } /* Return the current size (not capacity) of circular deque `d`. */ static size_t cdeque_size(struct cdeque* d) { return d->size; } /* Returns the first element of current circular deque. Note that this function * doesn't perform any bounds checking. If you need bounds checking, use * `cdeque_front()` function instead. */ static void cdeque_front_fast(struct cdeque* d, void** value) { *value = (void*) d->arr[d->beg_pos]; } /* Returns the first element of current circular deque. This function * performs bounds checking. */ static int cdeque_front(struct cdeque* d, void** value) { if(d->size > 0) { cdeque_front_fast(d, value); return CDE_OK; } else return CDE_OUT_OF_BOUNDS; } /* Pushes a new element into the end of this circular deque object. If current * size will exceed capacity, the oldest element will be overwritten. */ static int cdeque_push_back(struct cdeque* d, void* item) { if(d == NULL) return CDE_PARAM; if(d->size == d->cap_mask + 1) return CDE_OUT_OF_BOUNDS; d->arr[d->end_pos] = (size_t) item; d->end_pos = (d->end_pos + 1) & d->cap_mask; d->size++; return CDE_OK; } /* Pops a front element of this circular deque object and returns its value. * This function doesn't perform any bounds checking. */ static void cdeque_pop_front_fast(struct cdeque* d, void** value) { *value = (void*) d->arr[d->beg_pos]; d->beg_pos = (d->beg_pos + 1) & d->cap_mask; d->size--; } /* Pops a front element of this circular deque object and returns its value. * This function performs bounds checking. */ static int cdeque_pop_front(struct cdeque* d, void** value) { if(!d || !value) return CDE_PARAM; if(d->size == 0) return CDE_OUT_OF_BOUNDS; cdeque_pop_front_fast(d, value); return CDE_OK; } /* Convenience function to cast filter_info** to void **. */ static void** cdeque_filter_p(struct filter_info** f) { return (void**) (size_t) f; } /* Convenience function to cast filter_info* to void *. */ static void* cdeque_filter(struct filter_info* f) { return (void**) (size_t) f; } /* Destroys this circular deque object. Deallocates the memory of the * collection buffer, but doesn't deallocate the memory of any pointer passed * to this deque as a value. */ static void cdeque_free(struct cdeque* d) { if(!d) return; if(!d->arr) return; free(d->arr); d->arr = NULL; d->beg_pos = -1; d->end_pos = -1; d->cap_mask = 0; } static inline uint8_t bf_bit_size(const struct compressed_block_header* hdr) { return hdr->block_flags_u8 & 7; } static inline uint8_t bf_byte_count(const struct compressed_block_header* hdr) { return (hdr->block_flags_u8 >> 3) & 7; } static inline uint8_t bf_is_table_present(const struct compressed_block_header* hdr) { return (hdr->block_flags_u8 >> 7) & 1; } static inline struct rar5* get_context(struct archive_read* a) { return (struct rar5*) a->format->data; } /* Convenience functions used by filter implementations. */ static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask, int64_t start, int64_t end) { if((start & mask) > (end & mask)) { ssize_t len1 = mask + 1 - (start & mask); ssize_t len2 = end & mask; memcpy(dst, &window[start & mask], len1); memcpy(dst + len1, window, len2); } else { memcpy(dst, &window[start & mask], (size_t) (end - start)); } } static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { uint8_t linear_buf[4]; circular_memcpy(linear_buf, rar->cstate.window_buf, rar->cstate.window_mask, offset, offset + 4); return archive_le32dec(linear_buf); } static void write_filter_data(struct rar5* rar, uint32_t offset, uint32_t value) { archive_le32enc(&rar->cstate.filtered_buf[offset], value); } /* Allocates a new filter descriptor and adds it to the filter array. */ static struct filter_info* add_new_filter(struct rar5* rar) { struct filter_info* f = (struct filter_info*) calloc(1, sizeof(struct filter_info)); if(!f) { return NULL; } cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); return f; } static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { int i; ssize_t dest_pos, src_pos = 0; for(i = 0; i < flt->channels; i++) { uint8_t prev_byte = 0; for(dest_pos = i; dest_pos < flt->block_length; dest_pos += flt->channels) { uint8_t byte; byte = rar->cstate.window_buf[ (rar->cstate.solid_offset + flt->block_start + src_pos) & rar->cstate.window_mask]; prev_byte -= byte; rar->cstate.filtered_buf[dest_pos] = prev_byte; src_pos++; } } return ARCHIVE_OK; } static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, int extended) { const uint32_t file_size = 0x1000000; ssize_t i; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, rar->cstate.window_mask, rar->cstate.solid_offset + flt->block_start, rar->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 4;) { uint8_t b = rar->cstate.window_buf[ (rar->cstate.solid_offset + flt->block_start + i++) & rar->cstate.window_mask]; /* * 0xE8 = x86's call (function call) * 0xE9 = x86's jmp (unconditional jump) */ if(b == 0xE8 || (extended && b == 0xE9)) { uint32_t addr; uint32_t offset = (i + flt->block_start) % file_size; addr = read_filter_data(rar, (uint32_t)(rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask); if(addr & 0x80000000) { if(((addr + offset) & 0x80000000) == 0) { write_filter_data(rar, (uint32_t)i, addr + file_size); } } else { if((addr - file_size) & 0x80000000) { uint32_t naddr = addr - offset; write_filter_data(rar, (uint32_t)i, naddr); } } i += 4; } } return ARCHIVE_OK; } static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { ssize_t i = 0; uint32_t offset; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, rar->cstate.window_mask, rar->cstate.solid_offset + flt->block_start, rar->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 3; i += 4) { uint8_t* b = &rar->cstate.window_buf[ (rar->cstate.solid_offset + flt->block_start + i + 3) & rar->cstate.window_mask]; if(*b == 0xEB) { /* 0xEB = ARM's BL (branch + link) instruction. */ offset = read_filter_data(rar, (rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask) & 0x00ffffff; offset -= (uint32_t) ((i + flt->block_start) / 4); offset = (offset & 0x00ffffff) | 0xeb000000; write_filter_data(rar, (uint32_t)i, offset); } } return ARCHIVE_OK; } static int run_filter(struct archive_read* a, struct filter_info* flt) { int ret; struct rar5* rar = get_context(a); free(rar->cstate.filtered_buf); rar->cstate.filtered_buf = malloc(flt->block_length); if(!rar->cstate.filtered_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for filter data."); return ARCHIVE_FATAL; } switch(flt->type) { case FILTER_DELTA: ret = run_delta_filter(rar, flt); break; case FILTER_E8: /* fallthrough */ case FILTER_E8E9: ret = run_e8e9_filter(rar, flt, flt->type == FILTER_E8E9); break; case FILTER_ARM: ret = run_arm_filter(rar, flt); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported filter type: 0x%x", flt->type); return ARCHIVE_FATAL; } if(ret != ARCHIVE_OK) { /* Filter has failed. */ return ret; } if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, flt->block_length, rar->cstate.last_write_ptr)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Stack overflow when submitting unpacked data"); return ARCHIVE_FATAL; } rar->cstate.last_write_ptr += flt->block_length; return ARCHIVE_OK; } /* The `push_data` function submits the selected data range to the user. * Next call of `use_data` will use the pointer, size and offset arguments * that are specified here. These arguments are pushed to the FIFO stack here, * and popped from the stack by the `use_data` function. */ static void push_data(struct archive_read* a, struct rar5* rar, const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { const uint64_t wmask = rar->cstate.window_mask; const ssize_t solid_write_ptr = (rar->cstate.solid_offset + rar->cstate.last_write_ptr) & wmask; idx_begin += rar->cstate.solid_offset; idx_end += rar->cstate.solid_offset; /* Check if our unpacked data is wrapped inside the window circular * buffer. If it's not wrapped, it can be copied out by using * a single memcpy, but when it's wrapped, we need to copy the first * part with one memcpy, and the second part with another memcpy. */ if((idx_begin & wmask) > (idx_end & wmask)) { /* The data is wrapped (begin offset sis bigger than end * offset). */ const ssize_t frag1_size = rar->cstate.window_size - (idx_begin & wmask); const ssize_t frag2_size = idx_end & wmask; /* Copy the first part of the buffer first. */ push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, rar->cstate.last_write_ptr); /* Copy the second part of the buffer. */ push_data_ready(a, rar, buf, frag2_size, rar->cstate.last_write_ptr + frag1_size); rar->cstate.last_write_ptr += frag1_size + frag2_size; } else { /* Data is not wrapped, so we can just use one call to copy the * data. */ push_data_ready(a, rar, buf + solid_write_ptr, (idx_end - idx_begin) & wmask, rar->cstate.last_write_ptr); rar->cstate.last_write_ptr += idx_end - idx_begin; } } /* Convenience function that submits the data to the user. It uses the * unpack window buffer as a source location. */ static void push_window_data(struct archive_read* a, struct rar5* rar, int64_t idx_begin, int64_t idx_end) { push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); } static int apply_filters(struct archive_read* a) { struct filter_info* flt; struct rar5* rar = get_context(a); int ret; rar->cstate.all_filters_applied = 0; /* Get the first filter that can be applied to our data. The data * needs to be fully unpacked before the filter can be run. */ if(CDE_OK == cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) { /* Check if our unpacked data fully covers this filter's * range. */ if(rar->cstate.write_ptr > flt->block_start && rar->cstate.write_ptr >= flt->block_start + flt->block_length) { /* Check if we have some data pending to be written * right before the filter's start offset. */ if(rar->cstate.last_write_ptr == flt->block_start) { /* Run the filter specified by descriptor * `flt`. */ ret = run_filter(a, flt); if(ret != ARCHIVE_OK) { /* Filter failure, return error. */ return ret; } /* Filter descriptor won't be needed anymore * after it's used, * so remove it from the * filter list and free its memory. */ (void) cdeque_pop_front(&rar->cstate.filters, cdeque_filter_p(&flt)); free(flt); } else { /* We can't run filters yet, dump the memory * right before the filter. */ push_window_data(a, rar, rar->cstate.last_write_ptr, flt->block_start); } /* Return 'filter applied or not needed' state to the * caller. */ return ARCHIVE_RETRY; } } rar->cstate.all_filters_applied = 1; return ARCHIVE_OK; } static void dist_cache_push(struct rar5* rar, int value) { int* q = rar->cstate.dist_cache; q[3] = q[2]; q[2] = q[1]; q[1] = q[0]; q[0] = value; } static int dist_cache_touch(struct rar5* rar, int idx) { int* q = rar->cstate.dist_cache; int i, dist = q[idx]; for(i = idx; i > 0; i--) q[i] = q[i - 1]; q[0] = dist; return dist; } static void free_filters(struct rar5* rar) { struct cdeque* d = &rar->cstate.filters; /* Free any remaining filters. All filters should be naturally * consumed by the unpacking function, so remaining filters after * unpacking normally mean that unpacking wasn't successful. * But still of course we shouldn't leak memory in such case. */ /* cdeque_size() is a fast operation, so we can use it as a loop * expression. */ while(cdeque_size(d) > 0) { struct filter_info* f = NULL; /* Pop_front will also decrease the collection's size. */ if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) free(f); } cdeque_clear(d); /* Also clear out the variables needed for sanity checking. */ rar->cstate.last_block_start = 0; rar->cstate.last_block_length = 0; } static void reset_file_context(struct rar5* rar) { memset(&rar->file, 0, sizeof(rar->file)); blake2sp_init(&rar->file.b2state, 32); if(rar->main.solid) { rar->cstate.solid_offset += rar->cstate.write_ptr; } else { rar->cstate.solid_offset = 0; } rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; rar->cstate.last_unstore_ptr = 0; rar->file.redir_type = REDIR_TYPE_NONE; rar->file.redir_flags = 0; free_filters(rar); } static inline int get_archive_read(struct archive* a, struct archive_read** ar) { *ar = (struct archive_read*) a; archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar5"); return ARCHIVE_OK; } static int read_ahead(struct archive_read* a, size_t how_many, const uint8_t** ptr) { ssize_t avail = -1; if(!ptr) return 0; *ptr = __archive_read_ahead(a, how_many, &avail); if(*ptr == NULL) { return 0; } return 1; } static int consume(struct archive_read* a, int64_t how_many) { int ret; ret = how_many == __archive_read_consume(a, how_many) ? ARCHIVE_OK : ARCHIVE_FATAL; return ret; } /** * Read a RAR5 variable sized numeric value. This value will be stored in * `pvalue`. The `pvalue_len` argument points to a variable that will receive * the byte count that was consumed in order to decode the `pvalue` value, plus * one. * * pvalue_len is optional and can be NULL. * * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume * the number of bytes that `pvalue_len` value contains. If the `pvalue_len` * is NULL, this consuming operation is done automatically. * * Returns 1 if *pvalue was successfully read. * Returns 0 if there was an error. In this case, *pvalue contains an * invalid value. */ static int read_var(struct archive_read* a, uint64_t* pvalue, uint64_t* pvalue_len) { uint64_t result = 0; size_t shift, i; const uint8_t* p; uint8_t b; /* We will read maximum of 8 bytes. We don't have to handle the * situation to read the RAR5 variable-sized value stored at the end of * the file, because such situation will never happen. */ if(!read_ahead(a, 8, &p)) return 0; for(shift = 0, i = 0; i < 8; i++, shift += 7) { b = p[i]; /* Strip the MSB from the input byte and add the resulting * number to the `result`. */ result += (b & (uint64_t)0x7F) << shift; /* MSB set to 1 means we need to continue decoding process. * MSB set to 0 means we're done. * * This conditional checks for the second case. */ if((b & 0x80) == 0) { if(pvalue) { *pvalue = result; } /* If the caller has passed the `pvalue_len` pointer, * store the number of consumed bytes in it and do NOT * consume those bytes, since the caller has all the * information it needs to perform */ if(pvalue_len) { *pvalue_len = 1 + i; } else { /* If the caller did not provide the * `pvalue_len` pointer, it will not have the * possibility to advance the file pointer, * because it will not know how many bytes it * needs to consume. This is why we handle * such situation here automatically. */ if(ARCHIVE_OK != consume(a, 1 + i)) { return 0; } } /* End of decoding process, return success. */ return 1; } } /* The decoded value takes the maximum number of 8 bytes. * It's a maximum number of bytes, so end decoding process here * even if the first bit of last byte is 1. */ if(pvalue) { *pvalue = result; } if(pvalue_len) { *pvalue_len = 9; } else { if(ARCHIVE_OK != consume(a, 9)) { return 0; } } return 1; } static int read_var_sized(struct archive_read* a, size_t* pvalue, size_t* pvalue_len) { uint64_t v; uint64_t v_size = 0; const int ret = pvalue_len ? read_var(a, &v, &v_size) : read_var(a, &v, NULL); if(ret == 1 && pvalue) { *pvalue = (size_t) v; } if(pvalue_len) { /* Possible data truncation should be safe. */ *pvalue_len = (size_t) v_size; } return ret; } static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; bits |= p[rar->bits.in_addr + 1] << 16; bits |= p[rar->bits.in_addr + 2] << 8; bits |= p[rar->bits.in_addr + 3]; bits <<= rar->bits.bit_addr; bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); *value = bits; return ARCHIVE_OK; } static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; bits |= (int) p[rar->bits.in_addr + 1] << 8; bits |= (int) p[rar->bits.in_addr + 2]; bits >>= (8 - rar->bits.bit_addr); *value = bits & 0xffff; return ARCHIVE_OK; } static void skip_bits(struct rar5* rar, int bits) { const int new_bits = rar->bits.bit_addr + bits; rar->bits.in_addr += new_bits >> 3; rar->bits.bit_addr = new_bits & 7; } /* n = up to 16 */ static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n, int* value) { uint16_t v; int ret, num; if(n == 0 || n > 16) { /* This is a programmer error and should never happen * in runtime. */ return ARCHIVE_FATAL; } ret = read_bits_16(rar, p, &v); if(ret != ARCHIVE_OK) return ret; num = (int) v; num >>= 16 - n; skip_bits(rar, n); if(value) *value = num; return ARCHIVE_OK; } static int read_u32(struct archive_read* a, uint32_t* pvalue) { const uint8_t* p; if(!read_ahead(a, 4, &p)) return 0; *pvalue = archive_le32dec(p); return ARCHIVE_OK == consume(a, 4) ? 1 : 0; } static int read_u64(struct archive_read* a, uint64_t* pvalue) { const uint8_t* p; if(!read_ahead(a, 8, &p)) return 0; *pvalue = archive_le64dec(p); return ARCHIVE_OK == consume(a, 8) ? 1 : 0; } static int bid_standard(struct archive_read* a) { const uint8_t* p; char signature[sizeof(rar5_signature_xor)]; rar5_signature(signature); if(!read_ahead(a, sizeof(rar5_signature_xor), &p)) return -1; if(!memcmp(signature, p, sizeof(rar5_signature_xor))) return 30; return -1; } static int rar5_bid(struct archive_read* a, int best_bid) { int my_bid; if(best_bid > 30) return -1; my_bid = bid_standard(a); if(my_bid > -1) { return my_bid; } return -1; } static int rar5_options(struct archive_read *a, const char *key, const char *val) { (void) a; (void) key; (void) val; /* No options supported in this version. Return the ARCHIVE_WARN code * to signal the options supervisor that the unpacker didn't handle * setting this option. */ return ARCHIVE_WARN; } static void init_header(struct archive_read* a) { a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5; a->archive.archive_format_name = "RAR5"; } static void init_window_mask(struct rar5* rar) { if (rar->cstate.window_size) rar->cstate.window_mask = rar->cstate.window_size - 1; else rar->cstate.window_mask = 0; } enum HEADER_FLAGS { HFL_EXTRA_DATA = 0x0001, HFL_DATA = 0x0002, HFL_SKIP_IF_UNKNOWN = 0x0004, HFL_SPLIT_BEFORE = 0x0008, HFL_SPLIT_AFTER = 0x0010, HFL_CHILD = 0x0020, HFL_INHERITED = 0x0040 }; static int process_main_locator_extra_block(struct archive_read* a, struct rar5* rar) { uint64_t locator_flags; enum LOCATOR_FLAGS { QLIST = 0x01, RECOVERY = 0x02, }; if(!read_var(a, &locator_flags, NULL)) { return ARCHIVE_EOF; } if(locator_flags & QLIST) { if(!read_var(a, &rar->qlist_offset, NULL)) { return ARCHIVE_EOF; } /* qlist is not used */ } if(locator_flags & RECOVERY) { if(!read_var(a, &rar->rr_offset, NULL)) { return ARCHIVE_EOF; } /* rr is not used */ } return ARCHIVE_OK; } static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, ssize_t* extra_data_size) { size_t hash_type = 0; size_t value_len; enum HASH_TYPE { BLAKE2sp = 0x00 }; if(!read_var_sized(a, &hash_type, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { return ARCHIVE_EOF; } /* The file uses BLAKE2sp checksum algorithm instead of plain old * CRC32. */ if(hash_type == BLAKE2sp) { const uint8_t* p; const int hash_size = sizeof(rar->file.blake2sp); if(!read_ahead(a, hash_size, &p)) return ARCHIVE_EOF; rar->file.has_blake2 = 1; memcpy(&rar->file.blake2sp, p, hash_size); if(ARCHIVE_OK != consume(a, hash_size)) { return ARCHIVE_EOF; } *extra_data_size -= hash_size; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported hash type (0x%x)", (int) hash_type); return ARCHIVE_FATAL; } return ARCHIVE_OK; } static uint64_t time_win_to_unix(uint64_t win_time) { const size_t ns_in_sec = 10000000; const uint64_t sec_to_unix = 11644473600LL; return win_time / ns_in_sec - sec_to_unix; } static int parse_htime_item(struct archive_read* a, char unix_time, uint64_t* where, ssize_t* extra_data_size) { if(unix_time) { uint32_t time_val; if(!read_u32(a, &time_val)) return ARCHIVE_EOF; *extra_data_size -= 4; *where = (uint64_t) time_val; } else { uint64_t windows_time; if(!read_u64(a, &windows_time)) return ARCHIVE_EOF; *where = time_win_to_unix(windows_time); *extra_data_size -= 8; } return ARCHIVE_OK; } static int parse_file_extra_version(struct archive_read* a, struct archive_entry* e, ssize_t* extra_data_size) { size_t flags = 0; size_t version = 0; size_t value_len = 0; struct archive_string version_string; struct archive_string name_utf8_string; const char* cur_filename; /* Flags are ignored. */ if(!read_var_sized(a, &flags, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) return ARCHIVE_EOF; if(!read_var_sized(a, &version, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) return ARCHIVE_EOF; /* extra_data_size should be zero here. */ cur_filename = archive_entry_pathname_utf8(e); if(cur_filename == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Version entry without file name"); return ARCHIVE_FATAL; } archive_string_init(&version_string); archive_string_init(&name_utf8_string); /* Prepare a ;123 suffix for the filename, where '123' is the version * value of this file. */ archive_string_sprintf(&version_string, ";%zu", version); /* Build the new filename. */ archive_strcat(&name_utf8_string, cur_filename); archive_strcat(&name_utf8_string, version_string.s); /* Apply the new filename into this file's context. */ archive_entry_update_pathname_utf8(e, name_utf8_string.s); /* Free buffers. */ archive_string_free(&version_string); archive_string_free(&name_utf8_string); return ARCHIVE_OK; } static int parse_file_extra_htime(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) { char unix_time = 0; size_t flags = 0; size_t value_len; enum HTIME_FLAGS { IS_UNIX = 0x01, HAS_MTIME = 0x02, HAS_CTIME = 0x04, HAS_ATIME = 0x08, HAS_UNIX_NS = 0x10, }; if(!read_var_sized(a, &flags, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { return ARCHIVE_EOF; } unix_time = flags & IS_UNIX; if(flags & HAS_MTIME) { parse_htime_item(a, unix_time, &rar->file.e_mtime, extra_data_size); archive_entry_set_mtime(e, rar->file.e_mtime, 0); } if(flags & HAS_CTIME) { parse_htime_item(a, unix_time, &rar->file.e_ctime, extra_data_size); archive_entry_set_ctime(e, rar->file.e_ctime, 0); } if(flags & HAS_ATIME) { parse_htime_item(a, unix_time, &rar->file.e_atime, extra_data_size); archive_entry_set_atime(e, rar->file.e_atime, 0); } if(flags & HAS_UNIX_NS) { if(!read_u32(a, &rar->file.e_unix_ns)) return ARCHIVE_EOF; *extra_data_size -= 4; } return ARCHIVE_OK; } static int parse_file_extra_redir(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) { uint64_t value_size = 0; size_t target_size = 0; char target_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; if(!read_var(a, &rar->file.redir_type, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; if(!read_var(a, &rar->file.redir_flags, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; if(!read_var_sized(a, &target_size, NULL)) return ARCHIVE_EOF; *extra_data_size -= target_size + 1; if(!read_ahead(a, target_size, &p)) return ARCHIVE_EOF; if(target_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Link target is too long"); return ARCHIVE_FATAL; } if(target_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No link target specified"); return ARCHIVE_FATAL; } memcpy(target_utf8_buf, p, target_size); target_utf8_buf[target_size] = 0; if(ARCHIVE_OK != consume(a, (int64_t)target_size)) return ARCHIVE_EOF; switch(rar->file.redir_type) { case REDIR_TYPE_UNIXSYMLINK: case REDIR_TYPE_WINSYMLINK: archive_entry_set_filetype(e, AE_IFLNK); archive_entry_update_symlink_utf8(e, target_utf8_buf); if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { archive_entry_set_symlink_type(e, AE_SYMLINK_TYPE_DIRECTORY); } else { archive_entry_set_symlink_type(e, AE_SYMLINK_TYPE_FILE); } break; case REDIR_TYPE_HARDLINK: archive_entry_set_filetype(e, AE_IFREG); archive_entry_update_hardlink_utf8(e, target_utf8_buf); break; default: /* Unknown redir type, skip it. */ break; } return ARCHIVE_OK; } static int parse_file_extra_owner(struct archive_read* a, struct archive_entry* e, ssize_t* extra_data_size) { uint64_t flags = 0; uint64_t value_size = 0; uint64_t id = 0; size_t name_len = 0; size_t name_size = 0; char namebuf[OWNER_MAXNAMELEN]; const uint8_t* p; if(!read_var(a, &flags, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; if ((flags & OWNER_USER_NAME) != 0) { if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; *extra_data_size -= name_size + 1; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if (name_size >= OWNER_MAXNAMELEN) { name_len = OWNER_MAXNAMELEN - 1; } else { name_len = name_size; } memcpy(namebuf, p, name_len); namebuf[name_len] = 0; if(ARCHIVE_OK != consume(a, (int64_t)name_size)) return ARCHIVE_EOF; archive_entry_set_uname(e, namebuf); } if ((flags & OWNER_GROUP_NAME) != 0) { if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; *extra_data_size -= name_size + 1; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if (name_size >= OWNER_MAXNAMELEN) { name_len = OWNER_MAXNAMELEN - 1; } else { name_len = name_size; } memcpy(namebuf, p, name_len); namebuf[name_len] = 0; if(ARCHIVE_OK != consume(a, (int64_t)name_size)) return ARCHIVE_EOF; archive_entry_set_gname(e, namebuf); } if ((flags & OWNER_USER_UID) != 0) { if(!read_var(a, &id, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; archive_entry_set_uid(e, (la_int64_t)id); } if ((flags & OWNER_GROUP_GID) != 0) { if(!read_var(a, &id, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; archive_entry_set_gid(e, (la_int64_t)id); } return ARCHIVE_OK; } static int process_head_file_extra(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) { size_t extra_field_size; size_t extra_field_id = 0; int ret = ARCHIVE_FATAL; size_t var_size; while(extra_data_size > 0) { if(!read_var_sized(a, &extra_field_size, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &extra_field_id, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } switch(extra_field_id) { case EX_HASH: ret = parse_file_extra_hash(a, rar, &extra_data_size); break; case EX_HTIME: ret = parse_file_extra_htime(a, e, rar, &extra_data_size); break; case EX_REDIR: ret = parse_file_extra_redir(a, e, rar, &extra_data_size); break; case EX_UOWNER: ret = parse_file_extra_owner(a, e, &extra_data_size); break; case EX_VERSION: ret = parse_file_extra_version(a, e, &extra_data_size); break; case EX_CRYPT: /* fallthrough */ case EX_SUBDATA: /* fallthrough */ default: /* Skip unsupported entry. */ return consume(a, extra_data_size); } } if(ret != ARCHIVE_OK) { /* Attribute not implemented. */ return ret; } return ARCHIVE_OK; } static int process_head_file(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { ssize_t extra_data_size = 0; size_t data_size = 0; size_t file_flags = 0; size_t file_attr = 0; size_t compression_info = 0; size_t host_os = 0; size_t name_size = 0; uint64_t unpacked_size, window_size; uint32_t mtime = 0, crc = 0; int c_method = 0, c_version = 0; char name_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; enum FILE_FLAGS { DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, UNKNOWN_UNPACKED_SIZE = 0x0008, }; enum FILE_ATTRS { ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4, ATTR_DIRECTORY = 0x10, }; enum COMP_INFO_FLAGS { SOLID = 0x0040, }; enum HOST_OS { HOST_WINDOWS = 0, HOST_UNIX = 1, }; archive_entry_clear(entry); /* Do not reset file context if we're switching archives. */ if(!rar->cstate.switch_multivolume) { reset_file_context(rar); } if(block_flags & HFL_EXTRA_DATA) { size_t edata_size = 0; if(!read_var_sized(a, &edata_size, NULL)) return ARCHIVE_EOF; /* Intentional type cast from unsigned to signed. */ extra_data_size = (ssize_t) edata_size; } if(block_flags & HFL_DATA) { if(!read_var_sized(a, &data_size, NULL)) return ARCHIVE_EOF; rar->file.bytes_remaining = data_size; } else { rar->file.bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "no data found in file/service block"); return ARCHIVE_FATAL; } if(!read_var_sized(a, &file_flags, NULL)) return ARCHIVE_EOF; if(!read_var(a, &unpacked_size, NULL)) return ARCHIVE_EOF; if(file_flags & UNKNOWN_UNPACKED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Files with unknown unpacked size are not supported"); return ARCHIVE_FATAL; } rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); if(!read_var_sized(a, &file_attr, NULL)) return ARCHIVE_EOF; if(file_flags & UTIME) { if(!read_u32(a, &mtime)) return ARCHIVE_EOF; } if(file_flags & CRC32) { if(!read_u32(a, &crc)) return ARCHIVE_EOF; } if(!read_var_sized(a, &compression_info, NULL)) return ARCHIVE_EOF; c_method = (int) (compression_info >> 7) & 0x7; c_version = (int) (compression_info & 0x3f); /* RAR5 seems to limit the dictionary size to 64MB. */ window_size = (rar->file.dir > 0) ? 0 : g_unpack_window_size << ((compression_info >> 10) & 15); rar->cstate.method = c_method; rar->cstate.version = c_version + 50; rar->file.solid = (compression_info & SOLID) > 0; /* Archives which declare solid files without initializing the window * buffer first are invalid. */ if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Declared solid file, but no window buffer " "initialized yet."); return ARCHIVE_FATAL; } /* Check if window_size is a sane value. Also, if the file is not * declared as a directory, disallow window_size == 0. */ if(window_size > (64 * 1024 * 1024) || (rar->file.dir == 0 && window_size == 0)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Declared dictionary size is not supported."); return ARCHIVE_FATAL; } if(rar->file.solid > 0) { /* Re-check if current window size is the same as previous * window size (for solid files only). */ if(rar->file.solid_window_size > 0 && rar->file.solid_window_size != (ssize_t) window_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Window size for this solid file doesn't match " "the window size used in previous solid file. "); return ARCHIVE_FATAL; } } /* If we're currently switching volumes, ignore the new definition of * window_size. */ if(rar->cstate.switch_multivolume == 0) { /* Values up to 64M should fit into ssize_t on every * architecture. */ rar->cstate.window_size = (ssize_t) window_size; } if(rar->file.solid > 0 && rar->file.solid_window_size == 0) { /* Solid files have to have the same window_size across whole archive. Remember the window_size parameter for first solid file found. */ rar->file.solid_window_size = rar->cstate.window_size; } init_window_mask(rar); rar->file.service = 0; if(!read_var_sized(a, &host_os, NULL)) return ARCHIVE_EOF; if(host_os == HOST_WINDOWS) { /* Host OS is Windows */ __LA_MODE_T mode; if(file_attr & ATTR_DIRECTORY) { if (file_attr & ATTR_READONLY) { mode = 0555 | AE_IFDIR; } else { mode = 0755 | AE_IFDIR; } } else { if (file_attr & ATTR_READONLY) { mode = 0444 | AE_IFREG; } else { mode = 0644 | AE_IFREG; } } archive_entry_set_mode(entry, mode); if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) { char *fflags_text, *ptr; /* allocate for "rdonly,hidden,system," */ fflags_text = malloc(22 * sizeof(char)); if (fflags_text != NULL) { ptr = fflags_text; if (file_attr & ATTR_READONLY) { strcpy(ptr, "rdonly,"); ptr = ptr + 7; } if (file_attr & ATTR_HIDDEN) { strcpy(ptr, "hidden,"); ptr = ptr + 7; } if (file_attr & ATTR_SYSTEM) { strcpy(ptr, "system,"); ptr = ptr + 7; } if (ptr > fflags_text) { /* Delete trailing comma */ *(ptr - 1) = '\0'; archive_entry_copy_fflags_text(entry, fflags_text); } free(fflags_text); } } } else if(host_os == HOST_UNIX) { /* Host OS is Unix */ archive_entry_set_mode(entry, (__LA_MODE_T) file_attr); } else { /* Unknown host OS */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported Host OS: 0x%x", (int) host_os); return ARCHIVE_FATAL; } if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if(name_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Filename is too long"); return ARCHIVE_FATAL; } if(name_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No filename specified"); return ARCHIVE_FATAL; } memcpy(name_utf8_buf, p, name_size); name_utf8_buf[name_size] = 0; if(ARCHIVE_OK != consume(a, name_size)) { return ARCHIVE_EOF; } archive_entry_update_pathname_utf8(entry, name_utf8_buf); if(extra_data_size > 0) { int ret = process_head_file_extra(a, entry, rar, extra_data_size); /* * TODO: rewrite or remove useless sanity check * as extra_data_size is not passed as a pointer * if(extra_data_size < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "File extra data size is not zero"); return ARCHIVE_FATAL; } */ if(ret != ARCHIVE_OK) return ret; } if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { rar->file.unpacked_size = (ssize_t) unpacked_size; if(rar->file.redir_type == REDIR_TYPE_NONE) archive_entry_set_size(entry, unpacked_size); } if(file_flags & UTIME) { archive_entry_set_mtime(entry, (time_t) mtime, 0); } if(file_flags & CRC32) { rar->file.stored_crc32 = crc; } if(!rar->cstate.switch_multivolume) { /* Do not reinitialize unpacking state if we're switching * archives. */ rar->cstate.block_parsing_finished = 1; rar->cstate.all_filters_applied = 1; rar->cstate.initialized = 0; } if(rar->generic.split_before > 0) { /* If now we're standing on a header that has a 'split before' * mark, it means we're standing on a 'continuation' file * header. Signal the caller that if it wants to move to * another file, it must call rar5_read_header() function * again. */ return ARCHIVE_RETRY; } else { return ARCHIVE_OK; } } static int process_head_service(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { /* Process this SERVICE block the same way as FILE blocks. */ int ret = process_head_file(a, rar, entry, block_flags); if(ret != ARCHIVE_OK) return ret; rar->file.service = 1; /* But skip the data part automatically. It's no use for the user * anyway. It contains only service data, not even needed to * properly unpack the file. */ ret = rar5_read_data_skip(a); if(ret != ARCHIVE_OK) return ret; /* After skipping, try parsing another block automatically. */ return ARCHIVE_RETRY; } static int process_head_main(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { int ret; size_t extra_data_size = 0; size_t extra_field_size = 0; size_t extra_field_id = 0; size_t archive_flags = 0; enum MAIN_FLAGS { VOLUME = 0x0001, /* multi-volume archive */ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't * have it */ SOLID = 0x0004, /* solid archive */ PROTECT = 0x0008, /* contains Recovery info */ LOCK = 0x0010, /* readonly flag, not used */ }; enum MAIN_EXTRA { // Just one attribute here. LOCATOR = 0x01, }; (void) entry; if(block_flags & HFL_EXTRA_DATA) { if(!read_var_sized(a, &extra_data_size, NULL)) return ARCHIVE_EOF; } else { extra_data_size = 0; } if(!read_var_sized(a, &archive_flags, NULL)) { return ARCHIVE_EOF; } rar->main.volume = (archive_flags & VOLUME) > 0; rar->main.solid = (archive_flags & SOLID) > 0; if(archive_flags & VOLUME_NUMBER) { size_t v = 0; if(!read_var_sized(a, &v, NULL)) { return ARCHIVE_EOF; } if (v > UINT_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid volume number"); return ARCHIVE_FATAL; } rar->main.vol_no = (unsigned int) v; } else { rar->main.vol_no = 0; } if(rar->vol.expected_vol_no > 0 && rar->main.vol_no != rar->vol.expected_vol_no) { /* Returning EOF instead of FATAL because of strange * libarchive behavior. When opening multiple files via * archive_read_open_filenames(), after reading up the whole * last file, the __archive_read_ahead function wraps up to * the first archive instead of returning EOF. */ return ARCHIVE_EOF; } if(extra_data_size == 0) { /* Early return. */ return ARCHIVE_OK; } if(!read_var_sized(a, &extra_field_size, NULL)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &extra_field_id, NULL)) { return ARCHIVE_EOF; } if(extra_field_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extra field size"); return ARCHIVE_FATAL; } switch(extra_field_id) { case LOCATOR: ret = process_main_locator_extra_block(a, rar); if(ret != ARCHIVE_OK) { /* Error while parsing main locator extra * block. */ return ret; } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported extra type (0x%x)", (int) extra_field_id); return ARCHIVE_FATAL; } return ARCHIVE_OK; } static int skip_unprocessed_bytes(struct archive_read* a) { struct rar5* rar = get_context(a); int ret; if(rar->file.bytes_remaining) { /* Use different skipping method in block merging mode than in * normal mode. If merge mode is active, rar5_read_data_skip * can't be used, because it could allow recursive use of * merge_block() * function, and this function doesn't support * recursive use. */ if(rar->merge_mode) { /* Discard whole merged block. This is valid in solid * mode as well, because the code will discard blocks * only if those blocks are safe to discard (i.e. * they're not FILE blocks). */ ret = consume(a, rar->file.bytes_remaining); if(ret != ARCHIVE_OK) { return ret; } rar->file.bytes_remaining = 0; } else { /* If we're not in merge mode, use safe skipping code. * This will ensure we'll handle solid archives * properly. */ ret = rar5_read_data_skip(a); if(ret != ARCHIVE_OK) { return ret; } } } return ARCHIVE_OK; } static int scan_for_signature(struct archive_read* a); /* Base block processing function. A 'base block' is a RARv5 header block * that tells the reader what kind of data is stored inside the block. * * From the birds-eye view a RAR file looks file this: * * ... * * There are a few types of base blocks. Those types are specified inside * the 'switch' statement in this function. For example purposes, I'll write * how a standard RARv5 file could look like here: * *
* * The structure above could describe an archive file with 3 files in it, * one service "QuickOpen" block (that is ignored by this parser), and an * end of file base block marker. * * If the file is stored in multiple archive files ("multiarchive"), it might * look like this: * * .part01.rar:
* .part02.rar:
* .part03.rar:
* * This example could describe 3 RAR files that contain ONE archived file. * Or it could describe 3 RAR files that contain 3 different files. Or 3 * RAR files than contain 2 files. It all depends what metadata is stored in * the headers of blocks. * * Each block contains info about its size, the name of the file it's * storing inside, and whether this FILE block is a continuation block of * previous archive ('split before'), and is this FILE block should be * continued in another archive ('split after'). By parsing the 'split before' * and 'split after' flags, we're able to tell if multiple base blocks * are describing one file, or multiple files (with the same filename, for * example). * * One thing to note is that if we're parsing the first block, and * we see 'split after' flag, then we need to jump over to another * block to be able to decompress rest of the data. To do this, we need * to skip the block, then switch to another file, then skip the * block,
block, and then we're standing on the proper * block. */ static int process_base_block(struct archive_read* a, struct archive_entry* entry) { const size_t SMALLEST_RAR5_BLOCK_SIZE = 3; struct rar5* rar = get_context(a); uint32_t hdr_crc, computed_crc; size_t raw_hdr_size = 0, hdr_size_len, hdr_size; size_t header_id = 0; size_t header_flags = 0; const uint8_t* p; int ret; enum HEADER_TYPE { HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02, HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05, HEAD_UNKNOWN = 0xff, }; /* Skip any unprocessed data for this file. */ ret = skip_unprocessed_bytes(a); if(ret != ARCHIVE_OK) return ret; /* Read the expected CRC32 checksum. */ if(!read_u32(a, &hdr_crc)) { return ARCHIVE_EOF; } /* Read header size. */ if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { return ARCHIVE_EOF; } hdr_size = raw_hdr_size + hdr_size_len; /* Sanity check, maximum header size for RAR5 is 2MB. */ if(hdr_size > (2 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Base block header is too large"); return ARCHIVE_FATAL; } /* Additional sanity checks to weed out invalid files. */ if(raw_hdr_size == 0 || hdr_size_len == 0 || hdr_size < SMALLEST_RAR5_BLOCK_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Too small block encountered (%zu bytes)", raw_hdr_size); return ARCHIVE_FATAL; } /* Read the whole header data into memory, maximum memory use here is * 2MB. */ if(!read_ahead(a, hdr_size, &p)) { return ARCHIVE_EOF; } /* Verify the CRC32 of the header data. */ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); if(computed_crc != hdr_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return ARCHIVE_FATAL; } /* If the checksum is OK, we proceed with parsing. */ if(ARCHIVE_OK != consume(a, hdr_size_len)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &header_id, NULL)) return ARCHIVE_EOF; if(!read_var_sized(a, &header_flags, NULL)) return ARCHIVE_EOF; rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; rar->generic.size = (int)hdr_size; rar->generic.last_header_id = (int)header_id; rar->main.endarc = 0; /* Those are possible header ids in RARv5. */ switch(header_id) { case HEAD_MAIN: ret = process_head_main(a, rar, entry, header_flags); /* Main header doesn't have any files in it, so it's * pointless to return to the caller. Retry to next * header, which should be HEAD_FILE/HEAD_SERVICE. */ if(ret == ARCHIVE_OK) return ARCHIVE_RETRY; return ret; case HEAD_SERVICE: ret = process_head_service(a, rar, entry, header_flags); return ret; case HEAD_FILE: ret = process_head_file(a, rar, entry, header_flags); return ret; case HEAD_CRYPT: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encryption is not supported"); return ARCHIVE_FATAL; case HEAD_ENDARC: rar->main.endarc = 1; /* After encountering an end of file marker, we need * to take into consideration if this archive is * continued in another file (i.e. is it part01.rar: * is there a part02.rar?) */ if(rar->main.volume) { /* In case there is part02.rar, position the * read pointer in a proper place, so we can * resume parsing. */ ret = scan_for_signature(a); if(ret == ARCHIVE_FATAL) { return ARCHIVE_EOF; } else { if(rar->vol.expected_vol_no == UINT_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header error"); return ARCHIVE_FATAL; } rar->vol.expected_vol_no = rar->main.vol_no + 1; return ARCHIVE_OK; } } else { return ARCHIVE_EOF; } case HEAD_MARK: return ARCHIVE_EOF; default: if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header type error"); return ARCHIVE_FATAL; } else { /* If the block is marked as 'skip if unknown', * do as the flag says: skip the block * instead on failing on it. */ return ARCHIVE_RETRY; } } #if !defined WIN32 // Not reached. archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal unpacker error"); return ARCHIVE_FATAL; #endif } static int skip_base_block(struct archive_read* a) { int ret; struct rar5* rar = get_context(a); /* Create a new local archive_entry structure that will be operated on * by header reader; operations on this archive_entry will be discarded. */ struct archive_entry* entry = archive_entry_new(); ret = process_base_block(a, entry); /* Discard operations on this archive_entry structure. */ archive_entry_free(entry); if(ret == ARCHIVE_FATAL) return ret; if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) return ARCHIVE_OK; if(ret == ARCHIVE_OK) return ARCHIVE_RETRY; else return ret; } static int rar5_read_header(struct archive_read *a, struct archive_entry *entry) { struct rar5* rar = get_context(a); int ret; if(rar->header_initialized == 0) { init_header(a); rar->header_initialized = 1; } if(rar->skipped_magic == 0) { if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) { return ARCHIVE_EOF; } rar->skipped_magic = 1; } do { ret = process_base_block(a, entry); } while(ret == ARCHIVE_RETRY || (rar->main.endarc > 0 && ret == ARCHIVE_OK)); return ret; } static void init_unpack(struct rar5* rar) { rar->file.calculated_crc32 = 0; init_window_mask(rar); free(rar->cstate.window_buf); free(rar->cstate.filtered_buf); if(rar->cstate.window_size > 0) { rar->cstate.window_buf = calloc(1, rar->cstate.window_size); rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); } else { rar->cstate.window_buf = NULL; rar->cstate.filtered_buf = NULL; } rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); } static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { int verify_crc; if(rar->skip_mode) { #if defined CHECK_CRC_ON_SOLID_SKIP verify_crc = 1; #else verify_crc = 0; #endif } else verify_crc = 1; if(verify_crc) { /* Don't update CRC32 if the file doesn't have the * `stored_crc32` info filled in. */ if(rar->file.stored_crc32 > 0) { rar->file.calculated_crc32 = crc32(rar->file.calculated_crc32, p, to_read); } /* Check if the file uses an optional BLAKE2sp checksum * algorithm. */ if(rar->file.has_blake2 > 0) { /* Return value of the `update` function is always 0, * so we can explicitly ignore it here. */ (void) blake2sp_update(&rar->file.b2state, p, to_read); } } } static int create_decode_tables(uint8_t* bit_length, struct decode_table* table, int size) { int code, upper_limit = 0, i, lc[16]; uint32_t decode_pos_clone[rar5_countof(table->decode_pos)]; ssize_t cur_len, quick_data_size; memset(&lc, 0, sizeof(lc)); memset(table->decode_num, 0, sizeof(table->decode_num)); table->size = size; table->quick_bits = size == HUFF_NC ? 10 : 7; for(i = 0; i < size; i++) { lc[bit_length[i] & 15]++; } lc[0] = 0; table->decode_pos[0] = 0; table->decode_len[0] = 0; for(i = 1; i < 16; i++) { upper_limit += lc[i]; table->decode_len[i] = upper_limit << (16 - i); table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1]; upper_limit <<= 1; } memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); for(i = 0; i < size; i++) { uint8_t clen = bit_length[i] & 15; if(clen > 0) { int last_pos = decode_pos_clone[clen]; table->decode_num[last_pos] = i; decode_pos_clone[clen]++; } } quick_data_size = (int64_t)1 << table->quick_bits; cur_len = 1; for(code = 0; code < quick_data_size; code++) { int bit_field = code << (16 - table->quick_bits); int dist, pos; while(cur_len < rar5_countof(table->decode_len) && bit_field >= table->decode_len[cur_len]) { cur_len++; } table->quick_len[code] = (uint8_t) cur_len; dist = bit_field - table->decode_len[cur_len - 1]; dist >>= (16 - cur_len); pos = table->decode_pos[cur_len & 15] + dist; if(cur_len < rar5_countof(table->decode_pos) && pos < size) { table->quick_num[code] = table->decode_num[pos]; } else { table->quick_num[code] = 0; } } return ARCHIVE_OK; } static int decode_number(struct archive_read* a, struct decode_table* table, const uint8_t* p, uint16_t* num) { int i, bits, dist; uint16_t bitfield; uint32_t pos; struct rar5* rar = get_context(a); if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { return ARCHIVE_EOF; } bitfield &= 0xfffe; if(bitfield < table->decode_len[table->quick_bits]) { int code = bitfield >> (16 - table->quick_bits); skip_bits(rar, table->quick_len[code]); *num = table->quick_num[code]; return ARCHIVE_OK; } bits = 15; for(i = table->quick_bits + 1; i < 15; i++) { if(bitfield < table->decode_len[i]) { bits = i; break; } } skip_bits(rar, bits); dist = bitfield - table->decode_len[bits - 1]; dist >>= (16 - bits); pos = table->decode_pos[bits] + dist; if(pos >= table->size) pos = 0; *num = table->decode_num[pos]; return ARCHIVE_OK; } /* Reads and parses Huffman tables from the beginning of the block. */ static int parse_tables(struct archive_read* a, struct rar5* rar, const uint8_t* p) { int ret, value, i, w, idx = 0; uint8_t bit_length[HUFF_BC], table[HUFF_TABLE_SIZE], nibble_mask = 0xF0, nibble_shift = 4; enum { ESCAPE = 15 }; /* The data for table generation is compressed using a simple RLE-like * algorithm when storing zeroes, so we need to unpack it first. */ for(w = 0, i = 0; w < HUFF_BC;) { if(i >= rar->cstate.cur_block_size) { /* Truncated data, can't continue. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated data in huffman tables"); return ARCHIVE_FATAL; } value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) ++i; nibble_mask ^= 0xFF; nibble_shift ^= 4; /* Values smaller than 15 is data, so we write it directly. * Value 15 is a flag telling us that we need to unpack more * bytes. */ if(value == ESCAPE) { value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) ++i; nibble_mask ^= 0xFF; nibble_shift ^= 4; if(value == 0) { /* We sometimes need to write the actual value * of 15, so this case handles that. */ bit_length[w++] = ESCAPE; } else { int k; /* Fill zeroes. */ for(k = 0; (k < value + 2) && (w < HUFF_BC); k++) { bit_length[w++] = 0; } } } else { bit_length[w++] = value; } } rar->bits.in_addr = i; rar->bits.bit_addr = nibble_shift ^ 4; ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); return ARCHIVE_FATAL; } for(i = 0; i < HUFF_TABLE_SIZE;) { uint16_t num; if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) { /* Truncated data, can't continue. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated data in huffman tables (#2)"); return ARCHIVE_FATAL; } ret = decode_number(a, &rar->cstate.bd, p, &num); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); return ARCHIVE_FATAL; } if(num < 16) { /* 0..15: store directly */ table[i] = (uint8_t) num; i++; } else if(num < 18) { /* 16..17: repeat previous code */ uint16_t n; if(ARCHIVE_OK != read_bits_16(rar, p, &n)) return ARCHIVE_EOF; if(num == 16) { n >>= 13; n += 3; skip_bits(rar, 3); } else { n >>= 9; n += 11; skip_bits(rar, 7); } if(i > 0) { while(n-- > 0 && i < HUFF_TABLE_SIZE) { table[i] = table[i - 1]; i++; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unexpected error when decoding " "huffman tables"); return ARCHIVE_FATAL; } } else { /* other codes: fill with zeroes `n` times */ uint16_t n; if(ARCHIVE_OK != read_bits_16(rar, p, &n)) return ARCHIVE_EOF; if(num == 18) { n >>= 13; n += 3; skip_bits(rar, 3); } else { n >>= 9; n += 11; skip_bits(rar, 7); } while(n-- > 0 && i < HUFF_TABLE_SIZE) table[i++] = 0; } } ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create literal table"); return ARCHIVE_FATAL; } idx += HUFF_NC; ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create distance table"); return ARCHIVE_FATAL; } idx += HUFF_DC; ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create lower bits of distances table"); return ARCHIVE_FATAL; } idx += HUFF_LDC; ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create repeating distances table"); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Parses the block header, verifies its CRC byte, and saves the header * fields inside the `hdr` pointer. */ static int parse_block_header(struct archive_read* a, const uint8_t* p, ssize_t* block_size, struct compressed_block_header* hdr) { uint8_t calculated_cksum; memcpy(hdr, p, sizeof(struct compressed_block_header)); if(bf_byte_count(hdr) > 2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block header size (was %d, max is 2)", bf_byte_count(hdr)); return ARCHIVE_FATAL; } /* This should probably use bit reader interface in order to be more * future-proof. */ *block_size = 0; switch(bf_byte_count(hdr)) { /* 1-byte block size */ case 0: *block_size = *(const uint8_t*) &p[2]; break; /* 2-byte block size */ case 1: *block_size = archive_le16dec(&p[2]); break; /* 3-byte block size */ case 2: *block_size = archive_le32dec(&p[2]); *block_size &= 0x00FFFFFF; break; /* Other block sizes are not supported. This case is not * reached, because we have an 'if' guard before the switch * that makes sure of it. */ default: return ARCHIVE_FATAL; } /* Verify the block header checksum. 0x5A is a magic value and is * always * constant. */ calculated_cksum = 0x5A ^ (uint8_t) hdr->block_flags_u8 ^ (uint8_t) *block_size ^ (uint8_t) (*block_size >> 8) ^ (uint8_t) (*block_size >> 16); if(calculated_cksum != hdr->block_cksum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Block checksum error: got 0x%x, expected 0x%x", hdr->block_cksum, calculated_cksum); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Convenience function used during filter processing. */ static int parse_filter_data(struct rar5* rar, const uint8_t* p, uint32_t* filter_data) { int i, bytes; uint32_t data = 0; if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) return ARCHIVE_EOF; bytes++; for(i = 0; i < bytes; i++) { uint16_t byte; if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { return ARCHIVE_EOF; } /* Cast to uint32_t will ensure the shift operation will not * produce undefined result. */ data += ((uint32_t) byte >> 8) << (i * 8); skip_bits(rar, 8); } *filter_data = data; return ARCHIVE_OK; } /* Function is used during sanity checking. */ static int is_valid_filter_block_start(struct rar5* rar, uint32_t start) { const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; const int64_t last_bs = rar->cstate.last_block_start; const ssize_t last_bl = rar->cstate.last_block_length; if(last_bs == 0 || last_bl == 0) { /* We didn't have any filters yet, so accept this offset. */ return 1; } if(block_start >= last_bs + last_bl) { /* Current offset is bigger than last block's end offset, so * accept current offset. */ return 1; } /* Any other case is not a normal situation and we should fail. */ return 0; } /* The function will create a new filter, read its parameters from the input * stream and add it to the filter collection. */ static int parse_filter(struct archive_read* ar, const uint8_t* p) { uint32_t block_start, block_length; uint16_t filter_type; struct filter_info* filt = NULL; struct rar5* rar = get_context(ar); /* Read the parameters from the input stream. */ if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) return ARCHIVE_EOF; if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) return ARCHIVE_EOF; if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) return ARCHIVE_EOF; filter_type >>= 13; skip_bits(rar, 3); /* Perform some sanity checks on this filter parameters. Note that we * allow only DELTA, E8/E9 and ARM filters here, because rest of * filters are not used in RARv5. */ if(block_length < 4 || block_length > 0x400000 || filter_type > FILTER_ARM || !is_valid_filter_block_start(rar, block_start)) { archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filter encountered"); return ARCHIVE_FATAL; } /* Allocate a new filter. */ filt = add_new_filter(rar); if(filt == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate memory for a filter descriptor."); return ARCHIVE_FATAL; } filt->type = filter_type; filt->block_start = rar->cstate.write_ptr + block_start; filt->block_length = block_length; rar->cstate.last_block_start = filt->block_start; rar->cstate.last_block_length = filt->block_length; /* Read some more data in case this is a DELTA filter. Other filter * types don't require any additional data over what was already * read. */ if(filter_type == FILTER_DELTA) { int channels; if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) return ARCHIVE_EOF; filt->channels = channels + 1; } return ARCHIVE_OK; } static int decode_code_length(struct rar5* rar, const uint8_t* p, uint16_t code) { int lbits, length = 2; if(code < 8) { lbits = 0; length += code; } else { lbits = code / 4 - 1; length += (4 | (code & 3)) << lbits; } if(lbits > 0) { int add; if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) return -1; length += add; } return length; } static int copy_string(struct archive_read* a, int len, int dist) { struct rar5* rar = get_context(a); const uint64_t cmask = rar->cstate.window_mask; const uint64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; int i; if (rar->cstate.window_buf == NULL) return ARCHIVE_FATAL; /* The unpacker spends most of the time in this function. It would be * a good idea to introduce some optimizations here. * * Just remember that this loop treats buffers that overlap differently * than buffers that do not overlap. This is why a simple memcpy(3) * call will not be enough. */ for(i = 0; i < len; i++) { const ssize_t write_idx = (write_ptr + i) & cmask; const ssize_t read_idx = (write_ptr + i - dist) & cmask; rar->cstate.window_buf[write_idx] = rar->cstate.window_buf[read_idx]; } rar->cstate.write_ptr += len; return ARCHIVE_OK; } static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { struct rar5* rar = get_context(a); uint16_t num; int ret; const uint64_t cmask = rar->cstate.window_mask; const struct compressed_block_header* hdr = &rar->last_block_hdr; const uint8_t bit_size = 1 + bf_bit_size(hdr); while(1) { if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > (rar->cstate.window_size >> 1)) { /* Don't allow growing data by more than half of the * window size at a time. In such case, break the loop; * next call to this function will continue processing * from this moment. */ break; } if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && rar->bits.bit_addr >= bit_size)) { /* If the program counter is here, it means the * function has finished processing the block. */ rar->cstate.block_parsing_finished = 1; break; } /* Decode the next literal. */ if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { return ARCHIVE_EOF; } /* Num holds a decompression literal, or 'command code'. * * - Values lower than 256 are just bytes. Those codes * can be stored in the output buffer directly. * * - Code 256 defines a new filter, which is later used to * ransform the data block accordingly to the filter type. * The data block needs to be fully uncompressed first. * * - Code bigger than 257 and smaller than 262 define * a repetition pattern that should be copied from * an already uncompressed chunk of data. */ if(num < 256) { /* Directly store the byte. */ int64_t write_idx = rar->cstate.solid_offset + rar->cstate.write_ptr++; rar->cstate.window_buf[write_idx & cmask] = (uint8_t) num; continue; } else if(num >= 262) { uint16_t dist_slot; int len = decode_code_length(rar, p, num - 262), dbits, dist = 1; if(len == -1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the code length"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, &dist_slot)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the distance slot"); return ARCHIVE_FATAL; } if(dist_slot < 4) { dbits = 0; dist += dist_slot; } else { dbits = dist_slot / 2 - 1; /* Cast to uint32_t will make sure the shift * left operation won't produce undefined * result. Then, the uint32_t type will * be implicitly casted to int. */ dist += (uint32_t) (2 | (dist_slot & 1)) << dbits; } if(dbits > 0) { if(dbits >= 4) { uint32_t add = 0; uint16_t low_dist; if(dbits > 4) { if(ARCHIVE_OK != read_bits_32( rar, p, &add)) { /* Return EOF if we * can't read more * data. */ return ARCHIVE_EOF; } skip_bits(rar, dbits - 4); add = (add >> ( 36 - dbits)) << 4; dist += add; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.ldd, p, &low_dist)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the " "distance slot"); return ARCHIVE_FATAL; } if(dist >= INT_MAX - low_dist - 1) { /* This only happens in * invalid archives. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Distance pointer " "overflow"); return ARCHIVE_FATAL; } dist += low_dist; } else { /* dbits is one of [0,1,2,3] */ int add; if(ARCHIVE_OK != read_consume_bits(rar, p, dbits, &add)) { /* Return EOF if we can't read * more data. */ return ARCHIVE_EOF; } dist += add; } } if(dist > 0x100) { len++; if(dist > 0x2000) { len++; if(dist > 0x40000) { len++; } } } dist_cache_push(rar, dist); rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) return ARCHIVE_FATAL; continue; } else if(num == 256) { /* Create a filter. */ ret = parse_filter(a, p); if(ret != ARCHIVE_OK) return ret; continue; } else if(num == 257) { if(rar->cstate.last_len != 0) { if(ARCHIVE_OK != copy_string(a, rar->cstate.last_len, rar->cstate.dist_cache[0])) { return ARCHIVE_FATAL; } } continue; } else { /* num < 262 */ const int idx = num - 258; const int dist = dist_cache_touch(rar, idx); uint16_t len_slot; int len; if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, &len_slot)) { return ARCHIVE_FATAL; } len = decode_code_length(rar, p, len_slot); rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) return ARCHIVE_FATAL; continue; } } return ARCHIVE_OK; } /* Binary search for the RARv5 signature. */ static int scan_for_signature(struct archive_read* a) { const uint8_t* p; const int chunk_size = 512; ssize_t i; char signature[sizeof(rar5_signature_xor)]; /* If we're here, it means we're on an 'unknown territory' data. * There's no indication what kind of data we're reading here. * It could be some text comment, any kind of binary data, * digital sign, dragons, etc. * * We want to find a valid RARv5 magic header inside this unknown * data. */ /* Is it possible in libarchive to just skip everything until the * end of the file? If so, it would be a better approach than the * current implementation of this function. */ rar5_signature(signature); while(1) { if(!read_ahead(a, chunk_size, &p)) return ARCHIVE_EOF; for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor); i++) { if(memcmp(&p[i], signature, sizeof(rar5_signature_xor)) == 0) { /* Consume the number of bytes we've used to * search for the signature, as well as the * number of bytes used by the signature * itself. After this we should be standing * on a valid base block header. */ (void) consume(a, i + sizeof(rar5_signature_xor)); return ARCHIVE_OK; } } consume(a, chunk_size); } return ARCHIVE_FATAL; } /* This function will switch the multivolume archive file to another file, * i.e. from part03 to part 04. */ static int advance_multivolume(struct archive_read* a) { int lret; struct rar5* rar = get_context(a); /* A small state machine that will skip unnecessary data, needed to * switch from one multivolume to another. Such skipping is needed if * we want to be an stream-oriented (instead of file-oriented) * unpacker. * * The state machine starts with `rar->main.endarc` == 0. It also * assumes that current stream pointer points to some base block * header. * * The `endarc` field is being set when the base block parsing * function encounters the 'end of archive' marker. */ while(1) { if(rar->main.endarc == 1) { int looping = 1; rar->main.endarc = 0; while(looping) { lret = skip_base_block(a); switch(lret) { case ARCHIVE_RETRY: /* Continue looping. */ break; case ARCHIVE_OK: /* Break loop. */ looping = 0; break; default: /* Forward any errors to the * caller. */ return lret; } } break; } else { /* Skip current base block. In order to properly skip * it, we really need to simply parse it and discard * the results. */ lret = skip_base_block(a); if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED) return lret; /* The `skip_base_block` function tells us if we * should continue with skipping, or we should stop * skipping. We're trying to skip everything up to * a base FILE block. */ if(lret != ARCHIVE_RETRY) { /* If there was an error during skipping, or we * have just skipped a FILE base block... */ if(rar->main.endarc == 0) { return lret; } else { continue; } } } } return ARCHIVE_OK; } /* Merges the partial block from the first multivolume archive file, and * partial block from the second multivolume archive file. The result is * a chunk of memory containing the whole block, and the stream pointer * is advanced to the next block in the second multivolume archive file. */ static int merge_block(struct archive_read* a, ssize_t block_size, const uint8_t** p) { struct rar5* rar = get_context(a); ssize_t cur_block_size, partial_offset = 0; const uint8_t* lp; int ret; if(rar->merge_mode) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Recursive merge is not allowed"); return ARCHIVE_FATAL; } /* Set a flag that we're in the switching mode. */ rar->cstate.switch_multivolume = 1; /* Reallocate the memory which will hold the whole block. */ if(rar->vol.push_buf) free((void*) rar->vol.push_buf); /* Increasing the allocation block by 8 is due to bit reading functions, * which are using additional 2 or 4 bytes. Allocating the block size * by exact value would make bit reader perform reads from invalid * memory block when reading the last byte from the buffer. */ rar->vol.push_buf = malloc(block_size + 8); if(!rar->vol.push_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a merge block buffer."); return ARCHIVE_FATAL; } /* Valgrind complains if the extension block for bit reader is not * initialized, so initialize it. */ memset(&rar->vol.push_buf[block_size], 0, 8); /* A single block can span across multiple multivolume archive files, * so we use a loop here. This loop will consume enough multivolume * archive files until the whole block is read. */ while(1) { /* Get the size of current block chunk in this multivolume * archive file and read it. */ cur_block_size = rar5_min(rar->file.bytes_remaining, block_size - partial_offset); if(cur_block_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encountered block size == 0 during block merge"); return ARCHIVE_FATAL; } if(!read_ahead(a, cur_block_size, &lp)) return ARCHIVE_EOF; /* Sanity check; there should never be a situation where this * function reads more data than the block's size. */ if(partial_offset + cur_block_size > block_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Consumed too much data when merging blocks."); return ARCHIVE_FATAL; } /* Merge previous block chunk with current block chunk, * or create first block chunk if this is our first * iteration. */ memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); /* Advance the stream read pointer by this block chunk size. */ if(ARCHIVE_OK != consume(a, cur_block_size)) return ARCHIVE_EOF; /* Update the pointers. `partial_offset` contains information * about the sum of merged block chunks. */ partial_offset += cur_block_size; rar->file.bytes_remaining -= cur_block_size; /* If `partial_offset` is the same as `block_size`, this means * we've merged all block chunks and we have a valid full * block. */ if(partial_offset == block_size) { break; } /* If we don't have any bytes to read, this means we should * switch to another multivolume archive file. */ if(rar->file.bytes_remaining == 0) { rar->merge_mode++; ret = advance_multivolume(a); rar->merge_mode--; if(ret != ARCHIVE_OK) { return ret; } } } *p = rar->vol.push_buf; /* If we're here, we can resume unpacking by processing the block * pointed to by the `*p` memory pointer. */ return ARCHIVE_OK; } static int process_block(struct archive_read* a) { const uint8_t* p; struct rar5* rar = get_context(a); int ret; /* If we don't have any data to be processed, this most probably means * we need to switch to the next volume. */ if(rar->main.volume && rar->file.bytes_remaining == 0) { ret = advance_multivolume(a); if(ret != ARCHIVE_OK) return ret; } if(rar->cstate.block_parsing_finished) { ssize_t block_size; ssize_t to_skip; ssize_t cur_block_size; /* The header size won't be bigger than 6 bytes. */ if(!read_ahead(a, 6, &p)) { /* Failed to prefetch data block header. */ return ARCHIVE_EOF; } /* * Read block_size by parsing block header. Validate the header * by calculating CRC byte stored inside the header. Size of * the header is not constant (block size can be stored either * in 1 or 2 bytes), that's why block size is left out from the * `compressed_block_header` structure and returned by * `parse_block_header` as the second argument. */ ret = parse_block_header(a, p, &block_size, &rar->last_block_hdr); if(ret != ARCHIVE_OK) { return ret; } /* Skip block header. Next data is huffman tables, * if present. */ to_skip = sizeof(struct compressed_block_header) + bf_byte_count(&rar->last_block_hdr) + 1; if(ARCHIVE_OK != consume(a, to_skip)) return ARCHIVE_EOF; rar->file.bytes_remaining -= to_skip; /* The block size gives information about the whole block size, * but the block could be stored in split form when using * multi-volume archives. In this case, the block size will be * bigger than the actual data stored in this file. Remaining * part of the data will be in another file. */ cur_block_size = rar5_min(rar->file.bytes_remaining, block_size); if(block_size > rar->file.bytes_remaining) { /* If current blocks' size is bigger than our data * size, this means we have a multivolume archive. * In this case, skip all base headers until the end * of the file, proceed to next "partXXX.rar" volume, * find its signature, skip all headers up to the first * FILE base header, and continue from there. * * Note that `merge_block` will update the `rar` * context structure quite extensively. */ ret = merge_block(a, block_size, &p); if(ret != ARCHIVE_OK) { return ret; } cur_block_size = block_size; /* Current stream pointer should be now directly * *after* the block that spanned through multiple * archive files. `p` pointer should have the data of * the *whole* block (merged from partial blocks * stored in multiple archives files). */ } else { rar->cstate.switch_multivolume = 0; /* Read the whole block size into memory. This can take * up to 8 megabytes of memory in theoretical cases. * Might be worth to optimize this and use a standard * chunk of 4kb's. */ if(!read_ahead(a, 4 + cur_block_size, &p)) { /* Failed to prefetch block data. */ return ARCHIVE_EOF; } } rar->cstate.block_buf = p; rar->cstate.cur_block_size = cur_block_size; rar->cstate.block_parsing_finished = 0; rar->bits.in_addr = 0; rar->bits.bit_addr = 0; if(bf_is_table_present(&rar->last_block_hdr)) { /* Load Huffman tables. */ ret = parse_tables(a, rar, p); if(ret != ARCHIVE_OK) { /* Error during decompression of Huffman * tables. */ return ret; } } } else { /* Block parsing not finished, reuse previous memory buffer. */ p = rar->cstate.block_buf; } /* Uncompress the block, or a part of it, depending on how many bytes * will be generated by uncompressing the block. * * In case too many bytes will be generated, calling this function * again will resume the uncompression operation. */ ret = do_uncompress_block(a, p); if(ret != ARCHIVE_OK) { return ret; } if(rar->cstate.block_parsing_finished && rar->cstate.switch_multivolume == 0 && rar->cstate.cur_block_size > 0) { /* If we're processing a normal block, consume the whole * block. We can do this because we've already read the whole * block to memory. */ if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) return ARCHIVE_FATAL; rar->file.bytes_remaining -= rar->cstate.cur_block_size; } else if(rar->cstate.switch_multivolume) { /* Don't consume the block if we're doing multivolume * processing. The volume switching function will consume * the proper count of bytes instead. */ rar->cstate.switch_multivolume = 0; } return ARCHIVE_OK; } /* Pops the `buf`, `size` and `offset` from the "data ready" stack. * * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY * when there is no data on the stack. */ static int use_data(struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { int i; for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { struct data_ready *d = &rar->cstate.dready[i]; if(d->used) { if(buf) *buf = d->buf; if(size) *size = d->size; if(offset) *offset = d->offset; d->used = 0; return ARCHIVE_OK; } } return ARCHIVE_RETRY; } /* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready * FIFO stack. Those values will be popped from this stack by the `use_data` * function. */ static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset) { int i; /* Don't push if we're in skip mode. This is needed because solid * streams need full processing even if we're skipping data. After * fully processing the stream, we need to discard the generated bytes, * because we're interested only in the side effect: building up the * internal window circular buffer. This window buffer will be used * later during unpacking of requested data. */ if(rar->skip_mode) return ARCHIVE_OK; /* Sanity check. */ if(offset != rar->file.last_offset + rar->file.last_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Sanity check error: output stream is not continuous"); return ARCHIVE_FATAL; } for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { struct data_ready* d = &rar->cstate.dready[i]; if(!d->used) { d->used = 1; d->buf = buf; d->size = size; d->offset = offset; /* These fields are used only in sanity checking. */ rar->file.last_offset = offset; rar->file.last_size = size; /* Calculate the checksum of this new block before * submitting data to libarchive's engine. */ update_crc(rar, d->buf, d->size); return ARCHIVE_OK; } } /* Program counter will reach this code if the `rar->cstate.data_ready` * stack will be filled up so that no new entries will be allowed. The * code shouldn't allow such situation to occur. So we treat this case * as an internal error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Error: premature end of data_ready stack"); return ARCHIVE_FATAL; } /* This function uncompresses the data that is stored in the base * block. * * The FILE base block looks like this: * *
... * * The
is a block header, that is parsed in parse_block_header(). * It's a "compressed_block_header" structure, containing metadata needed * to know when we should stop looking for more blocks. * * contain data needed to set up the huffman tables, needed * for the actual decompression. * * Each consists of series of literals: * * ... * * Those literals generate the uncompression data. They operate on a circular * buffer, sometimes writing raw data into it, sometimes referencing * some previous data inside this buffer, and sometimes declaring a filter * that will need to be executed on the data stored in the circular buffer. * It all depends on the literal that is used. * * Sometimes blocks produce output data, sometimes they don't. For example, for * some huge files that use lots of filters, sometimes a block is filled with * only filter declaration literals. Such blocks won't produce any data in the * circular buffer. * * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte, * because a literal can reference previously decompressed data. For example, * there can be a literal that says: 'append a byte 0xFE here', and after * it another literal can say 'append 1 megabyte of data from circular buffer * offset 0x12345'. This is how RAR format handles compressing repeated * patterns. * * The RAR compressor creates those literals and the actual efficiency of * compression depends on what those literals are. The literals can also * be seen as a kind of a non-turing-complete virtual machine that simply * tells the decompressor what it should do. * */ static int do_uncompress_file(struct archive_read* a) { struct rar5* rar = get_context(a); int ret; int64_t max_end_pos; if(!rar->cstate.initialized) { /* Don't perform full context reinitialization if we're * processing a solid archive. */ if(!rar->main.solid || !rar->cstate.window_buf) { init_unpack(rar); } rar->cstate.initialized = 1; } if(rar->cstate.all_filters_applied == 1) { /* We use while(1) here, but standard case allows for just 1 * iteration. The loop will iterate if process_block() didn't * generate any data at all. This can happen if the block * contains only filter definitions (this is common in big * files). */ while(1) { ret = process_block(a); if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) return ret; if(rar->cstate.last_write_ptr == rar->cstate.write_ptr) { /* The block didn't generate any new data, * so just process a new block. */ continue; } /* The block has generated some new data, so break * the loop. */ break; } } /* Try to run filters. If filters won't be applied, it means that * insufficient data was generated. */ ret = apply_filters(a); if(ret == ARCHIVE_RETRY) { return ARCHIVE_OK; } else if(ret == ARCHIVE_FATAL) { return ARCHIVE_FATAL; } /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ if(cdeque_size(&rar->cstate.filters) > 0) { /* Check if we can write something before hitting first * filter. */ struct filter_info* flt; /* Get the block_start offset from the first filter. */ if(CDE_OK != cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Can't read first filter"); return ARCHIVE_FATAL; } max_end_pos = rar5_min(flt->block_start, rar->cstate.write_ptr); } else { /* There are no filters defined, or all filters were applied. * This means we can just store the data without any * postprocessing. */ max_end_pos = rar->cstate.write_ptr; } if(max_end_pos == rar->cstate.last_write_ptr) { /* We can't write anything yet. The block uncompression * function did not generate enough data, and no filter can be * applied. At the same time we don't have any data that can be * stored without filter postprocessing. This means we need to * wait for more data to be generated, so we can apply the * filters. * * Signal the caller that we need more data to be able to do * anything. */ return ARCHIVE_RETRY; } else { /* We can write the data before hitting the first filter. * So let's do it. The push_window_data() function will * effectively return the selected data block to the user * application. */ push_window_data(a, rar, rar->cstate.last_write_ptr, max_end_pos); rar->cstate.last_write_ptr = max_end_pos; } return ARCHIVE_OK; } static int uncompress_file(struct archive_read* a) { int ret; while(1) { /* Sometimes the uncompression function will return a * 'retry' signal. If this will happen, we have to retry * the function. */ ret = do_uncompress_file(a); if(ret != ARCHIVE_RETRY) return ret; } } static int do_unstore_file(struct archive_read* a, struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { size_t to_read; const uint8_t* p; if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && rar->generic.split_after > 0) { int ret; rar->cstate.switch_multivolume = 1; ret = advance_multivolume(a); rar->cstate.switch_multivolume = 0; if(ret != ARCHIVE_OK) { /* Failed to advance to next multivolume archive * file. */ return ret; } } to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); if(to_read == 0) { return ARCHIVE_EOF; } if(!read_ahead(a, to_read, &p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "I/O error when unstoring file"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != consume(a, to_read)) { return ARCHIVE_EOF; } if(buf) *buf = p; if(size) *size = to_read; if(offset) *offset = rar->cstate.last_unstore_ptr; rar->file.bytes_remaining -= to_read; rar->cstate.last_unstore_ptr += to_read; update_crc(rar, p, to_read); return ARCHIVE_OK; } static int do_unpack(struct archive_read* a, struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { enum COMPRESSION_METHOD { STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, BEST = 5 }; if(rar->file.service > 0) { return do_unstore_file(a, rar, buf, size, offset); } else { switch(rar->cstate.method) { case STORE: return do_unstore_file(a, rar, buf, size, offset); case FASTEST: /* fallthrough */ case FAST: /* fallthrough */ case NORMAL: /* fallthrough */ case GOOD: /* fallthrough */ case BEST: return uncompress_file(a); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Compression method not supported: 0x%x", rar->cstate.method); return ARCHIVE_FATAL; } } #if !defined WIN32 /* Not reached. */ return ARCHIVE_OK; #endif } static int verify_checksums(struct archive_read* a) { int verify_crc; struct rar5* rar = get_context(a); /* Check checksums only when actually unpacking the data. There's no * need to calculate checksum when we're skipping data in solid archives * (skipping in solid archives is the same thing as unpacking compressed * data and discarding the result). */ if(!rar->skip_mode) { /* Always check checksums if we're not in skip mode */ verify_crc = 1; } else { /* We can override the logic above with a compile-time option * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, * and it will check checksums of unpacked data even when * we're skipping it. */ #if defined CHECK_CRC_ON_SOLID_SKIP /* Debug case */ verify_crc = 1; #else /* Normal case */ verify_crc = 0; #endif } if(verify_crc) { /* During unpacking, on each unpacked block we're calling the * update_crc() function. Since we are here, the unpacking * process is already over and we can check if calculated * checksum (CRC32 or BLAKE2sp) is the same as what is stored * in the archive. */ if(rar->file.stored_crc32 > 0) { /* Check CRC32 only when the file contains a CRC32 * value for this file. */ if(rar->file.calculated_crc32 != rar->file.stored_crc32) { /* Checksums do not match; the unpacked file * is corrupted. */ DEBUG_CODE { printf("Checksum error: CRC32 " - "(was: %08x, expected: %08x)\n", + "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n", rar->file.calculated_crc32, rar->file.stored_crc32); } #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: CRC32"); return ARCHIVE_FATAL; #endif } else { DEBUG_CODE { printf("Checksum OK: CRC32 " - "(%08x/%08x)\n", + "(%08" PRIx32 "/%08" PRIx32 ")\n", rar->file.stored_crc32, rar->file.calculated_crc32); } } } if(rar->file.has_blake2 > 0) { /* BLAKE2sp is an optional checksum algorithm that is * added to RARv5 archives when using the `-htb` switch * during creation of archive. * * We now finalize the hash calculation by calling the * `final` function. This will generate the final hash * value we can use to compare it with the BLAKE2sp * checksum that is stored in the archive. * * The return value of this `final` function is not * very helpful, as it guards only against improper use. * This is why we're explicitly ignoring it. */ uint8_t b2_buf[32]; (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: BLAKE2"); return ARCHIVE_FATAL; #endif } } } /* Finalization for this file has been successfully completed. */ return ARCHIVE_OK; } static int verify_global_checksums(struct archive_read* a) { return verify_checksums(a); } /* * Decryption function for the magic signature pattern. Check the comment near * the `rar5_signature_xor` symbol to read the rationale behind this. */ static void rar5_signature(char *buf) { size_t i; for(i = 0; i < sizeof(rar5_signature_xor); i++) { buf[i] = rar5_signature_xor[i] ^ 0xA1; } } static int rar5_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int ret; struct rar5* rar = get_context(a); + + if (size) + *size = 0; if(rar->file.dir > 0) { /* Don't process any data if this file entry was declared * as a directory. This is needed, because entries marked as * directory doesn't have any dictionary buffer allocated, so * it's impossible to perform any decompression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't decompress an entry marked as a directory"); return ARCHIVE_FAILED; } if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Unpacker has written too many bytes"); return ARCHIVE_FATAL; } ret = use_data(rar, buff, size, offset); if(ret == ARCHIVE_OK) { return ret; } if(rar->file.eof == 1) { return ARCHIVE_EOF; } ret = do_unpack(a, rar, buff, size, offset); if(ret != ARCHIVE_OK) { return ret; } if(rar->file.bytes_remaining == 0 && rar->cstate.last_write_ptr == rar->file.unpacked_size) { /* If all bytes of current file were processed, run * finalization. * * Finalization will check checksum against proper values. If * some of the checksums will not match, we'll return an error * value in the last `archive_read_data` call to signal an error * to the user. */ rar->file.eof = 1; return verify_global_checksums(a); } return ARCHIVE_OK; } static int rar5_read_data_skip(struct archive_read *a) { struct rar5* rar = get_context(a); if(rar->main.solid) { /* In solid archives, instead of skipping the data, we need to * extract it, and dispose the result. The side effect of this * operation will be setting up the initial window buffer state * needed to be able to extract the selected file. */ int ret; /* Make sure to process all blocks in the compressed stream. */ while(rar->file.bytes_remaining > 0) { /* Setting the "skip mode" will allow us to skip * checksum checks during data skipping. Checking the * checksum of skipped data isn't really necessary and * it's only slowing things down. * * This is incremented instead of setting to 1 because * this data skipping function can be called * recursively. */ rar->skip_mode++; /* We're disposing 1 block of data, so we use triple * NULLs in arguments. */ ret = rar5_read_data(a, NULL, NULL, NULL); /* Turn off "skip mode". */ rar->skip_mode--; if(ret < 0 || ret == ARCHIVE_EOF) { /* Propagate any potential error conditions * to the caller. */ return ret; } } } else { /* In standard archives, we can just jump over the compressed * stream. Each file in non-solid archives starts from an empty * window buffer. */ if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { return ARCHIVE_FATAL; } rar->file.bytes_remaining = 0; } return ARCHIVE_OK; } static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, int whence) { (void) a; (void) offset; (void) whence; /* We're a streaming unpacker, and we don't support seeking. */ return ARCHIVE_FATAL; } static int rar5_cleanup(struct archive_read *a) { struct rar5* rar = get_context(a); free(rar->cstate.window_buf); free(rar->cstate.filtered_buf); free(rar->vol.push_buf); free_filters(rar); cdeque_free(&rar->cstate.filters); free(rar); a->format->data = NULL; return ARCHIVE_OK; } static int rar5_capabilities(struct archive_read * a) { (void) a; return 0; } static int rar5_has_encrypted_entries(struct archive_read *_a) { (void) _a; /* Unsupported for now. */ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; } static int rar5_init(struct rar5* rar) { memset(rar, 0, sizeof(struct rar5)); if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) return ARCHIVE_FATAL; return ARCHIVE_OK; } int archive_read_support_format_rar5(struct archive *_a) { struct archive_read* ar; int ret; struct rar5* rar; if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) return ret; rar = malloc(sizeof(*rar)); if(rar == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 data"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != rar5_init(rar)) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 filter buffer"); return ARCHIVE_FATAL; } ret = __archive_read_register_format(ar, rar, "rar5", rar5_bid, rar5_options, rar5_read_header, rar5_read_data, rar5_read_data_skip, rar5_seek_data, rar5_cleanup, rar5_capabilities, rar5_has_encrypted_entries); if(ret != ARCHIVE_OK) { (void) rar5_cleanup(ar); } return ret; } Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_warc.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_warc.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_warc.c (revision 368707) @@ -1,832 +1,848 @@ /*- * Copyright (c) 2014 Sebastian Freundt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /** * WARC is standardised by ISO TC46/SC4/WG12 and currently available as * ISO 28500:2009. * For the purposes of this file we used the final draft from: * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf * * Todo: * [ ] real-world warcs can contain resources at endpoints ending in / * e.g. http://bibnum.bnf.fr/warc/ * if you're lucky their response contains a Content-Location: header * pointing to a unix-compliant filename, in the example above it's * Content-Location: http://bibnum.bnf.fr/warc/index.html * however, that's not mandated and github for example doesn't follow * this convention. * We need a set of archive options to control what to do with * entries like these, at the moment care is taken to skip them. * **/ #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" typedef enum { WT_NONE, /* warcinfo */ WT_INFO, /* metadata */ WT_META, /* resource */ WT_RSRC, /* request, unsupported */ WT_REQ, /* response, unsupported */ WT_RSP, /* revisit, unsupported */ WT_RVIS, /* conversion, unsupported */ WT_CONV, /* continuation, unsupported at the moment */ WT_CONT, /* invalid type */ LAST_WT } warc_type_t; typedef struct { size_t len; const char *str; } warc_string_t; typedef struct { size_t len; char *str; } warc_strbuf_t; struct warc_s { /* content length ahead */ size_t cntlen; /* and how much we've processed so far */ size_t cntoff; /* and how much we need to consume between calls */ size_t unconsumed; /* string pool */ warc_strbuf_t pool; /* previous version */ unsigned int pver; /* stringified format name */ struct archive_string sver; }; static int _warc_bid(struct archive_read *a, int); static int _warc_cleanup(struct archive_read *a); static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*); static int _warc_skip(struct archive_read *a); static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e); /* private routines */ -static unsigned int _warc_rdver(const char buf[10], size_t bsz); +static unsigned int _warc_rdver(const char *buf, size_t bsz); static unsigned int _warc_rdtyp(const char *buf, size_t bsz); static warc_string_t _warc_rduri(const char *buf, size_t bsz); static ssize_t _warc_rdlen(const char *buf, size_t bsz); static time_t _warc_rdrtm(const char *buf, size_t bsz); static time_t _warc_rdmtm(const char *buf, size_t bsz); static const char *_warc_find_eoh(const char *buf, size_t bsz); static const char *_warc_find_eol(const char *buf, size_t bsz); int archive_read_support_format_warc(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct warc_s *w; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_warc"); if ((w = calloc(1, sizeof(*w))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } r = __archive_read_register_format( a, w, "warc", _warc_bid, NULL, _warc_rdhdr, _warc_read, _warc_skip, NULL, _warc_cleanup, NULL, NULL); if (r != ARCHIVE_OK) { free(w); return (r); } return (ARCHIVE_OK); } static int _warc_cleanup(struct archive_read *a) { struct warc_s *w = a->format->data; if (w->pool.len > 0U) { free(w->pool.str); } archive_string_free(&w->sver); free(w); a->format->data = NULL; return (ARCHIVE_OK); } static int _warc_bid(struct archive_read *a, int best_bid) { const char *hdr; ssize_t nrd; unsigned int ver; (void)best_bid; /* UNUSED */ /* check first line of file, it should be a record already */ if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) { /* no idea what to do */ return -1; } else if (nrd < 12) { /* nah, not for us, our magic cookie is at least 12 bytes */ return -1; } /* otherwise snarf the record's version number */ ver = _warc_rdver(hdr, nrd); if (ver < 1200U || ver > 10000U) { /* we only support WARC 0.12 to 1.0 */ return -1; } /* otherwise be confident */ return (64); } static int _warc_rdhdr(struct archive_read *a, struct archive_entry *entry) { #define HDR_PROBE_LEN (12U) struct warc_s *w = a->format->data; unsigned int ver; const char *buf; ssize_t nrd; const char *eoh; /* for the file name, saves some strndup()'ing */ warc_string_t fnam; /* warc record type, not that we really use it a lot */ warc_type_t ftyp; /* content-length+error monad */ ssize_t cntlen; /* record time is the WARC-Date time we reinterpret it as ctime */ time_t rtime; /* mtime is the Last-Modified time which will be the entry's mtime */ time_t mtime; start_over: /* just use read_ahead() they keep track of unconsumed * bits and bobs for us; no need to put an extra shift in * and reproduce that functionality here */ buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd); if (nrd < 0) { /* no good */ archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); return (ARCHIVE_FATAL); } else if (buf == NULL) { /* there should be room for at least WARC/bla\r\n * must be EOF therefore */ return (ARCHIVE_EOF); } /* looks good so far, try and find the end of the header now */ eoh = _warc_find_eoh(buf, nrd); if (eoh == NULL) { /* still no good, the header end might be beyond the * probe we've requested, but then again who'd cram * so much stuff into the header *and* be 28500-compliant */ archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); return (ARCHIVE_FATAL); } ver = _warc_rdver(buf, eoh - buf); /* we currently support WARC 0.12 to 1.0 */ if (ver == 0U) { archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Invalid record version"); return (ARCHIVE_FATAL); } else if (ver < 1200U || ver > 10000U) { archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Unsupported record version: %u.%u", ver / 10000, (ver % 10000) / 100); return (ARCHIVE_FATAL); } cntlen = _warc_rdlen(buf, eoh - buf); if (cntlen < 0) { /* nightmare! the specs say content-length is mandatory * so I don't feel overly bad stopping the reader here */ archive_set_error( &a->archive, EINVAL, "Bad content length"); return (ARCHIVE_FATAL); } rtime = _warc_rdrtm(buf, eoh - buf); if (rtime == (time_t)-1) { /* record time is mandatory as per WARC/1.0, * so just barf here, fast and loud */ archive_set_error( &a->archive, EINVAL, "Bad record time"); return (ARCHIVE_FATAL); } /* let the world know we're a WARC archive */ a->archive.archive_format = ARCHIVE_FORMAT_WARC; if (ver != w->pver) { /* stringify this entry's version */ archive_string_sprintf(&w->sver, "WARC/%u.%u", ver / 10000, (ver % 10000) / 100); /* remember the version */ w->pver = ver; } /* start off with the type */ ftyp = _warc_rdtyp(buf, eoh - buf); /* and let future calls know about the content */ w->cntlen = cntlen; w->cntoff = 0U; mtime = 0;/* Avoid compiling error on some platform. */ switch (ftyp) { case WT_RSRC: case WT_RSP: /* only try and read the filename in the cases that are * guaranteed to have one */ fnam = _warc_rduri(buf, eoh - buf); /* check the last character in the URI to avoid creating * directory endpoints as files, see Todo above */ if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') { /* break here for now */ fnam.len = 0U; fnam.str = NULL; break; } /* bang to our string pool, so we save a * malloc()+free() roundtrip */ if (fnam.len + 1U > w->pool.len) { w->pool.len = ((fnam.len + 64U) / 64U) * 64U; w->pool.str = realloc(w->pool.str, w->pool.len); } memcpy(w->pool.str, fnam.str, fnam.len); w->pool.str[fnam.len] = '\0'; /* let no one else know about the pool, it's a secret, shhh */ fnam.str = w->pool.str; /* snarf mtime or deduce from rtime * this is a custom header added by our writer, it's quite * hard to believe anyone else would go through with it * (apart from being part of some http responses of course) */ if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) { mtime = rtime; } break; + case WT_NONE: + case WT_INFO: + case WT_META: + case WT_REQ: + case WT_RVIS: + case WT_CONV: + case WT_CONT: + case LAST_WT: default: fnam.len = 0U; fnam.str = NULL; break; } /* now eat some of those delicious buffer bits */ __archive_read_consume(a, eoh - buf); switch (ftyp) { case WT_RSRC: case WT_RSP: if (fnam.len > 0U) { /* populate entry object */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_copy_pathname(entry, fnam.str); archive_entry_set_size(entry, cntlen); archive_entry_set_perm(entry, 0644); /* rtime is the new ctime, mtime stays mtime */ archive_entry_set_ctime(entry, rtime, 0L); archive_entry_set_mtime(entry, mtime, 0L); break; } /* FALLTHROUGH */ + case WT_NONE: + case WT_INFO: + case WT_META: + case WT_REQ: + case WT_RVIS: + case WT_CONV: + case WT_CONT: + case LAST_WT: default: /* consume the content and start over */ _warc_skip(a); goto start_over; } return (ARCHIVE_OK); } static int _warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off) { struct warc_s *w = a->format->data; const char *rab; ssize_t nrd; if (w->cntoff >= w->cntlen) { eof: /* it's our lucky day, no work, we can leave early */ *buf = NULL; *bsz = 0U; *off = w->cntoff + 4U/*for \r\n\r\n separator*/; w->unconsumed = 0U; return (ARCHIVE_EOF); } if (w->unconsumed) { __archive_read_consume(a, w->unconsumed); w->unconsumed = 0U; } rab = __archive_read_ahead(a, 1U, &nrd); if (nrd < 0) { *bsz = 0U; /* big catastrophe */ return (int)nrd; } else if (nrd == 0) { goto eof; } else if ((size_t)nrd > w->cntlen - w->cntoff) { /* clamp to content-length */ nrd = w->cntlen - w->cntoff; } *off = w->cntoff; *bsz = nrd; *buf = rab; w->cntoff += nrd; w->unconsumed = (size_t)nrd; return (ARCHIVE_OK); } static int _warc_skip(struct archive_read *a) { struct warc_s *w = a->format->data; __archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/); w->cntlen = 0U; w->cntoff = 0U; return (ARCHIVE_OK); } /* private routines */ static void* deconst(const void *c) { - return (char *)0x1 + (((const char *)c) - (const char *)0x1); + return (void *)(uintptr_t)c; } static char* xmemmem(const char *hay, const size_t haysize, const char *needle, const size_t needlesize) { const char *const eoh = hay + haysize; const char *const eon = needle + needlesize; const char *hp; const char *np; const char *cand; unsigned int hsum; unsigned int nsum; unsigned int eqp; /* trivial checks first * a 0-sized needle is defined to be found anywhere in haystack * then run strchr() to find a candidate in HAYSTACK (i.e. a portion * that happens to begin with *NEEDLE) */ if (needlesize == 0UL) { return deconst(hay); } else if ((hay = memchr(hay, *needle, haysize)) == NULL) { /* trivial */ return NULL; } /* First characters of haystack and needle are the same now. Both are * guaranteed to be at least one character long. Now computes the sum * of characters values of needle together with the sum of the first * needle_len characters of haystack. */ for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U; hp < eoh && np < eon; hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++); /* HP now references the (NEEDLESIZE + 1)-th character. */ if (np < eon) { /* haystack is smaller than needle, :O */ return NULL; } else if (eqp) { /* found a match */ return deconst(hay); } /* now loop through the rest of haystack, * updating the sum iteratively */ for (cand = hay; hp < eoh; hp++) { hsum ^= *cand++; hsum ^= *hp; /* Since the sum of the characters is already known to be * equal at that point, it is enough to check just NEEDLESIZE - 1 * characters for equality, * also CAND is by design < HP, so no need for range checks */ if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) { return deconst(cand); } } return NULL; } static int strtoi_lim(const char *str, const char **ep, int llim, int ulim) { int res = 0; const char *sp; /* we keep track of the number of digits via rulim */ int rulim; for (sp = str, rulim = ulim > 10 ? ulim : 10; res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9'; sp++, rulim /= 10) { res *= 10; res += *sp - '0'; } if (sp == str) { res = -1; } else if (res < llim || res > ulim) { res = -2; } *ep = (const char*)sp; return res; } static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); #elif HAVE__MKGMTIME64 return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ if (mktime(t) == (time_t)-1) return ((time_t)-1); /* Then we can compute timegm() from first principles. */ return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000 + ((t->tm_year - 69) / 4) * 86400 - ((t->tm_year - 1) / 100) * 86400 + ((t->tm_year + 299) / 400) * 86400); #endif } static time_t xstrpisotime(const char *s, char **endptr) { /** like strptime() but strictly for ISO 8601 Zulu strings */ struct tm tm; time_t res = (time_t)-1; /* make sure tm is clean */ memset(&tm, 0, sizeof(tm)); /* as a courtesy to our callers, and since this is a non-standard * routine, we skip leading whitespace */ while (*s == ' ' || *s == '\t') ++s; /* read year */ if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') { goto out; } /* read month */ if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') { goto out; } /* read day-of-month */ if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') { goto out; } /* read hour */ if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') { goto out; } /* read minute */ if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') { goto out; } /* read second */ if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') { goto out; } /* massage TM to fulfill some of POSIX' constraints */ tm.tm_year -= 1900; tm.tm_mon--; /* now convert our custom tm struct to a unix stamp using UTC */ res = time_from_tm(&tm); out: if (endptr != NULL) { *endptr = deconst(s); } return res; } static unsigned int _warc_rdver(const char *buf, size_t bsz) { static const char magic[] = "WARC/"; const char *c; unsigned int ver = 0U; unsigned int end = 0U; if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) { /* buffer too small or invalid magic */ return ver; } /* looks good so far, read the version number for a laugh */ buf += sizeof(magic) - 1U; if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') && isdigit((unsigned char)buf[2U])) { /* we support a maximum of 2 digits in the minor version */ if (isdigit((unsigned char)buf[3U])) end = 1U; /* set up major version */ ver = (buf[0U] - '0') * 10000U; /* set up minor version */ if (end == 1U) { ver += (buf[2U] - '0') * 1000U; ver += (buf[3U] - '0') * 100U; } else ver += (buf[2U] - '0') * 100U; /* * WARC below version 0.12 has a space-separated header * WARC 0.12 and above terminates the version with a CRLF */ c = buf + 3U + end; if (ver >= 1200U) { if (memcmp(c, "\r\n", 2U) != 0) ver = 0U; } else { /* ver < 1200U */ if (*c != ' ' && *c != '\t') ver = 0U; } } return ver; } static unsigned int _warc_rdtyp(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Type:"; const char *val, *eol; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return WT_NONE; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { /* no end of line */ return WT_NONE; } /* overread whitespace */ while (val < eol && (*val == ' ' || *val == '\t')) ++val; if (val + 8U == eol) { if (memcmp(val, "resource", 8U) == 0) return WT_RSRC; else if (memcmp(val, "response", 8U) == 0) return WT_RSP; } return WT_NONE; } static warc_string_t _warc_rduri(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Target-URI:"; const char *val, *uri, *eol, *p; warc_string_t res = {0U, NULL}; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return res; } /* overread whitespace */ val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { /* no end of line */ return res; } while (val < eol && (*val == ' ' || *val == '\t')) ++val; /* overread URL designators */ if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) { /* not touching that! */ return res; } /* spaces inside uri are not allowed, CRLF should follow */ for (p = val; p < eol; p++) { if (isspace((unsigned char)*p)) return res; } /* there must be at least space for ftp */ if (uri < (val + 3U)) return res; /* move uri to point to after :// */ uri += 3U; /* now then, inspect the URI */ if (memcmp(val, "file", 4U) == 0) { /* perfect, nothing left to do here */ } else if (memcmp(val, "http", 4U) == 0 || memcmp(val, "ftp", 3U) == 0) { /* overread domain, and the first / */ while (uri < eol && *uri++ != '/'); } else { /* not sure what to do? best to bugger off */ return res; } res.str = uri; res.len = eol - uri; return res; } static ssize_t _warc_rdlen(const char *buf, size_t bsz) { static const char _key[] = "\r\nContent-Length:"; const char *val, *eol; char *on = NULL; long int len; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return -1; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { /* no end of line */ return -1; } /* skip leading whitespace */ while (val < eol && (*val == ' ' || *val == '\t')) val++; /* there must be at least one digit */ if (!isdigit((unsigned char)*val)) return -1; errno = 0; len = strtol(val, &on, 10); if (errno != 0 || on != eol) { /* line must end here */ return -1; } return (size_t)len; } static time_t _warc_rdrtm(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Date:"; const char *val, *eol; char *on = NULL; time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return (time_t)-1; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) { /* no end of line */ return -1; } /* xstrpisotime() kindly overreads whitespace for us, so use that */ res = xstrpisotime(val, &on); if (on != eol) { /* line must end here */ return -1; } return res; } static time_t _warc_rdmtm(const char *buf, size_t bsz) { static const char _key[] = "\r\nLast-Modified:"; const char *val, *eol; char *on = NULL; time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* no bother */ return (time_t)-1; } val += sizeof(_key) - 1U; if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) { /* no end of line */ return -1; } /* xstrpisotime() kindly overreads whitespace for us, so use that */ res = xstrpisotime(val, &on); if (on != eol) { /* line must end here */ return -1; } return res; } static const char* _warc_find_eoh(const char *buf, size_t bsz) { static const char _marker[] = "\r\n\r\n"; const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U); if (hit != NULL) { hit += sizeof(_marker) - 1U; } return hit; } static const char* _warc_find_eol(const char *buf, size_t bsz) { static const char _marker[] = "\r\n"; const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U); return hit; } /* archive_read_support_format_warc.c ends here */ Index: stable/11/contrib/libarchive/libarchive/archive_read_support_format_zip.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 368707) @@ -1,4044 +1,4165 @@ /*- * Copyright (c) 2004-2013 Tim Kientzle * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2013 Konrad Kleine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* * The definitive documentation of the Zip file format is: * http://www.pkware.com/documents/casestudies/APPNOTE.TXT * * The Info-Zip project has pioneered various extensions to better * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855 * "Ux", and 0x7875 "ux" extensions for time and ownership * information. * * History of this code: The streaming Zip reader was first added to * libarchive in January 2005. Support for seekable input sources was * added in Nov 2011. Zip64 support (including a significant code * refactoring) was added in 2014. */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #include "archive.h" #include "archive_digest_private.h" #include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_hmac_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" #include "archive_ppmd8_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif struct zip_entry { struct archive_rb_node node; struct zip_entry *next; int64_t local_header_offset; int64_t compressed_size; int64_t uncompressed_size; int64_t gid; int64_t uid; struct archive_string rsrcname; time_t mtime; time_t atime; time_t ctime; uint32_t crc32; uint16_t mode; uint16_t zip_flags; /* From GP Flags Field */ unsigned char compression; unsigned char system; /* From "version written by" */ unsigned char flags; /* Our extra markers. */ unsigned char decdat;/* Used for Decryption check */ /* WinZip AES encryption extra field should be available * when compression is 99. */ struct { /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */ unsigned vendor; #define AES_VENDOR_AE_1 0x0001 #define AES_VENDOR_AE_2 0x0002 /* AES encryption strength: * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */ unsigned strength; /* Actual compression method. */ unsigned char compression; } aes_extra; }; struct trad_enc_ctx { uint32_t keys[3]; }; /* Bits used in zip_flags. */ #define ZIP_ENCRYPTED (1 << 0) #define ZIP_LENGTH_AT_END (1 << 3) #define ZIP_STRONG_ENCRYPTED (1 << 6) #define ZIP_UTF8_NAME (1 << 11) /* See "7.2 Single Password Symmetric Encryption Method" in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ #define ZIP_CENTRAL_DIRECTORY_ENCRYPTED (1 << 13) /* Bits used in flags. */ #define LA_USED_ZIP64 (1 << 0) #define LA_FROM_CENTRAL_DIRECTORY (1 << 1) /* * See "WinZip - AES Encryption Information" * http://www.winzip.com/aes_info.htm */ /* Value used in compression method. */ #define WINZIP_AES_ENCRYPTION 99 /* Authentication code size. */ #define AUTH_CODE_SIZE 10 /**/ #define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) struct zip { /* Structural information about the archive. */ struct archive_string format_name; int64_t central_directory_offset; size_t central_directory_entries_total; size_t central_directory_entries_on_this_disk; int has_encrypted_entries; /* List of entries (seekable Zip only) */ struct zip_entry *zip_entries; struct archive_rb_tree tree; struct archive_rb_tree tree_rsrc; /* Bytes read but not yet consumed via __archive_read_consume() */ size_t unconsumed; /* Information about entry we're currently reading. */ struct zip_entry *entry; int64_t entry_bytes_remaining; /* These count the number of bytes actually read for the entry. */ int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; unsigned long (*crc32func)(unsigned long, const void *, size_t); char ignore_crc32; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; #ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif #if HAVE_LZMA_H && HAVE_LIBLZMA lzma_stream zipx_lzma_stream; char zipx_lzma_valid; #endif #ifdef HAVE_BZLIB_H bz_stream bzstream; char bzstream_valid; #endif IByteIn zipx_ppmd_stream; ssize_t zipx_ppmd_read_compressed; CPpmd8 ppmd8; char ppmd8_valid; char ppmd8_stream_failed; struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; int init_default_conversion; int process_mac_extensions; char init_decryption; /* Decryption buffer. */ /* * The decrypted data starts at decrypted_ptr and * extends for decrypted_bytes_remaining. Decryption * adds new data to the end of this block, data is returned * to clients from the beginning. When the block hits the * end of decrypted_buffer, it has to be shuffled back to * the beginning of the buffer. */ unsigned char *decrypted_buffer; unsigned char *decrypted_ptr; size_t decrypted_buffer_size; size_t decrypted_bytes_remaining; size_t decrypted_unconsumed_bytes; /* Traditional PKWARE decryption. */ struct trad_enc_ctx tctx; char tctx_valid; /* WinZip AES decryption. */ /* Contexts used for AES decryption. */ archive_crypto_ctx cctx; char cctx_valid; archive_hmac_sha1_ctx hctx; char hctx_valid; /* Strong encryption's decryption header information. */ unsigned iv_size; unsigned alg_id; unsigned bit_len; unsigned flags; unsigned erd_size; unsigned v_size; unsigned v_crc32; uint8_t *iv; uint8_t *erd; uint8_t *v_data; }; /* Many systems define min or MIN, but not all. */ #define zipmin(a,b) ((a) < (b) ? (a) : (b)) /* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8 * streams inside ZIP files. It has 2 purposes: one is to fetch the next * compressed byte from the stream, second one is to increase the counter how * many compressed bytes were read. */ static Byte ppmd_read(void* p) { /* Get the handle to current decompression context. */ struct archive_read *a = ((IByteIn*)p)->a; struct zip *zip = (struct zip*) a->format->data; ssize_t bytes_avail = 0; /* Fetch next byte. */ const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail); if(bytes_avail < 1) { zip->ppmd8_stream_failed = 1; return 0; } __archive_read_consume(a, 1); /* Increment the counter. */ ++zip->zipx_ppmd_read_compressed; /* Return the next compressed byte. */ return data[0]; } /* ------------------------------------------------------------------------ */ /* Traditional PKWARE Decryption functions. */ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; #define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; t = (ctx->keys[1] >> 24) & 0xff; ctx->keys[2] = CRC32(ctx->keys[2], t); #undef CRC32 } static uint8_t trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; } static void trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { unsigned i, max; max = (unsigned)((in_len < out_len)? in_len: out_len); for (i = 0; i < max; i++) { uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx); out[i] = t; trad_enc_update_keys(ctx, t); } } static int trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len, const uint8_t *key, size_t key_len, uint8_t *crcchk) { uint8_t header[12]; if (key_len < 12) { *crcchk = 0xff; return -1; } ctx->keys[0] = 305419896L; ctx->keys[1] = 591751049L; ctx->keys[2] = 878082192L; for (;pw_len; --pw_len) trad_enc_update_keys(ctx, *pw++); trad_enc_decrypt_update(ctx, key, 12, header, 12); /* Return the last byte for CRC check. */ *crcchk = header[11]; return 0; } #if 0 static void crypt_derive_key_sha1(const void *p, int size, unsigned char *key, int key_size) { #define MD_SIZE 20 archive_sha1_ctx ctx; unsigned char md1[MD_SIZE]; unsigned char md2[MD_SIZE * 2]; unsigned char mkb[64]; int i; archive_sha1_init(&ctx); archive_sha1_update(&ctx, p, size); archive_sha1_final(&ctx, md1); memset(mkb, 0x36, sizeof(mkb)); for (i = 0; i < MD_SIZE; i++) mkb[i] ^= md1[i]; archive_sha1_init(&ctx); archive_sha1_update(&ctx, mkb, sizeof(mkb)); archive_sha1_final(&ctx, md2); memset(mkb, 0x5C, sizeof(mkb)); for (i = 0; i < MD_SIZE; i++) mkb[i] ^= md1[i]; archive_sha1_init(&ctx); archive_sha1_update(&ctx, mkb, sizeof(mkb)); archive_sha1_final(&ctx, md2 + MD_SIZE); if (key_size > 32) key_size = 32; memcpy(key, md2, key_size); #undef MD_SIZE } #endif /* * Common code for streaming or seeking modes. * * Includes code to read local file headers, decompress data * from entry bodies, and common API. */ static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { return crc32(crc, buff, (unsigned int)len); } /* Used by "ignorecrc32" option to speed up tests. */ static unsigned long fake_crc32(unsigned long crc, const void *buff, size_t len) { (void)crc; /* UNUSED */ (void)buff; /* UNUSED */ (void)len; /* UNUSED */ return 0; } static const struct { int id; const char * name; } compression_methods[] = { {0, "uncompressed"}, /* The file is stored (no compression) */ {1, "shrinking"}, /* The file is Shrunk */ {2, "reduced-1"}, /* The file is Reduced with compression factor 1 */ {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */ {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */ {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */ {6, "imploded"}, /* The file is Imploded */ {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */ {8, "deflation"}, /* The file is Deflated */ {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */ {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding * (old IBM TERSE) */ {11, "reserved"}, /* Reserved by PKWARE */ {12, "bzip"}, /* File is compressed using BZIP2 algorithm */ {13, "reserved"}, /* Reserved by PKWARE */ {14, "lzma"}, /* LZMA (EFS) */ {15, "reserved"}, /* Reserved by PKWARE */ {16, "reserved"}, /* Reserved by PKWARE */ {17, "reserved"}, /* Reserved by PKWARE */ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ {95, "xz"}, /* XZ compressed data */ {96, "jpeg"}, /* JPEG compressed data */ {97, "wav-pack"}, /* WavPack compressed data */ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ {99, "aes"} /* WinZip AES encryption */ }; static const char * compression_name(const int compression) { static const int num_compression_methods = sizeof(compression_methods)/sizeof(compression_methods[0]); int i=0; while(compression >= 0 && i < num_compression_methods) { if (compression_methods[i].id == compression) return compression_methods[i].name; i++; } return "??"; } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t zip_time(const char *p) { int msTime, msDate; struct tm ts; msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]); msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]); memset(&ts, 0, sizeof(ts)); ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ ts.tm_mday = msDate & 0x1f; /* Day of month. */ ts.tm_hour = (msTime >> 11) & 0x1f; ts.tm_min = (msTime >> 5) & 0x3f; ts.tm_sec = (msTime << 1) & 0x3e; ts.tm_isdst = -1; return mktime(&ts); } /* * The extra data is stored as a list of * id1+size1+data1 + id2+size2+data2 ... * triplets. id and size are 2 bytes each. */ static int process_extra(struct archive_read *a, struct archive_entry *entry, const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; struct zip *zip = (struct zip *)(a->format->data); if (extra_length == 0) { return ARCHIVE_OK; } if (extra_length < 4) { size_t i = 0; /* Some ZIP files may have trailing 0 bytes. Let's check they * are all 0 and ignore them instead of returning an error. * * This is not technically correct, but some ZIP files look * like this and other tools support those files - so let's * also support them. */ for (; i < extra_length; i++) { if (p[i] != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Too-small extra data: " "Need at least 4 bytes, " "but only found %d bytes", (int)extra_length); return ARCHIVE_FAILED; } } return ARCHIVE_OK; } while (offset <= extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > extra_length) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: " "Need %d bytes but only found %d bytes", (int)datasize, (int)(extra_length - offset)); return ARCHIVE_FAILED; } #ifdef DEBUG fprintf(stderr, "Header id 0x%04x, length %d\n", headerid, datasize); #endif switch (headerid) { case 0x0001: /* Zip64 extended information extra field. */ zip_entry->flags |= LA_USED_ZIP64; if (zip_entry->uncompressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit " "uncompressed size"); return ARCHIVE_FAILED; } zip_entry->uncompressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->compressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit " "compressed size"); return ARCHIVE_FAILED; } zip_entry->compressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->local_header_offset == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit " "local header offset"); return ARCHIVE_FAILED; } zip_entry->local_header_offset = t; offset += 8; datasize -= 8; } /* archive_le32dec(p + offset) gives disk * on which file starts, but we don't handle * multi-volume Zip files. */ break; #ifdef DEBUG case 0x0017: { /* Strong encryption field. */ if (archive_le16dec(p + offset) == 2) { unsigned algId = archive_le16dec(p + offset + 2); unsigned bitLen = archive_le16dec(p + offset + 4); int flags = archive_le16dec(p + offset + 6); fprintf(stderr, "algId=0x%04x, bitLen=%u, " "flgas=%d\n", algId, bitLen,flags); } break; } #endif case 0x5455: { /* Extended time field "UT". */ int flags; if (datasize == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete extended time field"); return ARCHIVE_FAILED; } flags = p[offset]; offset++; datasize--; /* Flag bits indicate which dates are present. */ if (flags & 0x01) { #ifdef DEBUG fprintf(stderr, "mtime: %lld -> %d\n", (long long)zip_entry->mtime, archive_le32dec(p + offset)); #endif if (datasize < 4) break; zip_entry->mtime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x02) { if (datasize < 4) break; zip_entry->atime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x04) { if (datasize < 4) break; zip_entry->ctime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } break; } case 0x5855: { /* Info-ZIP Unix Extra Field (old version) "UX". */ if (datasize >= 8) { zip_entry->atime = archive_le32dec(p + offset); zip_entry->mtime = archive_le32dec(p + offset + 4); } if (datasize >= 12) { zip_entry->uid = archive_le16dec(p + offset + 8); zip_entry->gid = archive_le16dec(p + offset + 10); } break; } case 0x6c78: { /* Experimental 'xl' field */ /* * Introduced Dec 2013 to provide a way to * include external file attributes (and other * fields that ordinarily appear only in * central directory) in local file header. * This provides file type and permission * information necessary to support full * streaming extraction. Currently being * discussed with other Zip developers * ... subject to change. * * Format: * The field starts with a bitmap that specifies * which additional fields are included. The * bitmap is variable length and can be extended in * the future. * * n bytes - feature bitmap: first byte has low-order * 7 bits. If high-order bit is set, a subsequent * byte holds the next 7 bits, etc. * * if bitmap & 1, 2 byte "version made by" * if bitmap & 2, 2 byte "internal file attributes" * if bitmap & 4, 4 byte "external file attributes" * if bitmap & 8, 2 byte comment length + n byte * comment */ int bitmap, bitmap_last; if (datasize < 1) break; bitmap_last = bitmap = 0xff & p[offset]; offset += 1; datasize -= 1; /* We only support first 7 bits of bitmap; skip rest. */ while ((bitmap_last & 0x80) != 0 && datasize >= 1) { bitmap_last = p[offset]; offset += 1; datasize -= 1; } if (bitmap & 1) { /* 2 byte "version made by" */ if (datasize < 2) break; zip_entry->system = archive_le16dec(p + offset) >> 8; offset += 2; datasize -= 2; } if (bitmap & 2) { /* 2 byte "internal file attributes" */ uint32_t internal_attributes; if (datasize < 2) break; internal_attributes = archive_le16dec(p + offset); /* Not used by libarchive at present. */ (void)internal_attributes; /* UNUSED */ offset += 2; datasize -= 2; } if (bitmap & 4) { /* 4 byte "external file attributes" */ uint32_t external_attributes; if (datasize < 4) break; external_attributes = archive_le32dec(p + offset); if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit if (0x10 == (external_attributes & 0x10)) { zip_entry->mode = AE_IFDIR | 0775; } else { zip_entry->mode = AE_IFREG | 0664; } if (0x01 == (external_attributes & 0x01)) { /* Read-only bit; * strip write permissions */ zip_entry->mode &= 0555; } } else { zip_entry->mode = 0; } offset += 4; datasize -= 4; } if (bitmap & 8) { /* 2 byte comment length + comment */ uint32_t comment_length; if (datasize < 2) break; comment_length = archive_le16dec(p + offset); offset += 2; datasize -= 2; if (datasize < comment_length) break; /* Comment is not supported by libarchive */ offset += comment_length; datasize -= comment_length; } break; } case 0x7075: { /* Info-ZIP Unicode Path Extra Field. */ if (datasize < 5 || entry == NULL) break; offset += 5; datasize -= 5; /* The path name in this field is always encoded * in UTF-8. */ if (zip->sconv_utf8 == NULL) { zip->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); /* If the converter from UTF-8 is not * available, then the path name from the main * field will more likely be correct. */ if (zip->sconv_utf8 == NULL) break; } /* Make sure the CRC32 of the filename matches. */ if (!zip->ignore_crc32) { const char *cp = archive_entry_pathname(entry); if (cp) { unsigned long file_crc = zip->crc32func(0, cp, strlen(cp)); unsigned long utf_crc = archive_le32dec(p + offset - 4); if (file_crc != utf_crc) { #ifdef DEBUG fprintf(stderr, "CRC filename mismatch; " "CDE is %lx, but UTF8 " "is outdated with %lx\n", file_crc, utf_crc); #endif break; } } } if (archive_entry_copy_pathname_l(entry, p + offset, datasize, zip->sconv_utf8) != 0) { /* Ignore the error, and fallback to the path * name from the main field. */ #ifdef DEBUG fprintf(stderr, "Failed to read the ZIP " "0x7075 extra field path.\n"); #endif } break; } case 0x7855: /* Info-ZIP Unix Extra Field (type 2) "Ux". */ #ifdef DEBUG fprintf(stderr, "uid %d gid %d\n", archive_le16dec(p + offset), archive_le16dec(p + offset + 2)); #endif if (datasize >= 2) zip_entry->uid = archive_le16dec(p + offset); if (datasize >= 4) zip_entry->gid = archive_le16dec(p + offset + 2); break; case 0x7875: { /* Info-Zip Unix Extra Field (type 3) "ux". */ int uidsize = 0, gidsize = 0; /* TODO: support arbitrary uidsize/gidsize. */ if (datasize >= 1 && p[offset] == 1) {/* version=1 */ if (datasize >= 4) { /* get a uid size. */ uidsize = 0xff & (int)p[offset+1]; if (uidsize == 2) zip_entry->uid = archive_le16dec( p + offset + 2); else if (uidsize == 4 && datasize >= 6) zip_entry->uid = archive_le32dec( p + offset + 2); } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ gidsize = 0xff & (int)p[offset+2+uidsize]; if (gidsize == 2) zip_entry->gid = archive_le16dec( p+offset+2+uidsize+1); else if (gidsize == 4 && datasize >= (2 + uidsize + 5)) zip_entry->gid = archive_le32dec( p+offset+2+uidsize+1); } } break; } case 0x9901: /* WinZip AES extra data field. */ if (datasize < 6) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete AES field"); return ARCHIVE_FAILED; } if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { /* Vendor version. */ zip_entry->aes_extra.vendor = archive_le16dec(p + offset); /* AES encryption strength. */ zip_entry->aes_extra.strength = p[offset + 4]; /* Actual compression method. */ zip_entry->aes_extra.compression = p[offset + 5]; } break; default: break; } offset += datasize; } return ARCHIVE_OK; } +#if HAVE_LZMA_H && HAVE_LIBLZMA /* + * Auxiliary function to uncompress data chunk from zipx archive + * (zip with lzma compression). + */ +static int +zipx_lzma_uncompress_buffer(const char *compressed_buffer, + size_t compressed_buffer_size, + char *uncompressed_buffer, + size_t uncompressed_buffer_size) +{ + int status = ARCHIVE_FATAL; + // length of 'lzma properties data' in lzma compressed + // data segment (stream) inside zip archive + const size_t lzma_params_length = 5; + // offset of 'lzma properties data' from the beginning of lzma stream + const size_t lzma_params_offset = 4; + // end position of 'lzma properties data' in lzma stream + const size_t lzma_params_end = lzma_params_offset + lzma_params_length; + if (compressed_buffer == NULL || + compressed_buffer_size < lzma_params_end || + uncompressed_buffer == NULL) + return status; + + // prepare header for lzma_alone_decoder to replace zipx header + // (see comments in 'zipx_lzma_alone_init' for justification) +#pragma pack(push) +#pragma pack(1) + struct _alone_header + { + uint8_t bytes[5]; // lzma_params_length + uint64_t uncompressed_size; + } alone_header; +#pragma pack(pop) + // copy 'lzma properties data' blob + memcpy(&alone_header.bytes[0], compressed_buffer + lzma_params_offset, + lzma_params_length); + alone_header.uncompressed_size = UINT64_MAX; + + // prepare new compressed buffer, see 'zipx_lzma_alone_init' for details + const size_t lzma_alone_buffer_size = + compressed_buffer_size - lzma_params_end + sizeof(alone_header); + unsigned char *lzma_alone_compressed_buffer = + (unsigned char*) malloc(lzma_alone_buffer_size); + if (lzma_alone_compressed_buffer == NULL) + return status; + // copy lzma_alone header into new buffer + memcpy(lzma_alone_compressed_buffer, (void*) &alone_header, + sizeof(alone_header)); + // copy compressed data into new buffer + memcpy(lzma_alone_compressed_buffer + sizeof(alone_header), + compressed_buffer + lzma_params_end, + compressed_buffer_size - lzma_params_end); + + // create and fill in lzma_alone_decoder stream + lzma_stream stream = LZMA_STREAM_INIT; + lzma_ret ret = lzma_alone_decoder(&stream, UINT64_MAX); + if (ret == LZMA_OK) + { + stream.next_in = lzma_alone_compressed_buffer; + stream.avail_in = lzma_alone_buffer_size; + stream.total_in = 0; + stream.next_out = (unsigned char*)uncompressed_buffer; + stream.avail_out = uncompressed_buffer_size; + stream.total_out = 0; + ret = lzma_code(&stream, LZMA_RUN); + if (ret == LZMA_OK || ret == LZMA_STREAM_END) + status = ARCHIVE_OK; + } + lzma_end(&stream); + free(lzma_alone_compressed_buffer); + return status; +} +#endif + +/* * Assumes file pointer is at beginning of local file header. */ static int zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const char *p; const void *h; const wchar_t *wp; const char *cp; size_t len, filename_length, extra_length; struct archive_string_conv *sconv; struct zip_entry *zip_entry = zip->entry; struct zip_entry zip_entry_central_dir; int ret = ARCHIVE_OK; char version; /* Save a copy of the original for consistency checks. */ zip_entry_central_dir = *zip_entry; zip->decompress_init = 0; zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); /* Setup default conversion. */ if (zip->sconv == NULL && !zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_read(&(a->archive)); zip->init_default_conversion = 1; } if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_FATAL; } version = p[4]; zip_entry->system = p[5]; zip_entry->zip_flags = archive_le16dec(p + 6); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { zip->has_encrypted_entries = 1; archive_entry_set_is_data_encrypted(entry, 1); if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED && zip_entry->zip_flags & ZIP_ENCRYPTED && zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) { archive_entry_set_is_metadata_encrypted(entry, 1); return ARCHIVE_FATAL; } } zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); zip_entry->mtime = zip_time(p + 10); zip_entry->crc32 = archive_le32dec(p + 14); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[11]; else zip_entry->decdat = p[17]; zip_entry->compressed_size = archive_le32dec(p + 18); zip_entry->uncompressed_size = archive_le32dec(p + 22); filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); __archive_read_consume(a, 30); /* Read the filename. */ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (zip_entry->zip_flags & ZIP_UTF8_NAME) { /* The filename is stored to be UTF-8. */ if (zip->sconv_utf8 == NULL) { zip->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); if (zip->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = zip->sconv_utf8; } else if (zip->sconv != NULL) sconv = zip->sconv; else sconv = zip->sconv_default; if (archive_entry_copy_pathname_l(entry, h, filename_length, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } __archive_read_consume(a, filename_length); /* Read the extra data. */ if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (ARCHIVE_OK != process_extra(a, entry, h, extra_length, zip_entry)) { return ARCHIVE_FATAL; } __archive_read_consume(a, extra_length); /* Work around a bug in Info-Zip: When reading from a pipe, it * stats the pipe instead of synthesizing a file entry. */ if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) { zip_entry->mode &= ~ AE_IFMT; zip_entry->mode |= AE_IFREG; } /* If the mode is totally empty, set some sane default. */ if (zip_entry->mode == 0) { zip_entry->mode |= 0664; } /* Windows archivers sometimes use backslash as the directory * separator. Normalize to slash. */ if (zip_entry->system == 0 && (wp = archive_entry_pathname_w(entry)) != NULL) { if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) { size_t i; struct archive_wstring s; archive_string_init(&s); archive_wstrcpy(&s, wp); for (i = 0; i < archive_strlen(&s); i++) { if (s.s[i] == '\\') s.s[i] = '/'; } archive_entry_copy_pathname_w(entry, s.s); archive_wstring_free(&s); } } /* Make sure that entries with a trailing '/' are marked as directories * even if the External File Attributes contains bogus values. If this * is not a directory and there is no type, assume a regular file. */ if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) { int has_slash; wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); has_slash = len > 0 && wp[len - 1] == L'/'; } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; has_slash = len > 0 && cp[len - 1] == '/'; } /* Correct file type as needed. */ if (has_slash) { zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFDIR; zip_entry->mode |= 0111; } else if ((zip_entry->mode & AE_IFMT) == 0) { zip_entry->mode |= AE_IFREG; } } /* Make sure directories end in '/' */ if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) { wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); if (len > 0 && wp[len - 1] != L'/') { struct archive_wstring s; archive_string_init(&s); archive_wstrcat(&s, wp); archive_wstrappend_wchar(&s, L'/'); archive_entry_copy_pathname_w(entry, s.s); archive_wstring_free(&s); } } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; if (len > 0 && cp[len - 1] != '/') { struct archive_string s; archive_string_init(&s); archive_strcat(&s, cp); archive_strappend_char(&s, '/'); archive_entry_set_pathname(entry, s.s); archive_string_free(&s); } } } if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) { /* If this came from the central dir, its size info * is definitive, so ignore the length-at-end flag. */ zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END; /* If local header is missing a value, use the one from the central directory. If both have it, warn about mismatches. */ if (zip_entry->crc32 == 0) { zip_entry->crc32 = zip_entry_central_dir.crc32; } else if (!zip->ignore_crc32 && zip_entry->crc32 != zip_entry_central_dir.crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent CRC32 values"); ret = ARCHIVE_WARN; } if (zip_entry->compressed_size == 0) { zip_entry->compressed_size = zip_entry_central_dir.compressed_size; } else if (zip_entry->compressed_size != zip_entry_central_dir.compressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent compressed size: " "%jd in central directory, %jd in local header", (intmax_t)zip_entry_central_dir.compressed_size, (intmax_t)zip_entry->compressed_size); ret = ARCHIVE_WARN; } if (zip_entry->uncompressed_size == 0) { zip_entry->uncompressed_size = zip_entry_central_dir.uncompressed_size; } else if (zip_entry->uncompressed_size != zip_entry_central_dir.uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent uncompressed size: " "%jd in central directory, %jd in local header", (intmax_t)zip_entry_central_dir.uncompressed_size, (intmax_t)zip_entry->uncompressed_size); ret = ARCHIVE_WARN; } } /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); archive_entry_set_uid(entry, zip_entry->uid); archive_entry_set_gid(entry, zip_entry->gid); archive_entry_set_mtime(entry, zip_entry->mtime, 0); archive_entry_set_ctime(entry, zip_entry->ctime, 0); archive_entry_set_atime(entry, zip_entry->atime, 0); if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { size_t linkname_length; if (zip_entry->compressed_size > 64 * 1024) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zip file with oversized link entry"); return ARCHIVE_FATAL; } linkname_length = (size_t)zip_entry->compressed_size; archive_entry_set_size(entry, 0); p = __archive_read_ahead(a, linkname_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated Zip file"); return ARCHIVE_FATAL; } + // take into account link compression if any + size_t linkname_full_length = linkname_length; + if (zip->entry->compression != 0) + { + // symlink target string appeared to be compressed + int status = ARCHIVE_FATAL; + char *uncompressed_buffer = + (char*) malloc(zip_entry->uncompressed_size); + if (uncompressed_buffer == NULL) + { + archive_set_error(&a->archive, ENOMEM, + "No memory for lzma decompression"); + return status; + } + switch (zip->entry->compression) + { +#if HAVE_LZMA_H && HAVE_LIBLZMA + case 14: /* ZIPx LZMA compression. */ + /*(see zip file format specification, section 4.4.5)*/ + status = zipx_lzma_uncompress_buffer(p, + linkname_length, + uncompressed_buffer, + (size_t)zip_entry->uncompressed_size); + break; +#endif + default: /* Unsupported compression. */ + break; + } + if (status == ARCHIVE_OK) + { + p = uncompressed_buffer; + linkname_full_length = + (size_t)zip_entry->uncompressed_size; + } + else + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported ZIP compression method " + "during decompression of link entry (%d: %s)", + zip->entry->compression, + compression_name(zip->entry->compression)); + return ARCHIVE_FAILED; + } + } + sconv = zip->sconv; if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME)) sconv = zip->sconv_utf8; if (sconv == NULL) sconv = zip->sconv_default; - if (archive_entry_copy_symlink_l(entry, p, linkname_length, + if (archive_entry_copy_symlink_l(entry, p, linkname_full_length, sconv) != 0) { if (errno != ENOMEM && sconv == zip->sconv_utf8 && (zip->entry->zip_flags & ZIP_UTF8_NAME)) archive_entry_copy_symlink_l(entry, p, - linkname_length, NULL); + linkname_full_length, NULL); if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Symlink"); return (ARCHIVE_FATAL); } /* * Since there is no character-set regulation for * symlink name, do not report the conversion error * in an automatic conversion. */ if (sconv != zip->sconv_utf8 || (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symlink cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name( sconv)); ret = ARCHIVE_WARN; } } zip_entry->uncompressed_size = zip_entry->compressed_size = 0; if (__archive_read_consume(a, linkname_length) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Read error skipping symlink target name"); return ARCHIVE_FATAL; } } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) || zip_entry->uncompressed_size > 0) { /* Set the size only if it's meaningful. */ archive_entry_set_size(entry, zip_entry->uncompressed_size); } zip->entry_bytes_remaining = zip_entry->compressed_size; /* If there's no body, force read_data() to return EOF immediately. */ if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ archive_string_empty(&zip->format_name); archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)", version / 10, version % 10, compression_name(zip->entry->compression)); a->archive.archive_format_name = zip->format_name.s; return (ret); } static int check_authentication_code(struct archive_read *a, const void *_p) { struct zip *zip = (struct zip *)(a->format->data); /* Check authentication code. */ if (zip->hctx_valid) { const void *p; uint8_t hmac[20]; size_t hmac_len = 20; int cmp; archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); if (_p == NULL) { /* Read authentication code. */ p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } } else { p = _p; } cmp = memcmp(hmac, p, AUTH_CODE_SIZE); __archive_read_consume(a, AUTH_CODE_SIZE); if (cmp != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad Authentication code"); return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } /* * Read "uncompressed" data. There are three cases: * 1) We know the size of the data. This is always true for the * seeking reader (we've examined the Central Directory already). * 2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred. * Info-ZIP seems to do this; we know the size but have to grab * the CRC from the data descriptor afterwards. * 3) We're streaming and ZIP_LENGTH_AT_END was specified and * we have no size information. In this case, we can do pretty * well by watching for the data descriptor record. The data * descriptor is 16 bytes and includes a computed CRC that should * provide a strong check. * * TODO: Technically, the PK\007\010 signature is optional. * In the original spec, the data descriptor contained CRC * and size fields but had no leading signature. In practice, * newer writers seem to provide the signature pretty consistently. * * For uncompressed data, the PK\007\010 marker seems essential * to be sure we've actually seen the end of the entry. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * zip->end_of_entry if it consumes all of the data. */ static int zip_read_data_none(struct archive_read *a, const void **_buff, size_t *size, int64_t *offset) { struct zip *zip; const char *buff; ssize_t bytes_avail; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { const char *p; ssize_t grabbing_bytes = 24; if (zip->hctx_valid) grabbing_bytes += AUTH_CODE_SIZE; /* Grab at least 24 bytes. */ buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail); if (bytes_avail < grabbing_bytes) { /* Zip archives have end-of-archive markers that are longer than this, so a failure to get at least 24 bytes really does indicate a truncated file. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } /* Check for a complete PK\007\010 signature, followed * by the correct 4-byte CRC. */ p = buff; if (zip->hctx_valid) p += AUTH_CODE_SIZE; if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' && (archive_le32dec(p + 4) == zip->entry_crc32 || zip->ignore_crc32 || (zip->hctx_valid && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { if (zip->entry->flags & LA_USED_ZIP64) { uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p + 4); compressed = archive_le64dec(p + 8); uncompressed = archive_le64dec(p + 16); if (compressed > INT64_MAX || uncompressed > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); return ARCHIVE_FAILED; } zip->entry->compressed_size = compressed; zip->entry->uncompressed_size = uncompressed; zip->unconsumed = 24; } else { zip->entry->crc32 = archive_le32dec(p + 4); zip->entry->compressed_size = archive_le32dec(p + 8); zip->entry->uncompressed_size = archive_le32dec(p + 12); zip->unconsumed = 16; } if (zip->hctx_valid) { r = check_authentication_code(a, buff); if (r != ARCHIVE_OK) return (r); } zip->end_of_entry = 1; return (ARCHIVE_OK); } /* If not at EOF, ensure we consume at least one byte. */ ++p; /* Scan forward until we see where a PK\007\010 signature * might be. */ /* Return bytes up until that point. On the next call, * the code above will verify the data descriptor. */ while (p < buff + bytes_avail - 4) { if (p[3] == 'P') { p += 3; } else if (p[3] == 'K') { p += 2; } else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->hctx_valid) p -= AUTH_CODE_SIZE; break; } else { p += 4; } } bytes_avail = p - buff; } else { if (zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; if (zip->hctx_valid) { r = check_authentication_code(a, NULL); if (r != ARCHIVE_OK) return (r); } return (ARCHIVE_OK); } /* Grab a bunch of bytes. */ buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = (ssize_t)zip->entry_bytes_remaining; } if (zip->tctx_valid || zip->cctx_valid) { size_t dec_size = bytes_avail; if (dec_size > zip->decrypted_buffer_size) dec_size = zip->decrypted_buffer_size; if (zip->tctx_valid) { trad_enc_decrypt_update(&zip->tctx, (const uint8_t *)buff, dec_size, zip->decrypted_buffer, dec_size); } else { size_t dsize = dec_size; archive_hmac_sha1_update(&zip->hctx, (const uint8_t *)buff, dec_size); archive_decrypto_aes_ctr_update(&zip->cctx, (const uint8_t *)buff, dec_size, zip->decrypted_buffer, &dsize); } bytes_avail = dec_size; buff = (const char *)zip->decrypted_buffer; } *size = bytes_avail; zip->entry_bytes_remaining -= bytes_avail; zip->entry_uncompressed_bytes_read += bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; zip->unconsumed += bytes_avail; *_buff = buff; return (ARCHIVE_OK); } static int consume_optional_marker(struct archive_read *a, struct zip *zip) { if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { const char *p; if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP end-of-file record"); return (ARCHIVE_FATAL); } /* Consume the optional PK\007\010 marker. */ if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010') { p += 4; zip->unconsumed = 4; } if (zip->entry->flags & LA_USED_ZIP64) { uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p); compressed = archive_le64dec(p + 4); uncompressed = archive_le64dec(p + 12); if (compressed > INT64_MAX || uncompressed > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); return ARCHIVE_FAILED; } zip->entry->compressed_size = compressed; zip->entry->uncompressed_size = uncompressed; zip->unconsumed += 20; } else { zip->entry->crc32 = archive_le32dec(p); zip->entry->compressed_size = archive_le32dec(p + 4); zip->entry->uncompressed_size = archive_le32dec(p + 8); zip->unconsumed += 12; } } return (ARCHIVE_OK); } #if HAVE_LZMA_H && HAVE_LIBLZMA static int zipx_xz_init(struct archive_read *a, struct zip *zip) { lzma_ret r; if(zip->zipx_lzma_valid) { lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; } memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0); if (r != LZMA_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "xz initialization failed(%d)", r); return (ARCHIVE_FAILED); } zip->zipx_lzma_valid = 1; free(zip->uncompressed_buffer); zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (uint8_t*) malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for xz decompression"); return (ARCHIVE_FATAL); } zip->decompress_init = 1; return (ARCHIVE_OK); } static int zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) { lzma_ret r; const uint8_t* p; #pragma pack(push) #pragma pack(1) struct _alone_header { uint8_t bytes[5]; uint64_t uncompressed_size; } alone_header; #pragma pack(pop) if(zip->zipx_lzma_valid) { lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; } /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma * that is a part of XZ Utils. The stream format stored inside ZIPX * file is a modified "lzma alone" file format, that was used by the * `lzma` utility which was later deprecated in favour of `xz` utility. * Since those formats are nearly the same, we can use a standard * "lzma alone" decoder from XZ Utils. */ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); if (r != LZMA_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "lzma initialization failed(%d)", r); return (ARCHIVE_FAILED); } /* Flag the cleanup function that we want our lzma-related structures * to be freed later. */ zip->zipx_lzma_valid = 1; /* The "lzma alone" file format and the stream format inside ZIPx are * almost the same. Here's an example of a structure of "lzma alone" * format: * * $ cat /bin/ls | lzma | xxd | head -n 1 * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814 * * 5 bytes 8 bytes n bytes * * * lzma_params is a 5-byte blob that has to be decoded to extract * parameters of this LZMA stream. The uncompressed_size field is an * uint64_t value that contains information about the size of the * uncompressed file, or UINT64_MAX if this value is unknown. * The part is the actual lzma-compressed data stream. * * Now here's the structure of the stream inside the ZIPX file: * * $ cat stream_inside_zipx | xxd | head -n 1 * 00000000: 0914 0500 5d00 8000 0000 2814 .... .... * * 2byte 2byte 5 bytes n bytes * * * This means that the ZIPX file contains an additional magic1 and * magic2 headers, the lzma_params field contains the same parameter * set as in the "lzma alone" format, and the field is the * same as in the "lzma alone" format as well. Note that also the zipx * format is missing the uncompressed_size field. * * So, in order to use the "lzma alone" decoder for the zipx lzma * stream, we simply need to shuffle around some fields, prepare a new * lzma alone header, feed it into lzma alone decoder so it will * initialize itself properly, and then we can start feeding normal * zipx lzma stream into the decoder. */ /* Read magic1,magic2,lzma_params from the ZIPX stream. */ if((p = __archive_read_ahead(a, 9, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzma data"); return (ARCHIVE_FATAL); } if(p[2] != 0x05 || p[3] != 0x00) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid lzma data"); return (ARCHIVE_FATAL); } /* Prepare an lzma alone header: copy the lzma_params blob into * a proper place into the lzma alone header. */ memcpy(&alone_header.bytes[0], p + 4, 5); /* Initialize the 'uncompressed size' field to unknown; we'll manually * monitor how many bytes there are still to be uncompressed. */ alone_header.uncompressed_size = UINT64_MAX; if(!zip->uncompressed_buffer) { zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (uint8_t*) malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for lzma decompression"); return (ARCHIVE_FATAL); } } zip->zipx_lzma_stream.next_in = (void*) &alone_header; zip->zipx_lzma_stream.avail_in = sizeof(alone_header); zip->zipx_lzma_stream.total_in = 0; zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; zip->zipx_lzma_stream.total_out = 0; /* Feed only the header into the lzma alone decoder. This will * effectively initialize the decoder, and will not produce any * output bytes yet. */ r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); if (r != LZMA_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "lzma stream initialization error"); return ARCHIVE_FATAL; } /* We've already consumed some bytes, so take this into account. */ __archive_read_consume(a, 9); zip->entry_bytes_remaining -= 9; zip->entry_compressed_bytes_read += 9; zip->decompress_init = 1; return (ARCHIVE_OK); } static int zip_read_data_zipx_xz(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip* zip = (struct zip *)(a->format->data); int ret; lzma_ret lz_ret; const void* compressed_buf; ssize_t bytes_avail, in_bytes, to_consume = 0; (void) offset; /* UNUSED */ /* Initialize decompressor if not yet initialized. */ if (!zip->decompress_init) { ret = zipx_xz_init(a, zip); if (ret != ARCHIVE_OK) return (ret); } compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated xz file body"); return (ARCHIVE_FATAL); } in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); zip->zipx_lzma_stream.next_in = compressed_buf; zip->zipx_lzma_stream.avail_in = in_bytes; zip->zipx_lzma_stream.total_in = 0; zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; zip->zipx_lzma_stream.total_out = 0; /* Perform the decompression. */ lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); switch(lz_ret) { case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xz data error (error %d)", (int) lz_ret); return (ARCHIVE_FATAL); case LZMA_NO_CHECK: case LZMA_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xz unknown error %d", (int) lz_ret); return (ARCHIVE_FATAL); case LZMA_STREAM_END: lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xz premature end of stream"); return (ARCHIVE_FATAL); } zip->end_of_entry = 1; break; } to_consume = zip->zipx_lzma_stream.total_in; __archive_read_consume(a, to_consume); zip->entry_bytes_remaining -= to_consume; zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; *size = zip->zipx_lzma_stream.total_out; *buff = zip->uncompressed_buffer; ret = consume_optional_marker(a, zip); if (ret != ARCHIVE_OK) return (ret); return (ARCHIVE_OK); } static int zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip* zip = (struct zip *)(a->format->data); int ret; lzma_ret lz_ret; const void* compressed_buf; ssize_t bytes_avail, in_bytes, to_consume; (void) offset; /* UNUSED */ /* Initialize decompressor if not yet initialized. */ if (!zip->decompress_init) { ret = zipx_lzma_alone_init(a, zip); if (ret != ARCHIVE_OK) return (ret); } /* Fetch more compressed data. The same note as in deflate handler * applies here as well: * * Note: '1' here is a performance optimization. Recall that the * decompression layer returns a count of available bytes; asking for * more than that forces the decompressor to combine reads by copying * data. */ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzma file body"); return (ARCHIVE_FATAL); } /* Set decompressor parameters. */ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); zip->zipx_lzma_stream.next_in = compressed_buf; zip->zipx_lzma_stream.avail_in = in_bytes; zip->zipx_lzma_stream.total_in = 0; zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; zip->zipx_lzma_stream.avail_out = /* These lzma_alone streams lack end of stream marker, so let's * make sure the unpacker won't try to unpack more than it's * supposed to. */ zipmin((int64_t) zip->uncompressed_buffer_size, zip->entry->uncompressed_size - zip->entry_uncompressed_bytes_read); zip->zipx_lzma_stream.total_out = 0; /* Perform the decompression. */ lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); switch(lz_ret) { case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lzma data error (error %d)", (int) lz_ret); return (ARCHIVE_FATAL); /* This case is optional in lzma alone format. It can happen, * but most of the files don't have it. (GitHub #1257) */ case LZMA_STREAM_END: lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lzma alone premature end of stream"); return (ARCHIVE_FATAL); } zip->end_of_entry = 1; break; case LZMA_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lzma unknown error %d", (int) lz_ret); return (ARCHIVE_FATAL); } to_consume = zip->zipx_lzma_stream.total_in; /* Update pointers. */ __archive_read_consume(a, to_consume); zip->entry_bytes_remaining -= to_consume; zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; if(zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; } /* Return values. */ *size = zip->zipx_lzma_stream.total_out; *buff = zip->uncompressed_buffer; /* Behave the same way as during deflate decompression. */ ret = consume_optional_marker(a, zip); if (ret != ARCHIVE_OK) return (ret); /* Free lzma decoder handle because we'll no longer need it. */ if(zip->end_of_entry) { lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; } /* If we're here, then we're good! */ return (ARCHIVE_OK); } #endif /* HAVE_LZMA_H && HAVE_LIBLZMA */ static int zipx_ppmd8_init(struct archive_read *a, struct zip *zip) { const void* p; uint32_t val; uint32_t order; uint32_t mem; uint32_t restore_method; /* Remove previous decompression context if it exists. */ if(zip->ppmd8_valid) { __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); zip->ppmd8_valid = 0; } /* Create a new decompression context. */ __archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8); zip->ppmd8_stream_failed = 0; /* Setup function pointers required by Ppmd8 decompressor. The * 'ppmd_read' function will feed new bytes to the decompressor, * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */ zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream; zip->zipx_ppmd_stream.a = a; zip->zipx_ppmd_stream.Read = &ppmd_read; /* Reset number of read bytes to 0. */ zip->zipx_ppmd_read_compressed = 0; /* Read Ppmd8 header (2 bytes). */ p = __archive_read_ahead(a, 2, NULL); if(!p) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated file data in PPMd8 stream"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 2); /* Decode the stream's compression parameters. */ val = archive_le16dec(p); order = (val & 15) + 1; mem = ((val >> 4) & 0xff) + 1; restore_method = (val >> 12); if(order < 2 || restore_method > 2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid parameter set in PPMd8 stream (order=%d, " - "restore=%d)", order, restore_method); + "Invalid parameter set in PPMd8 stream (order=%" PRId32 ", " + "restore=%" PRId32 ")", order, restore_method); return (ARCHIVE_FAILED); } /* Allocate the memory needed to properly decompress the file. */ if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for PPMd8 stream: %d bytes", + "Unable to allocate memory for PPMd8 stream: %" PRId32 " bytes", mem << 20); return (ARCHIVE_FATAL); } /* Signal the cleanup function to release Ppmd8 context in the * cleanup phase. */ zip->ppmd8_valid = 1; /* Perform further Ppmd8 initialization. */ if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "PPMd8 stream range decoder initialization error"); return (ARCHIVE_FATAL); } __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, restore_method); /* Allocate the buffer that will hold uncompressed data. */ free(zip->uncompressed_buffer); zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (uint8_t*) malloc(zip->uncompressed_buffer_size); if(zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for PPMd8 decompression"); return ARCHIVE_FATAL; } /* Ppmd8 initialization is done. */ zip->decompress_init = 1; /* We've already read 2 bytes in the output stream. Additionally, * Ppmd8 initialization code could read some data as well. So we * are advancing the stream by 2 bytes plus whatever number of * bytes Ppmd8 init function used. */ zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed; return ARCHIVE_OK; } static int zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip* zip = (struct zip *)(a->format->data); int ret; size_t consumed_bytes = 0; ssize_t bytes_avail = 0; (void) offset; /* UNUSED */ /* If we're here for the first time, initialize Ppmd8 decompression * context first. */ if(!zip->decompress_init) { ret = zipx_ppmd8_init(a, zip); if(ret != ARCHIVE_OK) return ret; } /* Fetch for more data. We're reading 1 byte here, but libarchive * should prefetch more bytes. */ (void) __archive_read_ahead(a, 1, &bytes_avail); if(bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated PPMd8 file body"); return (ARCHIVE_FATAL); } /* This counter will be updated inside ppmd_read(), which at one * point will be called by Ppmd8_DecodeSymbol. */ zip->zipx_ppmd_read_compressed = 0; /* Decompression loop. */ do { int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol( &zip->ppmd8); if(sym < 0) { zip->end_of_entry = 1; break; } /* This field is set by ppmd_read() when there was no more data * to be read. */ if(zip->ppmd8_stream_failed) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated PPMd8 file body"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym; ++consumed_bytes; } while(consumed_bytes < zip->uncompressed_buffer_size); /* Update pointers for libarchive. */ *buff = zip->uncompressed_buffer; *size = consumed_bytes; /* Update pointers so we can continue decompression in another call. */ zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed; zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed; zip->entry_uncompressed_bytes_read += consumed_bytes; /* If we're at the end of stream, deinitialize Ppmd8 context. */ if(zip->end_of_entry) { __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); zip->ppmd8_valid = 0; } /* Seek for optional marker, same way as in each zip entry. */ ret = consume_optional_marker(a, zip); if (ret != ARCHIVE_OK) return ret; return ARCHIVE_OK; } #ifdef HAVE_BZLIB_H static int zipx_bzip2_init(struct archive_read *a, struct zip *zip) { int r; /* Deallocate already existing BZ2 decompression context if it * exists. */ if(zip->bzstream_valid) { BZ2_bzDecompressEnd(&zip->bzstream); zip->bzstream_valid = 0; } /* Allocate a new BZ2 decompression context. */ memset(&zip->bzstream, 0, sizeof(bz_stream)); r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1); if(r != BZ_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "bzip2 initialization failed(%d)", r); return ARCHIVE_FAILED; } /* Mark the bzstream field to be released in cleanup phase. */ zip->bzstream_valid = 1; /* (Re)allocate the buffer that will contain decompressed bytes. */ free(zip->uncompressed_buffer); zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (uint8_t*) malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for bzip2 decompression"); return ARCHIVE_FATAL; } /* Initialization done. */ zip->decompress_init = 1; return ARCHIVE_OK; } static int zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip = (struct zip *)(a->format->data); ssize_t bytes_avail = 0, in_bytes, to_consume; const void *compressed_buff; int r; uint64_t total_out; (void) offset; /* UNUSED */ /* Initialize decompression context if we're here for the first time. */ if(!zip->decompress_init) { r = zipx_bzip2_init(a, zip); if(r != ARCHIVE_OK) return r; } /* Fetch more compressed bytes. */ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); if(bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated bzip2 file body"); return (ARCHIVE_FATAL); } in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); if(in_bytes < 1) { /* libbz2 doesn't complain when caller feeds avail_in == 0. * It will actually return success in this case, which is * undesirable. This is why we need to make this check * manually. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated bzip2 file body"); return (ARCHIVE_FATAL); } /* Setup buffer boundaries. */ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; zip->bzstream.avail_in = in_bytes; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_in_lo32 = 0; zip->bzstream.next_out = (char*) zip->uncompressed_buffer; zip->bzstream.avail_out = zip->uncompressed_buffer_size; zip->bzstream.total_out_hi32 = 0; zip->bzstream.total_out_lo32 = 0; /* Perform the decompression. */ r = BZ2_bzDecompress(&zip->bzstream); switch(r) { case BZ_STREAM_END: /* If we're at the end of the stream, deinitialize the * decompression context now. */ switch(BZ2_bzDecompressEnd(&zip->bzstream)) { case BZ_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up bzip2 " "decompressor"); return ARCHIVE_FATAL; } zip->end_of_entry = 1; break; case BZ_OK: /* The decompressor has successfully decoded this * chunk of data, but more data is still in queue. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "bzip2 decompression failed"); return ARCHIVE_FATAL; } /* Update the pointers so decompressor can continue decoding. */ to_consume = zip->bzstream.total_in_lo32; __archive_read_consume(a, to_consume); total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) + zip->bzstream.total_out_lo32; zip->entry_bytes_remaining -= to_consume; zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += total_out; /* Give libarchive its due. */ *size = total_out; *buff = zip->uncompressed_buffer; /* Seek for optional marker, like in other entries. */ r = consume_optional_marker(a, zip); if(r != ARCHIVE_OK) return r; return ARCHIVE_OK; } #endif #ifdef HAVE_ZLIB_H static int zip_deflate_init(struct archive_read *a, struct zip *zip) { int r; /* If we haven't yet read any data, initialize the decompressor. */ if (!zip->decompress_init) { if (zip->stream_valid) r = inflateReset(&zip->stream); else r = inflateInit2(&zip->stream, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize ZIP decompression."); return (ARCHIVE_FATAL); } /* Stream structure has been set up. */ zip->stream_valid = 1; /* We've initialized decompression for this stream. */ zip->decompress_init = 1; } return (ARCHIVE_OK); } static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip; ssize_t bytes_avail; const void *compressed_buff, *sp; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (unsigned char *)malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decompression"); return (ARCHIVE_FATAL); } } r = zip_deflate_init(a, zip); if (r != ARCHIVE_OK) return (r); /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail); if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && bytes_avail > zip->entry_bytes_remaining) { bytes_avail = (ssize_t)zip->entry_bytes_remaining; } if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); return (ARCHIVE_FATAL); } if (zip->tctx_valid || zip->cctx_valid) { if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { size_t buff_remaining = (zip->decrypted_buffer + zip->decrypted_buffer_size) - (zip->decrypted_ptr + zip->decrypted_bytes_remaining); if (buff_remaining > (size_t)bytes_avail) buff_remaining = (size_t)bytes_avail; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining > 0) { if ((int64_t)(zip->decrypted_bytes_remaining + buff_remaining) > zip->entry_bytes_remaining) { if (zip->entry_bytes_remaining < (int64_t)zip->decrypted_bytes_remaining) buff_remaining = 0; else buff_remaining = (size_t)zip->entry_bytes_remaining - zip->decrypted_bytes_remaining; } } if (buff_remaining > 0) { if (zip->tctx_valid) { trad_enc_decrypt_update(&zip->tctx, compressed_buff, buff_remaining, zip->decrypted_ptr + zip->decrypted_bytes_remaining, buff_remaining); } else { size_t dsize = buff_remaining; archive_decrypto_aes_ctr_update( &zip->cctx, compressed_buff, buff_remaining, zip->decrypted_ptr + zip->decrypted_bytes_remaining, &dsize); } zip->decrypted_bytes_remaining += buff_remaining; } } bytes_avail = zip->decrypted_bytes_remaining; compressed_buff = (const char *)zip->decrypted_ptr; } /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the * next_in pointer, only reads it). The result: this ugly * cast to remove 'const'. */ zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff; zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = zip->uncompressed_buffer; zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: zip->end_of_entry = 1; break; case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for ZIP decompression"); return (ARCHIVE_FATAL); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP decompression failed (%d)", r); return (ARCHIVE_FATAL); } /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; if (zip->tctx_valid || zip->cctx_valid) { zip->decrypted_bytes_remaining -= bytes_avail; if (zip->decrypted_bytes_remaining == 0) zip->decrypted_ptr = zip->decrypted_buffer; else zip->decrypted_ptr += bytes_avail; } /* Calculate compressed data as much as we used.*/ if (zip->hctx_valid) archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail); __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; *size = zip->stream.total_out; zip->entry_uncompressed_bytes_read += zip->stream.total_out; *buff = zip->uncompressed_buffer; if (zip->end_of_entry && zip->hctx_valid) { r = check_authentication_code(a, NULL); if (r != ARCHIVE_OK) return (r); } r = consume_optional_marker(a, zip); if (r != ARCHIVE_OK) return (r); return (ARCHIVE_OK); } #endif static int read_decryption_header(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const char *p; unsigned int remaining_size; unsigned int ts; /* * Read an initialization vector data field. */ p = __archive_read_ahead(a, 2, NULL); if (p == NULL) goto truncated; ts = zip->iv_size; zip->iv_size = archive_le16dec(p); __archive_read_consume(a, 2); if (ts < zip->iv_size) { free(zip->iv); zip->iv = NULL; } p = __archive_read_ahead(a, zip->iv_size, NULL); if (p == NULL) goto truncated; if (zip->iv == NULL) { zip->iv = malloc(zip->iv_size); if (zip->iv == NULL) goto nomem; } memcpy(zip->iv, p, zip->iv_size); __archive_read_consume(a, zip->iv_size); /* * Read a size of remaining decryption header field. */ p = __archive_read_ahead(a, 14, NULL); if (p == NULL) goto truncated; remaining_size = archive_le32dec(p); if (remaining_size < 16 || remaining_size > (1 << 18)) goto corrupted; /* Check if format version is supported. */ if (archive_le16dec(p+4) != 3) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported encryption format version: %u", archive_le16dec(p+4)); return (ARCHIVE_FAILED); } /* * Read an encryption algorithm field. */ zip->alg_id = archive_le16dec(p+6); switch (zip->alg_id) { case 0x6601:/* DES */ case 0x6602:/* RC2 */ case 0x6603:/* 3DES 168 */ case 0x6609:/* 3DES 112 */ case 0x660E:/* AES 128 */ case 0x660F:/* AES 192 */ case 0x6610:/* AES 256 */ case 0x6702:/* RC2 (version >= 5.2) */ case 0x6720:/* Blowfish */ case 0x6721:/* Twofish */ case 0x6801:/* RC4 */ /* Supported encryption algorithm. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption algorithm: %u", zip->alg_id); return (ARCHIVE_FAILED); } /* * Read a bit length field. */ zip->bit_len = archive_le16dec(p+8); /* * Read a flags field. */ zip->flags = archive_le16dec(p+10); switch (zip->flags & 0xf000) { case 0x0001: /* Password is required to decrypt. */ case 0x0002: /* Certificates only. */ case 0x0003: /* Password or certificate required to decrypt. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption flag: %u", zip->flags); return (ARCHIVE_FAILED); } if ((zip->flags & 0xf000) == 0 || (zip->flags & 0xf000) == 0x4000) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption flag: %u", zip->flags); return (ARCHIVE_FAILED); } /* * Read an encrypted random data field. */ ts = zip->erd_size; zip->erd_size = archive_le16dec(p+12); __archive_read_consume(a, 14); if ((zip->erd_size & 0xf) != 0 || (zip->erd_size + 16) > remaining_size || (zip->erd_size + 16) < zip->erd_size) goto corrupted; if (ts < zip->erd_size) { free(zip->erd); zip->erd = NULL; } p = __archive_read_ahead(a, zip->erd_size, NULL); if (p == NULL) goto truncated; if (zip->erd == NULL) { zip->erd = malloc(zip->erd_size); if (zip->erd == NULL) goto nomem; } memcpy(zip->erd, p, zip->erd_size); __archive_read_consume(a, zip->erd_size); /* * Read a reserved data field. */ p = __archive_read_ahead(a, 4, NULL); if (p == NULL) goto truncated; /* Reserved data size should be zero. */ if (archive_le32dec(p) != 0) goto corrupted; __archive_read_consume(a, 4); /* * Read a password validation data field. */ p = __archive_read_ahead(a, 2, NULL); if (p == NULL) goto truncated; ts = zip->v_size; zip->v_size = archive_le16dec(p); __archive_read_consume(a, 2); if ((zip->v_size & 0x0f) != 0 || (zip->erd_size + zip->v_size + 16) > remaining_size || (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size)) goto corrupted; if (ts < zip->v_size) { free(zip->v_data); zip->v_data = NULL; } p = __archive_read_ahead(a, zip->v_size, NULL); if (p == NULL) goto truncated; if (zip->v_data == NULL) { zip->v_data = malloc(zip->v_size); if (zip->v_data == NULL) goto nomem; } memcpy(zip->v_data, p, zip->v_size); __archive_read_consume(a, zip->v_size); p = __archive_read_ahead(a, 4, NULL); if (p == NULL) goto truncated; zip->v_crc32 = archive_le32dec(p); __archive_read_consume(a, 4); /*return (ARCHIVE_OK); * This is not fully implemented yet.*/ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encrypted file is unsupported"); return (ARCHIVE_FAILED); truncated: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); corrupted: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted ZIP file data"); return (ARCHIVE_FATAL); nomem: archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decryption"); return (ARCHIVE_FATAL); } static int zip_alloc_decryption_buffer(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); size_t bs = 256 * 1024; if (zip->decrypted_buffer == NULL) { zip->decrypted_buffer_size = bs; zip->decrypted_buffer = malloc(bs); if (zip->decrypted_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decryption"); return (ARCHIVE_FATAL); } } zip->decrypted_ptr = zip->decrypted_buffer; return (ARCHIVE_OK); } static int init_traditional_PKWARE_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const void *p; int retry; int r; if (zip->tctx_valid) return (ARCHIVE_OK); /* Read the 12 bytes encryption header stored at the start of the data area. */ #define ENC_HEADER_SIZE 12 if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < ENC_HEADER_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated Zip encrypted body: only %jd bytes available", (intmax_t)zip->entry_bytes_remaining); return (ARCHIVE_FATAL); } p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } for (retry = 0;; retry++) { const char *passphrase; uint8_t crcchk; passphrase = __archive_read_next_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, (retry > 0)? "Incorrect passphrase": "Passphrase required for this entry"); return (ARCHIVE_FAILED); } /* * Initialize ctx for Traditional PKWARE Decryption. */ r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), p, ENC_HEADER_SIZE, &crcchk); if (r == 0 && crcchk == zip->entry->decdat) break;/* The passphrase is OK. */ if (retry > 10000) { /* Avoid infinity loop. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many incorrect passphrases"); return (ARCHIVE_FAILED); } } __archive_read_consume(a, ENC_HEADER_SIZE); zip->tctx_valid = 1; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { zip->entry_bytes_remaining -= ENC_HEADER_SIZE; } /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/ zip->entry_compressed_bytes_read += ENC_HEADER_SIZE; zip->decrypted_bytes_remaining = 0; return (zip_alloc_decryption_buffer(a)); #undef ENC_HEADER_SIZE } static int init_WinZip_AES_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const void *p; const uint8_t *pv; size_t key_len, salt_len; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; int retry; int r; if (zip->cctx_valid || zip->hctx_valid) return (ARCHIVE_OK); switch (zip->entry->aes_extra.strength) { case 1: salt_len = 8; key_len = 16; break; case 2: salt_len = 12; key_len = 24; break; case 3: salt_len = 16; key_len = 32; break; default: goto corrupted; } p = __archive_read_ahead(a, salt_len + 2, NULL); if (p == NULL) goto truncated; for (retry = 0;; retry++) { const char *passphrase; passphrase = __archive_read_next_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, (retry > 0)? "Incorrect passphrase": "Passphrase required for this entry"); return (ARCHIVE_FAILED); } memset(derived_key, 0, sizeof(derived_key)); r = archive_pbkdf2_sha1(passphrase, strlen(passphrase), p, salt_len, 1000, derived_key, key_len * 2 + 2); if (r != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of " "crypto library"); return (ARCHIVE_FAILED); } /* Check password verification value. */ pv = ((const uint8_t *)p) + salt_len; if (derived_key[key_len * 2] == pv[0] && derived_key[key_len * 2 + 1] == pv[1]) break;/* The passphrase is OK. */ if (retry > 10000) { /* Avoid infinity loop. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many incorrect passphrases"); return (ARCHIVE_FAILED); } } r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); if (r != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of crypto library"); return (ARCHIVE_FAILED); } r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); if (r != 0) { archive_decrypto_aes_ctr_release(&zip->cctx); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize HMAC-SHA1"); return (ARCHIVE_FAILED); } zip->cctx_valid = zip->hctx_valid = 1; __archive_read_consume(a, salt_len + 2); zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 0) goto corrupted; zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE; zip->decrypted_bytes_remaining = 0; zip->entry->compression = zip->entry->aes_extra.compression; return (zip_alloc_decryption_buffer(a)); truncated: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); corrupted: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted ZIP file data"); return (ARCHIVE_FATAL); } static int archive_read_format_zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int r; struct zip *zip = (struct zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } *offset = zip->entry_uncompressed_bytes_read; *size = 0; *buff = NULL; /* If we hit end-of-entry last time, return ARCHIVE_EOF. */ if (zip->end_of_entry) return (ARCHIVE_EOF); /* Return EOF immediately if this is a non-regular file. */ if (AE_IFREG != (zip->entry->mode & AE_IFMT)) return (ARCHIVE_EOF); __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; if (zip->init_decryption) { zip->has_encrypted_entries = 1; if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) r = read_decryption_header(a); else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) r = init_WinZip_AES_decryption(a); else r = init_traditional_PKWARE_decryption(a); if (r != ARCHIVE_OK) return (r); zip->init_decryption = 0; } switch(zip->entry->compression) { case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); break; #ifdef HAVE_BZLIB_H case 12: /* ZIPx bzip2 compression. */ r = zip_read_data_zipx_bzip2(a, buff, size, offset); break; #endif #if HAVE_LZMA_H && HAVE_LIBLZMA case 14: /* ZIPx LZMA compression. */ r = zip_read_data_zipx_lzma_alone(a, buff, size, offset); break; case 95: /* ZIPx XZ compression. */ r = zip_read_data_zipx_xz(a, buff, size, offset); break; #endif /* PPMd support is built-in, so we don't need any #if guards. */ case 98: /* ZIPx PPMd compression. */ r = zip_read_data_zipx_ppmd(a, buff, size, offset); break; #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ r = zip_read_data_deflate(a, buff, size, offset); break; #endif default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported ZIP compression method (%d: %s)", zip->entry->compression, compression_name(zip->entry->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_FAILED); break; } if (r != ARCHIVE_OK) return (r); /* Update checksum */ if (*size) zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff, (unsigned)*size); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { /* Check file size, CRC against these values. */ if (zip->entry->compressed_size != zip->entry_compressed_bytes_read) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP compressed data is wrong size " "(read %jd, expected %jd)", (intmax_t)zip->entry_compressed_bytes_read, (intmax_t)zip->entry->compressed_size); return (ARCHIVE_WARN); } /* Size field only stores the lower 32 bits of the actual * size. */ if ((zip->entry->uncompressed_size & UINT32_MAX) != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP uncompressed data is wrong size " "(read %jd, expected %jd)\n", (intmax_t)zip->entry_uncompressed_bytes_read, (intmax_t)zip->entry->uncompressed_size); return (ARCHIVE_WARN); } /* Check computed CRC against header */ if ((!zip->hctx_valid || zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && zip->entry->crc32 != zip->entry_crc32 && !zip->ignore_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad CRC: 0x%lx should be 0x%lx", (unsigned long)zip->entry_crc32, (unsigned long)zip->entry->crc32); return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } static int archive_read_format_zip_cleanup(struct archive_read *a) { struct zip *zip; struct zip_entry *zip_entry, *next_zip_entry; zip = (struct zip *)(a->format->data); #ifdef HAVE_ZLIB_H if (zip->stream_valid) inflateEnd(&zip->stream); #endif #if HAVE_LZMA_H && HAVE_LIBLZMA if (zip->zipx_lzma_valid) { lzma_end(&zip->zipx_lzma_stream); } #endif #ifdef HAVE_BZLIB_H if (zip->bzstream_valid) { BZ2_bzDecompressEnd(&zip->bzstream); } #endif free(zip->uncompressed_buffer); if (zip->ppmd8_valid) __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); if (zip->zip_entries) { zip_entry = zip->zip_entries; while (zip_entry != NULL) { next_zip_entry = zip_entry->next; archive_string_free(&zip_entry->rsrcname); free(zip_entry); zip_entry = next_zip_entry; } } free(zip->decrypted_buffer); if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); free(zip->iv); free(zip->erd); free(zip->v_data); archive_string_free(&zip->format_name); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } static int archive_read_format_zip_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct zip * zip = (struct zip *)_a->format->data; if (zip) { return zip->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_zip_options(struct archive_read *a, const char *key, const char *val) { struct zip *zip; int ret = ARCHIVE_FAILED; zip = (struct zip *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filenames as libarchive 2.x */ zip->init_default_conversion = (val != NULL) ? 1 : 0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zip: hdrcharset option needs a character-set name" ); else { zip->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (zip->sconv != NULL) { if (strcmp(val, "UTF-8") == 0) zip->sconv_utf8 = zip->sconv; ret = ARCHIVE_OK; } else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "ignorecrc32") == 0) { /* Mostly useful for testing. */ if (val == NULL || val[0] == 0) { zip->crc32func = real_crc32; zip->ignore_crc32 = 0; } else { zip->crc32func = fake_crc32; zip->ignore_crc32 = 1; } return (ARCHIVE_OK); } else if (strcmp(key, "mac-ext") == 0) { zip->process_mac_extensions = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } int archive_read_support_format_zip(struct archive *a) { int r; r = archive_read_support_format_zip_streamable(a); if (r != ARCHIVE_OK) return r; return (archive_read_support_format_zip_seekable(a)); } /* ------------------------------------------------------------------------ */ /* * Streaming-mode support */ static int archive_read_support_format_zip_capabilities_streamable(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) { const char *p; (void)best_bid; /* UNUSED */ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* * Bid of 29 here comes from: * + 16 bits for "PK", * + next 16-bit field has 6 options so contributes * about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits * * So we've effectively verified ~29 total bits of check data. */ if (p[0] == 'P' && p[1] == 'K') { if ((p[2] == '\001' && p[3] == '\002') || (p[2] == '\003' && p[3] == '\004') || (p[2] == '\005' && p[3] == '\006') || (p[2] == '\006' && p[3] == '\006') || (p[2] == '\007' && p[3] == '\010') || (p[2] == '0' && p[3] == '0')) return (29); } /* TODO: It's worth looking ahead a little bit for a valid * PK signature. In particular, that would make it possible * to read some UUEncoded SFX files or SFX files coming from * a network socket. */ return (0); } static int archive_read_format_zip_streamable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "ZIP"; zip = (struct zip *)(a->format->data); /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; /* Make sure we have a zip_entry structure to use. */ if (zip->zip_entries == NULL) { zip->zip_entries = malloc(sizeof(struct zip_entry)); if (zip->zip_entries == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return ARCHIVE_FATAL; } } zip->entry = zip->zip_entries; memset(zip->entry, 0, sizeof(struct zip_entry)); if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; __archive_read_reset_passphrase(a); /* Search ahead for the next local file header. */ __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; for (;;) { int64_t skipped = 0; const char *p, *end; ssize_t bytes; p = __archive_read_ahead(a, 4, &bytes); if (p == NULL) return (ARCHIVE_FATAL); end = p + bytes; while (p + 4 <= end) { if (p[0] == 'P' && p[1] == 'K') { if (p[2] == '\003' && p[3] == '\004') { /* Regular file entry. */ __archive_read_consume(a, skipped); return zip_read_local_file_header(a, entry, zip); } /* * TODO: We cannot restore permissions * based only on the local file headers. * Consider scanning the central * directory and returning additional * entries for at least directories. * This would allow us to properly set * directory permissions. * * This won't help us fix symlinks * and may not help with regular file * permissions, either. */ if (p[2] == '\001' && p[3] == '\002') { return (ARCHIVE_EOF); } /* End of central directory? Must be an * empty archive. */ if ((p[2] == '\005' && p[3] == '\006') || (p[2] == '\006' && p[3] == '\006')) return (ARCHIVE_EOF); } ++p; ++skipped; } __archive_read_consume(a, skipped); } } static int archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) { struct zip *zip; int64_t bytes_skipped; zip = (struct zip *)(a->format->data); bytes_skipped = __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* So we know we're streaming... */ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) || zip->entry->compressed_size > 0) { /* We know the compressed length, so we can just skip. */ bytes_skipped = __archive_read_consume(a, zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } if (zip->init_decryption) { int r; zip->has_encrypted_entries = 1; if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) r = read_decryption_header(a); else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) r = init_WinZip_AES_decryption(a); else r = init_traditional_PKWARE_decryption(a); if (r != ARCHIVE_OK) return (r); zip->init_decryption = 0; } /* We're streaming and we don't know the length. */ /* If the body is compressed and we know the format, we can * find an exact end-of-entry by decompressing it. */ switch (zip->entry->compression) { #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ while (!zip->end_of_entry) { int64_t offset = 0; const void *buff = NULL; size_t size = 0; int r; r = zip_read_data_deflate(a, &buff, &size, &offset); if (r != ARCHIVE_OK) return (r); } return ARCHIVE_OK; #endif default: /* Uncompressed or unknown. */ /* Scan for a PK\007\010 signature. */ for (;;) { const char *p, *buff; ssize_t bytes_avail; buff = __archive_read_ahead(a, 16, &bytes_avail); if (bytes_avail < 16) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } p = buff; while (p <= buff + bytes_avail - 16) { if (p[3] == 'P') { p += 3; } else if (p[3] == 'K') { p += 2; } else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->entry->flags & LA_USED_ZIP64) __archive_read_consume(a, p - buff + 24); else __archive_read_consume(a, p - buff + 16); return ARCHIVE_OK; } else { p += 4; } } __archive_read_consume(a, p - buff); } } } int archive_read_support_format_zip_streamable(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip"); zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } /* Streamable reader doesn't support mac extensions. */ zip->process_mac_extensions = 0; /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; zip->crc32func = real_crc32; r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_streamable_bid, archive_read_format_zip_options, archive_read_format_zip_streamable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip_streamable, NULL, archive_read_format_zip_cleanup, archive_read_support_format_zip_capabilities_streamable, archive_read_format_zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } /* ------------------------------------------------------------------------ */ /* * Seeking-mode support */ static int archive_read_support_format_zip_capabilities_seekable(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } /* * TODO: This is a performance sink because it forces the read core to * drop buffered data from the start of file, which will then have to * be re-read again if this bidder loses. * * We workaround this a little by passing in the best bid so far so * that later bidders can do nothing if they know they'll never * outbid. But we can certainly do better... */ static int read_eocd(struct zip *zip, const char *p, int64_t current_offset) { /* Sanity-check the EOCD we've found. */ /* This must be the first volume. */ if (archive_le16dec(p + 4) != 0) return 0; /* Central directory must be on this volume. */ if (archive_le16dec(p + 4) != archive_le16dec(p + 6)) return 0; /* All central directory entries must be on this volume. */ if (archive_le16dec(p + 10) != archive_le16dec(p + 8)) return 0; /* Central directory can't extend beyond start of EOCD record. */ if (archive_le32dec(p + 16) + archive_le32dec(p + 12) > current_offset) return 0; /* Save the central directory location for later use. */ zip->central_directory_offset = archive_le32dec(p + 16); /* This is just a tiny bit higher than the maximum returned by the streaming Zip bidder. This ensures that the more accurate seeking Zip parser wins whenever seek is available. */ return 32; } /* * Examine Zip64 EOCD locator: If it's valid, store the information * from it. */ static int read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) { int64_t eocd64_offset; int64_t eocd64_size; /* Sanity-check the locator record. */ /* Central dir must be on first volume. */ if (archive_le32dec(p + 4) != 0) return 0; /* Must be only a single volume. */ if (archive_le32dec(p + 16) != 1) return 0; /* Find the Zip64 EOCD record. */ eocd64_offset = archive_le64dec(p + 8); if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0) return 0; if ((p = __archive_read_ahead(a, 56, NULL)) == NULL) return 0; /* Make sure we can read all of it. */ eocd64_size = archive_le64dec(p + 4) + 12; if (eocd64_size < 56 || eocd64_size > 16384) return 0; if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL) return 0; /* Sanity-check the EOCD64 */ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */ return 0; if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */ return 0; /* CD can't be split. */ if (archive_le64dec(p + 24) != archive_le64dec(p + 32)) return 0; /* Save the central directory offset for later use. */ zip->central_directory_offset = archive_le64dec(p + 48); return 32; } static int archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) { struct zip *zip = (struct zip *)a->format->data; int64_t file_size, current_offset; const char *p; int i, tail; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); file_size = __archive_read_seek(a, 0, SEEK_END); if (file_size <= 0) return 0; /* Search last 16k of file for end-of-central-directory * record (which starts with PK\005\006) */ tail = (int)zipmin(1024 * 16, file_size); current_offset = __archive_read_seek(a, -tail, SEEK_END); if (current_offset < 0) return 0; if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) return 0; /* Boyer-Moore search backwards from the end, since we want * to match the last EOCD in the file (there can be more than * one if there is an uncompressed Zip archive as a member * within this Zip archive). */ for (i = tail - 22; i > 0;) { switch (p[i]) { case 'P': if (memcmp(p + i, "PK\005\006", 4) == 0) { int ret = read_eocd(zip, p + i, current_offset + i); /* Zip64 EOCD locator precedes * regular EOCD if present. */ if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) { int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20); if (ret_zip64 > ret) ret = ret_zip64; } return (ret); } i -= 4; break; case 'K': i -= 1; break; case 005: i -= 2; break; case 006: i -= 3; break; default: i -= 4; break; } } return 0; } /* The red-black trees are only used in seeking mode to manage * the in-memory copy of the central directory. */ static int cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct zip_entry *e1 = (const struct zip_entry *)n1; const struct zip_entry *e2 = (const struct zip_entry *)n2; if (e1->local_header_offset > e2->local_header_offset) return -1; if (e1->local_header_offset < e2->local_header_offset) return 1; return 0; } static int cmp_key(const struct archive_rb_node *n, const void *key) { /* This function won't be called */ (void)n; /* UNUSED */ (void)key; /* UNUSED */ return 1; } static const struct archive_rb_tree_ops rb_ops = { &cmp_node, &cmp_key }; static int rsrc_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct zip_entry *e1 = (const struct zip_entry *)n1; const struct zip_entry *e2 = (const struct zip_entry *)n2; return (strcmp(e2->rsrcname.s, e1->rsrcname.s)); } static int rsrc_cmp_key(const struct archive_rb_node *n, const void *key) { const struct zip_entry *e = (const struct zip_entry *)n; return (strcmp((const char *)key, e->rsrcname.s)); } static const struct archive_rb_tree_ops rb_rsrc_ops = { &rsrc_cmp_node, &rsrc_cmp_key }; static const char * rsrc_basename(const char *name, size_t name_length) { const char *s, *r; r = s = name; for (;;) { s = memchr(s, '/', name_length - (s - name)); if (s == NULL) break; r = ++s; } return (r); } static void expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) { struct archive_string str; struct zip_entry *dir; char *s; archive_string_init(&str); archive_strncpy(&str, name, name_length); for (;;) { s = strrchr(str.s, '/'); if (s == NULL) break; *s = '\0'; /* Transfer the parent directory from zip->tree_rsrc RB * tree to zip->tree RB tree to expose. */ dir = (struct zip_entry *) __archive_rb_tree_find_node(&zip->tree_rsrc, str.s); if (dir == NULL) break; __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node); archive_string_free(&dir->rsrcname); __archive_rb_tree_insert_node(&zip->tree, &dir->node); } archive_string_free(&str); } static int slurp_central_directory(struct archive_read *a, struct archive_entry* entry, struct zip *zip) { ssize_t i; unsigned found; int64_t correction; ssize_t bytes_avail; const char *p; /* * Find the start of the central directory. The end-of-CD * record has our starting point, but there are lots of * Zip archives which have had other data prepended to the * file, which makes the recorded offsets all too small. * So we search forward from the specified offset until we * find the real start of the central directory. Then we * know the correction we need to apply to account for leading * padding. */ if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0) return ARCHIVE_FATAL; found = 0; while (!found) { if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL) return ARCHIVE_FATAL; for (found = 0, i = 0; !found && i < bytes_avail - 4;) { switch (p[i + 3]) { case 'P': i += 3; break; case 'K': i += 2; break; case 001: i += 1; break; case 002: if (memcmp(p + i, "PK\001\002", 4) == 0) { p += i; found = 1; } else i += 4; break; case 005: i += 1; break; case 006: if (memcmp(p + i, "PK\005\006", 4) == 0) { p += i; found = 1; } else if (memcmp(p + i, "PK\006\006", 4) == 0) { p += i; found = 1; } else i += 1; break; default: i += 4; break; } } __archive_read_consume(a, i); } correction = archive_filter_bytes(&a->archive, 0) - zip->central_directory_offset; __archive_rb_tree_init(&zip->tree, &rb_ops); __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); zip->central_directory_entries_total = 0; while (1) { struct zip_entry *zip_entry; size_t filename_length, extra_length, comment_length; uint32_t external_attributes; const char *name, *r; if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return ARCHIVE_FATAL; if (memcmp(p, "PK\006\006", 4) == 0 || memcmp(p, "PK\005\006", 4) == 0) { break; } else if (memcmp(p, "PK\001\002", 4) != 0) { archive_set_error(&a->archive, -1, "Invalid central directory signature"); return ARCHIVE_FATAL; } if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) return ARCHIVE_FATAL; zip_entry = calloc(1, sizeof(struct zip_entry)); if (zip_entry == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip entry"); return ARCHIVE_FATAL; } zip_entry->next = zip->zip_entries; zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY; zip->zip_entries = zip_entry; zip->central_directory_entries_total++; /* version = p[4]; */ zip_entry->system = p[5]; /* version_required = archive_le16dec(p + 6); */ zip_entry->zip_flags = archive_le16dec(p + 8); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){ zip->has_encrypted_entries = 1; } zip_entry->compression = (char)archive_le16dec(p + 10); zip_entry->mtime = zip_time(p + 12); zip_entry->crc32 = archive_le32dec(p + 16); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[13]; else zip_entry->decdat = p[19]; zip_entry->compressed_size = archive_le32dec(p + 20); zip_entry->uncompressed_size = archive_le32dec(p + 24); filename_length = archive_le16dec(p + 28); extra_length = archive_le16dec(p + 30); comment_length = archive_le16dec(p + 32); /* disk_start = archive_le16dec(p + 34); * Better be zero. * internal_attributes = archive_le16dec(p + 36); * text bit */ external_attributes = archive_le32dec(p + 38); zip_entry->local_header_offset = archive_le32dec(p + 42) + correction; /* If we can't guess the mode, leave it zero here; when we read the local file header we might get more information. */ if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit if (0x10 == (external_attributes & 0x10)) { zip_entry->mode = AE_IFDIR | 0775; } else { zip_entry->mode = AE_IFREG | 0664; } if (0x01 == (external_attributes & 0x01)) { // Read-only bit; strip write permissions zip_entry->mode &= 0555; } } else { zip_entry->mode = 0; } /* We're done with the regular data; get the filename and * extra data. */ __archive_read_consume(a, 46); p = __archive_read_ahead(a, filename_length + extra_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return ARCHIVE_FATAL; } if (ARCHIVE_OK != process_extra(a, entry, p + filename_length, extra_length, zip_entry)) { return ARCHIVE_FATAL; } /* * Mac resource fork files are stored under the * "__MACOSX/" directory, so we should check if * it is. */ if (!zip->process_mac_extensions) { /* Treat every entry as a regular entry. */ __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); } else { name = p; r = rsrc_basename(name, filename_length); if (filename_length >= 9 && strncmp("__MACOSX/", name, 9) == 0) { /* If this file is not a resource fork nor * a directory. We should treat it as a non * resource fork file to expose it. */ if (name[filename_length-1] != '/' && (r - name < 3 || r[0] != '.' || r[1] != '_')) { __archive_rb_tree_insert_node( &zip->tree, &zip_entry->node); /* Expose its parent directories. */ expose_parent_dirs(zip, name, filename_length); } else { /* This file is a resource fork file or * a directory. */ archive_strncpy(&(zip_entry->rsrcname), name, filename_length); __archive_rb_tree_insert_node( &zip->tree_rsrc, &zip_entry->node); } } else { /* Generate resource fork name to find its * resource file at zip->tree_rsrc. */ archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); archive_strncat(&(zip_entry->rsrcname), name, r - name); archive_strcat(&(zip_entry->rsrcname), "._"); archive_strncat(&(zip_entry->rsrcname), name + (r - name), filename_length - (r - name)); /* Register an entry to RB tree to sort it by * file offset. */ __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); } } /* Skip the comment too ... */ __archive_read_consume(a, filename_length + extra_length + comment_length); } return ARCHIVE_OK; } static ssize_t zip_get_local_file_header_size(struct archive_read *a, size_t extra) { const char *p; ssize_t filename_length, extra_length; if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_WARN); } p += extra; if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_WARN; } filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); return (30 + filename_length + extra_length); } static int zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, struct zip_entry *rsrc) { struct zip *zip = (struct zip *)a->format->data; unsigned char *metadata, *mp; int64_t offset = archive_filter_bytes(&a->archive, 0); size_t remaining_bytes, metadata_bytes; ssize_t hsize; int ret = ARCHIVE_OK, eof; switch(rsrc->compression) { case 0: /* No compression. */ if (rsrc->uncompressed_size != rsrc->compressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed OS X metadata entry: " "inconsistent size"); return (ARCHIVE_FATAL); } #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ #endif break; default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported ZIP compression method (%s)", compression_name(rsrc->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_WARN); } if (rsrc->uncompressed_size > (4 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mac metadata is too large: %jd > 4M bytes", (intmax_t)rsrc->uncompressed_size); return (ARCHIVE_WARN); } if (rsrc->compressed_size > (4 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mac metadata is too large: %jd > 4M bytes", (intmax_t)rsrc->compressed_size); return (ARCHIVE_WARN); } metadata = malloc((size_t)rsrc->uncompressed_size); if (metadata == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Mac metadata"); return (ARCHIVE_FATAL); } if (offset < rsrc->local_header_offset) __archive_read_consume(a, rsrc->local_header_offset - offset); else if (offset != rsrc->local_header_offset) { __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); } hsize = zip_get_local_file_header_size(a, 0); __archive_read_consume(a, hsize); remaining_bytes = (size_t)rsrc->compressed_size; metadata_bytes = (size_t)rsrc->uncompressed_size; mp = metadata; eof = 0; while (!eof && remaining_bytes) { const unsigned char *p; ssize_t bytes_avail; size_t bytes_used; p = __archive_read_ahead(a, 1, &bytes_avail); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); ret = ARCHIVE_WARN; goto exit_mac_metadata; } if ((size_t)bytes_avail > remaining_bytes) bytes_avail = remaining_bytes; switch(rsrc->compression) { case 0: /* No compression. */ if ((size_t)bytes_avail > metadata_bytes) bytes_avail = metadata_bytes; memcpy(mp, p, bytes_avail); bytes_used = (size_t)bytes_avail; metadata_bytes -= bytes_used; mp += bytes_used; if (metadata_bytes == 0) eof = 1; break; #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ { int r; ret = zip_deflate_init(a, zip); if (ret != ARCHIVE_OK) goto exit_mac_metadata; zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = mp; zip->stream.avail_out = (uInt)metadata_bytes; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: eof = 1; break; case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for ZIP decompression"); ret = ARCHIVE_FATAL; goto exit_mac_metadata; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP decompression failed (%d)", r); ret = ARCHIVE_FATAL; goto exit_mac_metadata; } bytes_used = zip->stream.total_in; metadata_bytes -= zip->stream.total_out; mp += zip->stream.total_out; break; } #endif default: bytes_used = 0; break; } __archive_read_consume(a, bytes_used); remaining_bytes -= bytes_used; } archive_entry_copy_mac_metadata(entry, metadata, (size_t)rsrc->uncompressed_size - metadata_bytes); exit_mac_metadata: __archive_read_seek(a, offset, SEEK_SET); zip->decompress_init = 0; free(metadata); return (ret); } static int archive_read_format_zip_seekable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip = (struct zip *)a->format->data; struct zip_entry *rsrc; int64_t offset; int r, ret = ARCHIVE_OK; /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "ZIP"; if (zip->zip_entries == NULL) { r = slurp_central_directory(a, entry, zip); if (r != ARCHIVE_OK) return r; /* Get first entry whose local header offset is lower than * other entries in the archive file. */ zip->entry = (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree); } else if (zip->entry != NULL) { /* Get next entry in local header offset order. */ zip->entry = (struct zip_entry *)__archive_rb_tree_iterate( &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT); } if (zip->entry == NULL) return ARCHIVE_EOF; if (zip->entry->rsrcname.s) rsrc = (struct zip_entry *)__archive_rb_tree_find_node( &zip->tree_rsrc, zip->entry->rsrcname.s); else rsrc = NULL; if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; __archive_read_reset_passphrase(a); /* File entries are sorted by the header offset, we should mostly * use __archive_read_consume to advance a read point to avoid * redundant data reading. */ offset = archive_filter_bytes(&a->archive, 0); if (offset < zip->entry->local_header_offset) __archive_read_consume(a, zip->entry->local_header_offset - offset); else if (offset != zip->entry->local_header_offset) { __archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET); } zip->unconsumed = 0; r = zip_read_local_file_header(a, entry, zip); if (r != ARCHIVE_OK) return r; if (rsrc) { int ret2 = zip_read_mac_metadata(a, entry, rsrc); if (ret2 < ret) ret = ret2; } return (ret); } /* * We're going to seek for the next header anyway, so we don't * need to bother doing anything here. */ static int archive_read_format_zip_read_data_skip_seekable(struct archive_read *a) { struct zip *zip; zip = (struct zip *)(a->format->data); zip->unconsumed = 0; return (ARCHIVE_OK); } int archive_read_support_format_zip_seekable(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable"); zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } #ifdef HAVE_COPYFILE_H /* Set this by default on Mac OS. */ zip->process_mac_extensions = 1; #endif /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; zip->crc32func = real_crc32; r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_seekable_bid, archive_read_format_zip_options, archive_read_format_zip_seekable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip_seekable, NULL, archive_read_format_zip_cleanup, archive_read_support_format_zip_capabilities_seekable, archive_read_format_zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } /*# vim:set noet:*/ Index: stable/11/contrib/libarchive/libarchive/archive_string.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_string.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_string.c (revision 368707) @@ -1,4224 +1,4240 @@ /*- * Copyright (c) 2003-2011 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* * Basic resizable string support, to simplify manipulating arbitrary-sized * strings while minimizing heap activity. * * In particular, the buffer used by a string object is only grown, it * never shrinks, so you can clear and reuse the same string object * without incurring additional memory allocations. */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_ICONV_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_LOCALCHARSET_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #endif #include "archive_endian.h" #include "archive_private.h" #include "archive_string.h" #include "archive_string_composition.h" #if !defined(HAVE_WMEMCPY) && !defined(wmemcpy) #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif #if !defined(HAVE_WMEMMOVE) && !defined(wmemmove) #define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) #endif #undef max #define max(a, b) ((a)>(b)?(a):(b)) struct archive_string_conv { struct archive_string_conv *next; char *from_charset; char *to_charset; unsigned from_cp; unsigned to_cp; /* Set 1 if from_charset and to_charset are the same. */ int same; int flag; #define SCONV_TO_CHARSET 1 /* MBS is being converted to specified * charset. */ #define SCONV_FROM_CHARSET (1<<1) /* MBS is being converted from * specified charset. */ #define SCONV_BEST_EFFORT (1<<2) /* Copy at least ASCII code. */ #define SCONV_WIN_CP (1<<3) /* Use Windows API for converting * MBS. */ #define SCONV_UTF8_LIBARCHIVE_2 (1<<4) /* Incorrect UTF-8 made by libarchive * 2.x in the wrong assumption. */ #define SCONV_NORMALIZATION_C (1<<6) /* Need normalization to be Form C. * Before UTF-8 characters are actually * processed. */ #define SCONV_NORMALIZATION_D (1<<7) /* Need normalization to be Form D. * Before UTF-8 characters are actually * processed. * Currently this only for MAC OS X. */ #define SCONV_TO_UTF8 (1<<8) /* "to charset" side is UTF-8. */ #define SCONV_FROM_UTF8 (1<<9) /* "from charset" side is UTF-8. */ #define SCONV_TO_UTF16BE (1<<10) /* "to charset" side is UTF-16BE. */ #define SCONV_FROM_UTF16BE (1<<11) /* "from charset" side is UTF-16BE. */ #define SCONV_TO_UTF16LE (1<<12) /* "to charset" side is UTF-16LE. */ #define SCONV_FROM_UTF16LE (1<<13) /* "from charset" side is UTF-16LE. */ #define SCONV_TO_UTF16 (SCONV_TO_UTF16BE | SCONV_TO_UTF16LE) #define SCONV_FROM_UTF16 (SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE) #if HAVE_ICONV iconv_t cd; iconv_t cd_w;/* Use at archive_mstring on * Windows. */ #endif /* A temporary buffer for normalization. */ struct archive_string utftmp; int (*converter[2])(struct archive_string *, const void *, size_t, struct archive_string_conv *); int nconverter; }; #define CP_C_LOCALE 0 /* "C" locale only for this file. */ #define CP_UTF16LE 1200 #define CP_UTF16BE 1201 #define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF) #define IS_LOW_SURROGATE_LA(uc) ((uc) >= 0xDC00 && (uc) <= 0xDFFF) #define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF) #define UNICODE_MAX 0x10FFFF #define UNICODE_R_CHAR 0xFFFD /* Replacement character. */ /* Set U+FFFD(Replacement character) in UTF-8. */ static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd}; static struct archive_string_conv *find_sconv_object(struct archive *, const char *, const char *); static void add_sconv_object(struct archive *, struct archive_string_conv *); static struct archive_string_conv *create_sconv_object(const char *, const char *, unsigned, int); static void free_sconv_object(struct archive_string_conv *); static struct archive_string_conv *get_sconv_object(struct archive *, const char *, const char *, int); static unsigned make_codepage_from_charset(const char *); static unsigned get_current_codepage(void); static unsigned get_current_oemcp(void); static size_t mbsnbytes(const void *, size_t); static size_t utf16nbytes(const void *, size_t); #if defined(_WIN32) && !defined(__CYGWIN__) static int archive_wstring_append_from_mbs_in_codepage( struct archive_wstring *, const char *, size_t, struct archive_string_conv *); static int archive_string_append_from_wcs_in_codepage(struct archive_string *, const wchar_t *, size_t, struct archive_string_conv *); static int is_big_endian(void); static int strncat_in_codepage(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_from_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_from_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_to_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int win_strncat_to_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); #endif static int best_effort_strncat_from_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int best_effort_strncat_from_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int best_effort_strncat_to_utf16be(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int best_effort_strncat_to_utf16le(struct archive_string *, const void *, size_t, struct archive_string_conv *); #if defined(HAVE_ICONV) static int iconv_strncat_in_locale(struct archive_string *, const void *, size_t, struct archive_string_conv *); #endif static int best_effort_strncat_in_locale(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int _utf8_to_unicode(uint32_t *, const char *, size_t); static int utf8_to_unicode(uint32_t *, const char *, size_t); static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t); static int cesu8_to_unicode(uint32_t *, const char *, size_t); static size_t unicode_to_utf8(char *, size_t, uint32_t); static int utf16_to_unicode(uint32_t *, const char *, size_t, int); static size_t unicode_to_utf16be(char *, size_t, uint32_t); static size_t unicode_to_utf16le(char *, size_t, uint32_t); static int strncat_from_utf8_libarchive2(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int strncat_from_utf8_to_utf8(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int archive_string_normalize_C(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int archive_string_normalize_D(struct archive_string *, const void *, size_t, struct archive_string_conv *); static int archive_string_append_unicode(struct archive_string *, const void *, size_t, struct archive_string_conv *); static struct archive_string * archive_string_append(struct archive_string *as, const char *p, size_t s) { if (archive_string_ensure(as, as->length + s + 1) == NULL) return (NULL); if (s) memmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); } static struct archive_wstring * archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) { if (archive_wstring_ensure(as, as->length + s + 1) == NULL) return (NULL); if (s) wmemmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); } struct archive_string * archive_array_append(struct archive_string *as, const char *p, size_t s) { return archive_string_append(as, p, s); } void archive_string_concat(struct archive_string *dest, struct archive_string *src) { if (archive_string_append(dest, src->s, src->length) == NULL) __archive_errx(1, "Out of memory"); } void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src) { if (archive_wstring_append(dest, src->s, src->length) == NULL) __archive_errx(1, "Out of memory"); } void archive_string_free(struct archive_string *as) { as->length = 0; as->buffer_length = 0; free(as->s); as->s = NULL; } void archive_wstring_free(struct archive_wstring *as) { as->length = 0; as->buffer_length = 0; free(as->s); as->s = NULL; } struct archive_wstring * archive_wstring_ensure(struct archive_wstring *as, size_t s) { return (struct archive_wstring *) archive_string_ensure((struct archive_string *)as, s * sizeof(wchar_t)); } /* Returns NULL on any allocation failure. */ struct archive_string * archive_string_ensure(struct archive_string *as, size_t s) { char *p; size_t new_length; /* If buffer is already big enough, don't reallocate. */ if (as->s && (s <= as->buffer_length)) return (as); /* * Growing the buffer at least exponentially ensures that * append operations are always linear in the number of * characters appended. Using a smaller growth rate for * larger buffers reduces memory waste somewhat at the cost of * a larger constant factor. */ if (as->buffer_length < 32) /* Start with a minimum 32-character buffer. */ new_length = 32; else if (as->buffer_length < 8192) /* Buffers under 8k are doubled for speed. */ new_length = as->buffer_length + as->buffer_length; else { /* Buffers 8k and over grow by at least 25% each time. */ new_length = as->buffer_length + as->buffer_length / 4; /* Be safe: If size wraps, fail. */ if (new_length < as->buffer_length) { /* On failure, wipe the string and return NULL. */ archive_string_free(as); errno = ENOMEM;/* Make sure errno has ENOMEM. */ return (NULL); } } /* * The computation above is a lower limit to how much we'll * grow the buffer. In any case, we have to grow it enough to * hold the request. */ if (new_length < s) new_length = s; /* Now we can reallocate the buffer. */ p = (char *)realloc(as->s, new_length); if (p == NULL) { /* On failure, wipe the string and return NULL. */ archive_string_free(as); errno = ENOMEM;/* Make sure errno has ENOMEM. */ return (NULL); } as->s = p; as->buffer_length = new_length; return (as); } /* * TODO: See if there's a way to avoid scanning * the source string twice. Then test to see * if it actually helps (remember that we're almost * always called with pretty short arguments, so * such an optimization might not help). */ struct archive_string * archive_strncat(struct archive_string *as, const void *_p, size_t n) { size_t s; const char *p, *pp; p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (s < n && *pp) { pp++; s++; } if ((as = archive_string_append(as, p, s)) == NULL) __archive_errx(1, "Out of memory"); return (as); } struct archive_wstring * archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n) { size_t s; const wchar_t *pp; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (s < n && *pp) { pp++; s++; } if ((as = archive_wstring_append(as, p, s)) == NULL) __archive_errx(1, "Out of memory"); return (as); } struct archive_string * archive_strcat(struct archive_string *as, const void *p) { /* strcat is just strncat without an effective limit. * Assert that we'll never get called with a source * string over 16MB. * TODO: Review all uses of strcat in the source * and try to replace them with strncat(). */ return archive_strncat(as, p, 0x1000000); } struct archive_wstring * archive_wstrcat(struct archive_wstring *as, const wchar_t *p) { /* Ditto. */ return archive_wstrncat(as, p, 0x1000000); } struct archive_string * archive_strappend_char(struct archive_string *as, char c) { if ((as = archive_string_append(as, &c, 1)) == NULL) __archive_errx(1, "Out of memory"); return (as); } struct archive_wstring * archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c) { if ((as = archive_wstring_append(as, &c, 1)) == NULL) __archive_errx(1, "Out of memory"); return (as); } /* * Get the "current character set" name to use with iconv. * On FreeBSD, the empty character set name "" chooses * the correct character encoding for the current locale, * so this isn't necessary. * But iconv on Mac OS 10.6 doesn't seem to handle this correctly; * on that system, we have to explicitly call nl_langinfo() * to get the right name. Not sure about other platforms. * * NOTE: GNU libiconv does not recognize the character-set name * which some platform nl_langinfo(CODESET) returns, so we should * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv. */ static const char * default_iconv_charset(const char *charset) { if (charset != NULL && charset[0] != '\0') return charset; #if HAVE_LOCALE_CHARSET && !defined(__APPLE__) /* locale_charset() is broken on Mac OS */ return locale_charset(); #elif HAVE_NL_LANGINFO return nl_langinfo(CODESET); #else return ""; #endif } #if defined(_WIN32) && !defined(__CYGWIN__) /* * Convert MBS to WCS. * Note: returns -1 if conversion fails. */ int archive_wstring_append_from_mbs(struct archive_wstring *dest, const char *p, size_t len) { return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL); } static int archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, const char *s, size_t length, struct archive_string_conv *sc) { int count, ret = 0; UINT from_cp; if (sc != NULL) from_cp = sc->from_cp; else from_cp = get_current_codepage(); if (from_cp == CP_C_LOCALE) { /* * "C" locale special processing. */ wchar_t *ws; const unsigned char *mp; if (NULL == archive_wstring_ensure(dest, dest->length + length + 1)) return (-1); ws = dest->s + dest->length; mp = (const unsigned char *)s; count = 0; while (count < (int)length && *mp) { *ws++ = (wchar_t)*mp++; count++; } } else if (sc != NULL && (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) { /* * Normalize UTF-8 and UTF-16BE and convert it directly * to UTF-16 as wchar_t. */ struct archive_string u16; int saved_flag = sc->flag;/* save current flag. */ if (is_big_endian()) sc->flag |= SCONV_TO_UTF16BE; else sc->flag |= SCONV_TO_UTF16LE; if (sc->flag & SCONV_FROM_UTF16) { /* * UTF-16BE/LE NFD ===> UTF-16 NFC * UTF-16BE/LE NFC ===> UTF-16 NFD */ count = (int)utf16nbytes(s, length); } else { /* * UTF-8 NFD ===> UTF-16 NFC * UTF-8 NFC ===> UTF-16 NFD */ count = (int)mbsnbytes(s, length); } u16.s = (char *)dest->s; u16.length = dest->length << 1;; u16.buffer_length = dest->buffer_length; if (sc->flag & SCONV_NORMALIZATION_C) ret = archive_string_normalize_C(&u16, s, count, sc); else ret = archive_string_normalize_D(&u16, s, count, sc); dest->s = (wchar_t *)u16.s; dest->length = u16.length >> 1; dest->buffer_length = u16.buffer_length; sc->flag = saved_flag;/* restore the saved flag. */ return (ret); } else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) { count = (int)utf16nbytes(s, length); count >>= 1; /* to be WCS length */ /* Allocate memory for WCS. */ if (NULL == archive_wstring_ensure(dest, dest->length + count + 1)) return (-1); wmemcpy(dest->s + dest->length, (const wchar_t *)s, count); if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) { uint16_t *u16 = (uint16_t *)(dest->s + dest->length); int b; for (b = 0; b < count; b++) { uint16_t val = archive_le16dec(u16+b); archive_be16enc(u16+b, val); } } else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) { uint16_t *u16 = (uint16_t *)(dest->s + dest->length); int b; for (b = 0; b < count; b++) { uint16_t val = archive_be16dec(u16+b); archive_le16enc(u16+b, val); } } } else { DWORD mbflag; size_t buffsize; if (sc == NULL) mbflag = 0; else if (sc->flag & SCONV_FROM_CHARSET) { /* Do not trust the length which comes from * an archive file. */ length = mbsnbytes(s, length); mbflag = 0; } else mbflag = MB_PRECOMPOSED; buffsize = dest->length + length + 1; do { /* Allocate memory for WCS. */ if (NULL == archive_wstring_ensure(dest, buffsize)) return (-1); /* Convert MBS to WCS. */ count = MultiByteToWideChar(from_cp, mbflag, s, (int)length, dest->s + dest->length, (int)(dest->buffer_length >> 1) -1); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the WCS buffer. */ buffsize = dest->buffer_length << 1; continue; } if (count == 0 && length != 0) ret = -1; break; } while (1); } dest->length += count; dest->s[dest->length] = L'\0'; return (ret); } #else /* * Convert MBS to WCS. * Note: returns -1 if conversion fails. */ int archive_wstring_append_from_mbs(struct archive_wstring *dest, const char *p, size_t len) { size_t r; int ret_val = 0; /* * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ // size_t wcs_length = len; size_t mbs_length = len; const char *mbs = p; wchar_t *wcs; #if HAVE_MBRTOWC mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #endif /* * As we decided to have wcs_length == mbs_length == len * we can use len here instead of wcs_length */ if (NULL == archive_wstring_ensure(dest, dest->length + len + 1)) return (-1); wcs = dest->s + dest->length; /* * We cannot use mbsrtowcs/mbstowcs here because those may convert * extra MBS when strlen(p) > len and one wide character consists of * multi bytes. */ while (*mbs && mbs_length > 0) { /* * The buffer we allocated is always big enough. * Keep this code path in a comment if we decide to choose * smaller wcs_length in the future */ /* if (wcs_length == 0) { dest->length = wcs - dest->s; dest->s[dest->length] = L'\0'; wcs_length = mbs_length; if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1)) return (-1); wcs = dest->s + dest->length; } */ #if HAVE_MBRTOWC r = mbrtowc(wcs, mbs, mbs_length, &shift_state); #else r = mbtowc(wcs, mbs, mbs_length); #endif if (r == (size_t)-1 || r == (size_t)-2) { ret_val = -1; break; } if (r == 0 || r > mbs_length) break; wcs++; // wcs_length--; mbs += r; mbs_length -= r; } dest->length = wcs - dest->s; dest->s[dest->length] = L'\0'; return (ret_val); } #endif #if defined(_WIN32) && !defined(__CYGWIN__) /* * WCS ==> MBS. * Note: returns -1 if conversion fails. * * Win32 builds use WideCharToMultiByte from the Windows API. * (Maybe Cygwin should too? WideCharToMultiByte will know a * lot more about local character encodings than the wcrtomb() * wrapper is going to know.) */ int archive_string_append_from_wcs(struct archive_string *as, const wchar_t *w, size_t len) { return archive_string_append_from_wcs_in_codepage(as, w, len, NULL); } static int archive_string_append_from_wcs_in_codepage(struct archive_string *as, const wchar_t *ws, size_t len, struct archive_string_conv *sc) { BOOL defchar_used, *dp; int count, ret = 0; UINT to_cp; int wslen = (int)len; if (sc != NULL) to_cp = sc->to_cp; else to_cp = get_current_codepage(); if (to_cp == CP_C_LOCALE) { /* * "C" locale special processing. */ const wchar_t *wp = ws; char *p; if (NULL == archive_string_ensure(as, as->length + wslen +1)) return (-1); p = as->s + as->length; count = 0; defchar_used = 0; while (count < wslen && *wp) { if (*wp > 255) { *p++ = '?'; wp++; defchar_used = 1; } else *p++ = (char)*wp++; count++; } } else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) { uint16_t *u16; if (NULL == archive_string_ensure(as, as->length + len * 2 + 2)) return (-1); u16 = (uint16_t *)(as->s + as->length); count = 0; defchar_used = 0; if (sc->flag & SCONV_TO_UTF16BE) { while (count < (int)len && *ws) { archive_be16enc(u16+count, *ws); ws++; count++; } } else { while (count < (int)len && *ws) { archive_le16enc(u16+count, *ws); ws++; count++; } } count <<= 1; /* to be byte size */ } else { /* Make sure the MBS buffer has plenty to set. */ if (NULL == archive_string_ensure(as, as->length + len * 2 + 1)) return (-1); do { defchar_used = 0; if (to_cp == CP_UTF8 || sc == NULL) dp = NULL; else dp = &defchar_used; count = WideCharToMultiByte(to_cp, 0, ws, wslen, as->s + as->length, (int)as->buffer_length - as->length - 1, NULL, dp); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the MBS buffer and retry. */ if (NULL == archive_string_ensure(as, as->buffer_length + len)) return (-1); continue; } if (count == 0) ret = -1; break; } while (1); } as->length += count; as->s[as->length] = '\0'; return (defchar_used?-1:ret); } #elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB) /* * Translates a wide character string into current locale character set * and appends to the archive_string. Note: returns -1 if conversion * fails. */ int archive_string_append_from_wcs(struct archive_string *as, const wchar_t *w, size_t len) { /* We cannot use the standard wcstombs() here because it * cannot tell us how big the output buffer should be. So * I've built a loop around wcrtomb() or wctomb() that * converts a character at a time and resizes the string as * needed. We prefer wcrtomb() when it's available because * it's thread-safe. */ int n, ret_val = 0; char *p; char *end; #if HAVE_WCRTOMB mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ wctomb(NULL, L'\0'); #endif /* * Allocate buffer for MBS. * We need this allocation here since it is possible that * as->s is still NULL. */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; while (*w != L'\0' && len > 0) { if (p >= end) { as->length = p - as->s; as->s[as->length] = '\0'; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, as->length + max(len * 2, (size_t)MB_CUR_MAX) + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; } #if HAVE_WCRTOMB n = wcrtomb(p, *w++, &shift_state); #else n = wctomb(p, *w++); #endif if (n == -1) { if (errno == EILSEQ) { /* Skip an illegal wide char. */ *p++ = '?'; ret_val = -1; } else { ret_val = -1; break; } } else p += n; len--; } as->length = p - as->s; as->s[as->length] = '\0'; return (ret_val); } #else /* HAVE_WCTOMB || HAVE_WCRTOMB */ /* * TODO: Test if __STDC_ISO_10646__ is defined. * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion * one character at a time. If a non-Windows platform doesn't have * either of these, fall back to the built-in UTF8 conversion. */ int archive_string_append_from_wcs(struct archive_string *as, const wchar_t *w, size_t len) { (void)as;/* UNUSED */ (void)w;/* UNUSED */ (void)len;/* UNUSED */ errno = ENOSYS; return (-1); } #endif /* HAVE_WCTOMB || HAVE_WCRTOMB */ /* * Find a string conversion object by a pair of 'from' charset name * and 'to' charset name from an archive object. * Return NULL if not found. */ static struct archive_string_conv * find_sconv_object(struct archive *a, const char *fc, const char *tc) { struct archive_string_conv *sc; if (a == NULL) return (NULL); for (sc = a->sconv; sc != NULL; sc = sc->next) { if (strcmp(sc->from_charset, fc) == 0 && strcmp(sc->to_charset, tc) == 0) break; } return (sc); } /* * Register a string object to an archive object. */ static void add_sconv_object(struct archive *a, struct archive_string_conv *sc) { struct archive_string_conv **psc; /* Add a new sconv to sconv list. */ psc = &(a->sconv); while (*psc != NULL) psc = &((*psc)->next); *psc = sc; } static void add_converter(struct archive_string_conv *sc, int (*converter) (struct archive_string *, const void *, size_t, struct archive_string_conv *)) { if (sc == NULL || sc->nconverter >= 2) __archive_errx(1, "Programming error"); sc->converter[sc->nconverter++] = converter; } static void setup_converter(struct archive_string_conv *sc) { /* Reset. */ sc->nconverter = 0; /* * Perform special sequence for the incorrect UTF-8 filenames * made by libarchive2.x. */ if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) { add_converter(sc, strncat_from_utf8_libarchive2); return; } /* * Convert a string to UTF-16BE/LE. */ if (sc->flag & SCONV_TO_UTF16) { /* * If the current locale is UTF-8, we can translate * a UTF-8 string into a UTF-16BE string. */ if (sc->flag & SCONV_FROM_UTF8) { add_converter(sc, archive_string_append_unicode); return; } #if defined(_WIN32) && !defined(__CYGWIN__) if (sc->flag & SCONV_WIN_CP) { if (sc->flag & SCONV_TO_UTF16BE) add_converter(sc, win_strncat_to_utf16be); else add_converter(sc, win_strncat_to_utf16le); return; } #endif #if defined(HAVE_ICONV) if (sc->cd != (iconv_t)-1) { add_converter(sc, iconv_strncat_in_locale); return; } #endif if (sc->flag & SCONV_BEST_EFFORT) { if (sc->flag & SCONV_TO_UTF16BE) add_converter(sc, best_effort_strncat_to_utf16be); else add_converter(sc, best_effort_strncat_to_utf16le); } else /* Make sure we have no converter. */ sc->nconverter = 0; return; } /* * Convert a string from UTF-16BE/LE. */ if (sc->flag & SCONV_FROM_UTF16) { /* * At least we should normalize a UTF-16BE string. */ if (sc->flag & SCONV_NORMALIZATION_D) add_converter(sc,archive_string_normalize_D); else if (sc->flag & SCONV_NORMALIZATION_C) add_converter(sc, archive_string_normalize_C); if (sc->flag & SCONV_TO_UTF8) { /* * If the current locale is UTF-8, we can translate * a UTF-16BE/LE string into a UTF-8 string directly. */ if (!(sc->flag & (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C))) add_converter(sc, archive_string_append_unicode); return; } #if defined(_WIN32) && !defined(__CYGWIN__) if (sc->flag & SCONV_WIN_CP) { if (sc->flag & SCONV_FROM_UTF16BE) add_converter(sc, win_strncat_from_utf16be); else add_converter(sc, win_strncat_from_utf16le); return; } #endif #if defined(HAVE_ICONV) if (sc->cd != (iconv_t)-1) { add_converter(sc, iconv_strncat_in_locale); return; } #endif if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE)) == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE)) add_converter(sc, best_effort_strncat_from_utf16be); else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE)) == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE)) add_converter(sc, best_effort_strncat_from_utf16le); else /* Make sure we have no converter. */ sc->nconverter = 0; return; } if (sc->flag & SCONV_FROM_UTF8) { /* * At least we should normalize a UTF-8 string. */ if (sc->flag & SCONV_NORMALIZATION_D) add_converter(sc,archive_string_normalize_D); else if (sc->flag & SCONV_NORMALIZATION_C) add_converter(sc, archive_string_normalize_C); /* * Copy UTF-8 string with a check of CESU-8. * Apparently, iconv does not check surrogate pairs in UTF-8 * when both from-charset and to-charset are UTF-8, and then * we use our UTF-8 copy code. */ if (sc->flag & SCONV_TO_UTF8) { /* * If the current locale is UTF-8, we can translate * a UTF-16BE string into a UTF-8 string directly. */ if (!(sc->flag & (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C))) add_converter(sc, strncat_from_utf8_to_utf8); return; } } #if defined(_WIN32) && !defined(__CYGWIN__) /* * On Windows we can use Windows API for a string conversion. */ if (sc->flag & SCONV_WIN_CP) { add_converter(sc, strncat_in_codepage); return; } #endif #if HAVE_ICONV if (sc->cd != (iconv_t)-1) { add_converter(sc, iconv_strncat_in_locale); /* * iconv generally does not support UTF-8-MAC and so * we have to the output of iconv from NFC to NFD if * need. */ if ((sc->flag & SCONV_FROM_CHARSET) && (sc->flag & SCONV_TO_UTF8)) { if (sc->flag & SCONV_NORMALIZATION_D) add_converter(sc, archive_string_normalize_D); } return; } #endif /* * Try conversion in the best effort or no conversion. */ if ((sc->flag & SCONV_BEST_EFFORT) || sc->same) add_converter(sc, best_effort_strncat_in_locale); else /* Make sure we have no converter. */ sc->nconverter = 0; } /* * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE * and CP932 which are referenced in create_sconv_object(). */ static const char * canonical_charset_name(const char *charset) { char cs[16]; char *p; const char *s; if (charset == NULL || charset[0] == '\0' || strlen(charset) > 15) return (charset); /* Copy name to uppercase. */ p = cs; s = charset; while (*s) { char c = *s++; if (c >= 'a' && c <= 'z') c -= 'a' - 'A'; *p++ = c; } *p++ = '\0'; if (strcmp(cs, "UTF-8") == 0 || strcmp(cs, "UTF8") == 0) return ("UTF-8"); if (strcmp(cs, "UTF-16BE") == 0 || strcmp(cs, "UTF16BE") == 0) return ("UTF-16BE"); if (strcmp(cs, "UTF-16LE") == 0 || strcmp(cs, "UTF16LE") == 0) return ("UTF-16LE"); if (strcmp(cs, "CP932") == 0) return ("CP932"); return (charset); } /* * Create a string conversion object. */ static struct archive_string_conv * create_sconv_object(const char *fc, const char *tc, unsigned current_codepage, int flag) { struct archive_string_conv *sc; sc = calloc(1, sizeof(*sc)); if (sc == NULL) return (NULL); sc->next = NULL; sc->from_charset = strdup(fc); if (sc->from_charset == NULL) { free(sc); return (NULL); } sc->to_charset = strdup(tc); if (sc->to_charset == NULL) { free(sc->from_charset); free(sc); return (NULL); } archive_string_init(&sc->utftmp); if (flag & SCONV_TO_CHARSET) { /* * Convert characters from the current locale charset to * a specified charset. */ sc->from_cp = current_codepage; sc->to_cp = make_codepage_from_charset(tc); #if defined(_WIN32) && !defined(__CYGWIN__) if (IsValidCodePage(sc->to_cp)) flag |= SCONV_WIN_CP; #endif } else if (flag & SCONV_FROM_CHARSET) { /* * Convert characters from a specified charset to * the current locale charset. */ sc->to_cp = current_codepage; sc->from_cp = make_codepage_from_charset(fc); #if defined(_WIN32) && !defined(__CYGWIN__) if (IsValidCodePage(sc->from_cp)) flag |= SCONV_WIN_CP; #endif } /* * Check if "from charset" and "to charset" are the same. */ if (strcmp(fc, tc) == 0 || (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp)) sc->same = 1; else sc->same = 0; /* * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE. */ if (strcmp(tc, "UTF-8") == 0) flag |= SCONV_TO_UTF8; else if (strcmp(tc, "UTF-16BE") == 0) flag |= SCONV_TO_UTF16BE; else if (strcmp(tc, "UTF-16LE") == 0) flag |= SCONV_TO_UTF16LE; if (strcmp(fc, "UTF-8") == 0) flag |= SCONV_FROM_UTF8; else if (strcmp(fc, "UTF-16BE") == 0) flag |= SCONV_FROM_UTF16BE; else if (strcmp(fc, "UTF-16LE") == 0) flag |= SCONV_FROM_UTF16LE; #if defined(_WIN32) && !defined(__CYGWIN__) if (sc->to_cp == CP_UTF8) flag |= SCONV_TO_UTF8; else if (sc->to_cp == CP_UTF16BE) flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP; else if (sc->to_cp == CP_UTF16LE) flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP; if (sc->from_cp == CP_UTF8) flag |= SCONV_FROM_UTF8; else if (sc->from_cp == CP_UTF16BE) flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP; else if (sc->from_cp == CP_UTF16LE) flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP; #endif /* * Set a flag for Unicode NFD. Usually iconv cannot correctly * handle it. So we have to translate NFD characters to NFC ones * ourselves before iconv handles. Another reason is to prevent * that the same sight of two filenames, one is NFC and other * is NFD, would be in its directory. * On Mac OS X, although its filesystem layer automatically * convert filenames to NFD, it would be useful for filename * comparing to find out the same filenames that we normalize * that to be NFD ourselves. */ if ((flag & SCONV_FROM_CHARSET) && (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) { #if defined(__APPLE__) if (flag & SCONV_TO_UTF8) flag |= SCONV_NORMALIZATION_D; else #endif flag |= SCONV_NORMALIZATION_C; } #if defined(__APPLE__) /* * In case writing an archive file, make sure that a filename * going to be passed to iconv is a Unicode NFC string since * a filename in HFS Plus filesystem is a Unicode NFD one and * iconv cannot handle it with "UTF-8" charset. It is simpler * than a use of "UTF-8-MAC" charset. */ if ((flag & SCONV_TO_CHARSET) && (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8))) flag |= SCONV_NORMALIZATION_C; /* * In case reading an archive file. make sure that a filename * will be passed to users is a Unicode NFD string in order to * correctly compare the filename with other one which comes * from HFS Plus filesystem. */ if ((flag & SCONV_FROM_CHARSET) && !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && (flag & SCONV_TO_UTF8)) flag |= SCONV_NORMALIZATION_D; #endif #if defined(HAVE_ICONV) sc->cd_w = (iconv_t)-1; /* * Create an iconv object. */ if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) && (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) || (flag & SCONV_WIN_CP)) { /* This case we won't use iconv. */ sc->cd = (iconv_t)-1; } else { sc->cd = iconv_open(tc, fc); if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) { /* * Unfortunately, all of iconv implements do support * "CP932" character-set, so we should use "SJIS" * instead if iconv_open failed. */ if (strcmp(tc, "CP932") == 0) sc->cd = iconv_open("SJIS", fc); else if (strcmp(fc, "CP932") == 0) sc->cd = iconv_open(tc, "SJIS"); } #if defined(_WIN32) && !defined(__CYGWIN__) /* * archive_mstring on Windows directly convert multi-bytes * into archive_wstring in order not to depend on locale * so that you can do a I18N programming. This will be * used only in archive_mstring_copy_mbs_len_l so far. */ if (flag & SCONV_FROM_CHARSET) { sc->cd_w = iconv_open("UTF-8", fc); if (sc->cd_w == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) { if (strcmp(fc, "CP932") == 0) sc->cd_w = iconv_open("UTF-8", "SJIS"); } } #endif /* _WIN32 && !__CYGWIN__ */ } #endif /* HAVE_ICONV */ sc->flag = flag; /* * Set up converters. */ setup_converter(sc); return (sc); } /* * Free a string conversion object. */ static void free_sconv_object(struct archive_string_conv *sc) { free(sc->from_charset); free(sc->to_charset); archive_string_free(&sc->utftmp); #if HAVE_ICONV if (sc->cd != (iconv_t)-1) iconv_close(sc->cd); if (sc->cd_w != (iconv_t)-1) iconv_close(sc->cd_w); #endif free(sc); } #if defined(_WIN32) && !defined(__CYGWIN__) static unsigned my_atoi(const char *p) { unsigned cp; cp = 0; while (*p) { if (*p >= '0' && *p <= '9') cp = cp * 10 + (*p - '0'); else return (-1); p++; } return (cp); } /* * Translate Charset name (as used by iconv) into CodePage (as used by Windows) * Return -1 if failed. * * Note: This translation code may be insufficient. */ static struct charset { const char *name; unsigned cp; } charsets[] = { /* MUST BE SORTED! */ {"ASCII", 1252}, {"ASMO-708", 708}, {"BIG5", 950}, {"CHINESE", 936}, {"CP367", 1252}, {"CP819", 1252}, {"CP1025", 21025}, {"DOS-720", 720}, {"DOS-862", 862}, {"EUC-CN", 51936}, {"EUC-JP", 51932}, {"EUC-KR", 949}, {"EUCCN", 51936}, {"EUCJP", 51932}, {"EUCKR", 949}, {"GB18030", 54936}, {"GB2312", 936}, {"HEBREW", 1255}, {"HZ-GB-2312", 52936}, {"IBM273", 20273}, {"IBM277", 20277}, {"IBM278", 20278}, {"IBM280", 20280}, {"IBM284", 20284}, {"IBM285", 20285}, {"IBM290", 20290}, {"IBM297", 20297}, {"IBM367", 1252}, {"IBM420", 20420}, {"IBM423", 20423}, {"IBM424", 20424}, {"IBM819", 1252}, {"IBM871", 20871}, {"IBM880", 20880}, {"IBM905", 20905}, {"IBM924", 20924}, {"ISO-8859-1", 28591}, {"ISO-8859-13", 28603}, {"ISO-8859-15", 28605}, {"ISO-8859-2", 28592}, {"ISO-8859-3", 28593}, {"ISO-8859-4", 28594}, {"ISO-8859-5", 28595}, {"ISO-8859-6", 28596}, {"ISO-8859-7", 28597}, {"ISO-8859-8", 28598}, {"ISO-8859-9", 28599}, {"ISO8859-1", 28591}, {"ISO8859-13", 28603}, {"ISO8859-15", 28605}, {"ISO8859-2", 28592}, {"ISO8859-3", 28593}, {"ISO8859-4", 28594}, {"ISO8859-5", 28595}, {"ISO8859-6", 28596}, {"ISO8859-7", 28597}, {"ISO8859-8", 28598}, {"ISO8859-9", 28599}, {"JOHAB", 1361}, {"KOI8-R", 20866}, {"KOI8-U", 21866}, {"KS_C_5601-1987", 949}, {"LATIN1", 1252}, {"LATIN2", 28592}, {"MACINTOSH", 10000}, {"SHIFT-JIS", 932}, {"SHIFT_JIS", 932}, {"SJIS", 932}, {"US", 1252}, {"US-ASCII", 1252}, {"UTF-16", 1200}, {"UTF-16BE", 1201}, {"UTF-16LE", 1200}, {"UTF-8", CP_UTF8}, {"X-EUROPA", 29001}, {"X-MAC-ARABIC", 10004}, {"X-MAC-CE", 10029}, {"X-MAC-CHINESEIMP", 10008}, {"X-MAC-CHINESETRAD", 10002}, {"X-MAC-CROATIAN", 10082}, {"X-MAC-CYRILLIC", 10007}, {"X-MAC-GREEK", 10006}, {"X-MAC-HEBREW", 10005}, {"X-MAC-ICELANDIC", 10079}, {"X-MAC-JAPANESE", 10001}, {"X-MAC-KOREAN", 10003}, {"X-MAC-ROMANIAN", 10010}, {"X-MAC-THAI", 10021}, {"X-MAC-TURKISH", 10081}, {"X-MAC-UKRAINIAN", 10017}, }; static unsigned make_codepage_from_charset(const char *charset) { char cs[16]; char *p; unsigned cp; int a, b; if (charset == NULL || strlen(charset) > 15) return -1; /* Copy name to uppercase. */ p = cs; while (*charset) { char c = *charset++; if (c >= 'a' && c <= 'z') c -= 'a' - 'A'; *p++ = c; } *p++ = '\0'; cp = -1; /* Look it up in the table first, so that we can easily * override CP367, which we map to 1252 instead of 367. */ a = 0; b = sizeof(charsets)/sizeof(charsets[0]); while (b > a) { int c = (b + a) / 2; int r = strcmp(charsets[c].name, cs); if (r < 0) a = c + 1; else if (r > 0) b = c; else return charsets[c].cp; } /* If it's not in the table, try to parse it. */ switch (*cs) { case 'C': if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') { cp = my_atoi(cs + 2); } else if (strcmp(cs, "CP_ACP") == 0) cp = get_current_codepage(); else if (strcmp(cs, "CP_OEMCP") == 0) cp = get_current_oemcp(); break; case 'I': if (cs[1] == 'B' && cs[2] == 'M' && cs[3] >= '0' && cs[3] <= '9') { cp = my_atoi(cs + 3); } break; case 'W': if (strncmp(cs, "WINDOWS-", 8) == 0) { cp = my_atoi(cs + 8); if (cp != 874 && (cp < 1250 || cp > 1258)) cp = -1;/* This may invalid code. */ } break; } return (cp); } /* * Return ANSI Code Page of current locale set by setlocale(). */ static unsigned get_current_codepage(void) { char *locale, *p; unsigned cp; locale = setlocale(LC_CTYPE, NULL); if (locale == NULL) return (GetACP()); if (locale[0] == 'C' && locale[1] == '\0') return (CP_C_LOCALE); p = strrchr(locale, '.'); if (p == NULL) return (GetACP()); if (strcmp(p+1, "utf8") == 0) return CP_UTF8; cp = my_atoi(p+1); if ((int)cp <= 0) return (GetACP()); return (cp); } /* * Translation table between Locale Name and ACP/OEMCP. */ static struct { unsigned acp; unsigned ocp; const char *locale; } acp_ocp_map[] = { { 950, 950, "Chinese_Taiwan" }, { 936, 936, "Chinese_People's Republic of China" }, { 950, 950, "Chinese_Taiwan" }, { 1250, 852, "Czech_Czech Republic" }, { 1252, 850, "Danish_Denmark" }, { 1252, 850, "Dutch_Netherlands" }, { 1252, 850, "Dutch_Belgium" }, { 1252, 437, "English_United States" }, { 1252, 850, "English_Australia" }, { 1252, 850, "English_Canada" }, { 1252, 850, "English_New Zealand" }, { 1252, 850, "English_United Kingdom" }, { 1252, 437, "English_United States" }, { 1252, 850, "Finnish_Finland" }, { 1252, 850, "French_France" }, { 1252, 850, "French_Belgium" }, { 1252, 850, "French_Canada" }, { 1252, 850, "French_Switzerland" }, { 1252, 850, "German_Germany" }, { 1252, 850, "German_Austria" }, { 1252, 850, "German_Switzerland" }, { 1253, 737, "Greek_Greece" }, { 1250, 852, "Hungarian_Hungary" }, { 1252, 850, "Icelandic_Iceland" }, { 1252, 850, "Italian_Italy" }, { 1252, 850, "Italian_Switzerland" }, { 932, 932, "Japanese_Japan" }, { 949, 949, "Korean_Korea" }, { 1252, 850, "Norwegian (BokmOl)_Norway" }, { 1252, 850, "Norwegian (BokmOl)_Norway" }, { 1252, 850, "Norwegian-Nynorsk_Norway" }, { 1250, 852, "Polish_Poland" }, { 1252, 850, "Portuguese_Portugal" }, { 1252, 850, "Portuguese_Brazil" }, { 1251, 866, "Russian_Russia" }, { 1250, 852, "Slovak_Slovakia" }, { 1252, 850, "Spanish_Spain" }, { 1252, 850, "Spanish_Mexico" }, { 1252, 850, "Spanish_Spain" }, { 1252, 850, "Swedish_Sweden" }, { 1254, 857, "Turkish_Turkey" }, { 0, 0, NULL} }; /* * Return OEM Code Page of current locale set by setlocale(). */ static unsigned get_current_oemcp(void) { int i; char *locale, *p; size_t len; locale = setlocale(LC_CTYPE, NULL); if (locale == NULL) return (GetOEMCP()); if (locale[0] == 'C' && locale[1] == '\0') return (CP_C_LOCALE); p = strrchr(locale, '.'); if (p == NULL) return (GetOEMCP()); len = p - locale; for (i = 0; acp_ocp_map[i].acp; i++) { if (strncmp(acp_ocp_map[i].locale, locale, len) == 0) return (acp_ocp_map[i].ocp); } return (GetOEMCP()); } #else /* * POSIX platform does not use CodePage. */ static unsigned get_current_codepage(void) { return (-1);/* Unknown */ } static unsigned make_codepage_from_charset(const char *charset) { (void)charset; /* UNUSED */ return (-1);/* Unknown */ } static unsigned get_current_oemcp(void) { return (-1);/* Unknown */ } #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ /* * Return a string conversion object. */ static struct archive_string_conv * get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag) { struct archive_string_conv *sc; unsigned current_codepage; /* Check if we have made the sconv object. */ sc = find_sconv_object(a, fc, tc); if (sc != NULL) return (sc); if (a == NULL) current_codepage = get_current_codepage(); else current_codepage = a->current_codepage; sc = create_sconv_object(canonical_charset_name(fc), canonical_charset_name(tc), current_codepage, flag); if (sc == NULL) { if (a != NULL) archive_set_error(a, ENOMEM, "Could not allocate memory for " "a string conversion object"); return (NULL); } /* * If there is no converter for current string conversion object, * we cannot handle this conversion. */ if (sc->nconverter == 0) { if (a != NULL) { #if HAVE_ICONV archive_set_error(a, ARCHIVE_ERRNO_MISC, "iconv_open failed : Cannot handle ``%s''", (flag & SCONV_TO_CHARSET)?tc:fc); #else archive_set_error(a, ARCHIVE_ERRNO_MISC, "A character-set conversion not fully supported " "on this platform"); #endif } /* Failed; free a sconv object. */ free_sconv_object(sc); return (NULL); } /* * Success! */ if (a != NULL) add_sconv_object(a, sc); return (sc); } static const char * get_current_charset(struct archive *a) { const char *cur_charset; if (a == NULL) cur_charset = default_iconv_charset(""); else { cur_charset = default_iconv_charset(a->current_code); if (a->current_code == NULL) { a->current_code = strdup(cur_charset); a->current_codepage = get_current_codepage(); a->current_oemcp = get_current_oemcp(); } } return (cur_charset); } /* * Make and Return a string conversion object. * Return NULL if the platform does not support the specified conversion * and best_effort is 0. * If best_effort is set, A string conversion object must be returned * unless memory allocation for the object fails, but the conversion * might fail when non-ASCII code is found. */ struct archive_string_conv * archive_string_conversion_to_charset(struct archive *a, const char *charset, int best_effort) { int flag = SCONV_TO_CHARSET; if (best_effort) flag |= SCONV_BEST_EFFORT; return (get_sconv_object(a, get_current_charset(a), charset, flag)); } struct archive_string_conv * archive_string_conversion_from_charset(struct archive *a, const char *charset, int best_effort) { int flag = SCONV_FROM_CHARSET; if (best_effort) flag |= SCONV_BEST_EFFORT; return (get_sconv_object(a, charset, get_current_charset(a), flag)); } /* * archive_string_default_conversion_*_archive() are provided for Windows * platform because other archiver application use CP_OEMCP for * MultiByteToWideChar() and WideCharToMultiByte() for the filenames * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP). * So we should make a string conversion between CP_ACP and CP_OEMCP * for compatibility. */ #if defined(_WIN32) && !defined(__CYGWIN__) struct archive_string_conv * archive_string_default_conversion_for_read(struct archive *a) { const char *cur_charset = get_current_charset(a); char oemcp[16]; /* NOTE: a check of cur_charset is unneeded but we need * that get_current_charset() has been surely called at * this time whatever C compiler optimized. */ if (cur_charset != NULL && (a->current_codepage == CP_C_LOCALE || a->current_codepage == a->current_oemcp)) return (NULL);/* no conversion. */ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp); /* Make sure a null termination must be set. */ oemcp[sizeof(oemcp)-1] = '\0'; return (get_sconv_object(a, oemcp, cur_charset, SCONV_FROM_CHARSET)); } struct archive_string_conv * archive_string_default_conversion_for_write(struct archive *a) { const char *cur_charset = get_current_charset(a); char oemcp[16]; /* NOTE: a check of cur_charset is unneeded but we need * that get_current_charset() has been surely called at * this time whatever C compiler optimized. */ if (cur_charset != NULL && (a->current_codepage == CP_C_LOCALE || a->current_codepage == a->current_oemcp)) return (NULL);/* no conversion. */ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp); /* Make sure a null termination must be set. */ oemcp[sizeof(oemcp)-1] = '\0'; return (get_sconv_object(a, cur_charset, oemcp, SCONV_TO_CHARSET)); } #else struct archive_string_conv * archive_string_default_conversion_for_read(struct archive *a) { (void)a; /* UNUSED */ return (NULL); } struct archive_string_conv * archive_string_default_conversion_for_write(struct archive *a) { (void)a; /* UNUSED */ return (NULL); } #endif /* * Dispose of all character conversion objects in the archive object. */ void archive_string_conversion_free(struct archive *a) { struct archive_string_conv *sc; struct archive_string_conv *sc_next; for (sc = a->sconv; sc != NULL; sc = sc_next) { sc_next = sc->next; free_sconv_object(sc); } a->sconv = NULL; free(a->current_code); a->current_code = NULL; } /* * Return a conversion charset name. */ const char * archive_string_conversion_charset_name(struct archive_string_conv *sc) { if (sc->flag & SCONV_TO_CHARSET) return (sc->to_charset); else return (sc->from_charset); } /* * Change the behavior of a string conversion. */ void archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt) { switch (opt) { /* * A filename in UTF-8 was made with libarchive 2.x in a wrong * assumption that wchar_t was Unicode. * This option enables simulating the assumption in order to read * that filename correctly. */ case SCONV_SET_OPT_UTF8_LIBARCHIVE2X: #if (defined(_WIN32) && !defined(__CYGWIN__)) \ || defined(__STDC_ISO_10646__) || defined(__APPLE__) /* * Nothing to do for it since wchar_t on these platforms * is really Unicode. */ (void)sc; /* UNUSED */ #else if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) { sc->flag |= SCONV_UTF8_LIBARCHIVE_2; /* Set up string converters. */ setup_converter(sc); } #endif break; case SCONV_SET_OPT_NORMALIZATION_C: if ((sc->flag & SCONV_NORMALIZATION_C) == 0) { sc->flag |= SCONV_NORMALIZATION_C; sc->flag &= ~SCONV_NORMALIZATION_D; /* Set up string converters. */ setup_converter(sc); } break; case SCONV_SET_OPT_NORMALIZATION_D: #if defined(HAVE_ICONV) /* * If iconv will take the string, do not change the * setting of the normalization. */ if (!(sc->flag & SCONV_WIN_CP) && (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8))) break; #endif if ((sc->flag & SCONV_NORMALIZATION_D) == 0) { sc->flag |= SCONV_NORMALIZATION_D; sc->flag &= ~SCONV_NORMALIZATION_C; /* Set up string converters. */ setup_converter(sc); } break; default: break; } } /* * * Copy one archive_string to another in locale conversion. * * archive_strncat_l(); * archive_strncpy_l(); * */ static size_t mbsnbytes(const void *_p, size_t n) { size_t s; const char *p, *pp; if (_p == NULL) return (0); p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; while (s < n && *pp) { pp++; s++; } return (s); } static size_t utf16nbytes(const void *_p, size_t n) { size_t s; const char *p, *pp; if (_p == NULL) return (0); p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; pp = p; n >>= 1; while (s < n && (pp[0] || pp[1])) { pp += 2; s++; } return (s<<1); } int archive_strncpy_l(struct archive_string *as, const void *_p, size_t n, struct archive_string_conv *sc) { as->length = 0; return (archive_strncat_l(as, _p, n, sc)); } int archive_strncat_l(struct archive_string *as, const void *_p, size_t n, struct archive_string_conv *sc) { const void *s; size_t length = 0; int i, r = 0, r2; if (_p != NULL && n > 0) { if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) length = utf16nbytes(_p, n); else length = mbsnbytes(_p, n); } /* We must allocate memory even if there is no data for conversion * or copy. This simulates archive_string_append behavior. */ if (length == 0) { int tn = 1; if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) tn = 2; if (archive_string_ensure(as, as->length + tn) == NULL) return (-1); as->s[as->length] = 0; if (tn == 2) as->s[as->length+1] = 0; return (0); } /* * If sc is NULL, we just make a copy. */ if (sc == NULL) { if (archive_string_append(as, _p, length) == NULL) return (-1);/* No memory */ return (0); } s = _p; i = 0; if (sc->nconverter > 1) { sc->utftmp.length = 0; r2 = sc->converter[0](&(sc->utftmp), s, length, sc); if (r2 != 0 && errno == ENOMEM) return (r2); if (r > r2) r = r2; s = sc->utftmp.s; length = sc->utftmp.length; ++i; } r2 = sc->converter[i](as, s, length, sc); if (r > r2) r = r2; return (r); } #if HAVE_ICONV /* * Return -1 if conversion fails. */ static int iconv_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { ICONV_CONST char *itp; size_t remaining; iconv_t cd; char *outp; size_t avail, bs; int return_value = 0; /* success */ int to_size, from_size; if (sc->flag & SCONV_TO_UTF16) to_size = 2; else to_size = 1; if (sc->flag & SCONV_FROM_UTF16) from_size = 2; else from_size = 1; if (archive_string_ensure(as, as->length + length*2+to_size) == NULL) return (-1); cd = sc->cd; itp = (char *)(uintptr_t)_p; remaining = length; outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; while (remaining >= (size_t)from_size) { size_t result = iconv(cd, &itp, &remaining, &outp, &avail); if (result != (size_t)-1) break; /* Conversion completed. */ if (errno == EILSEQ || errno == EINVAL) { /* * If an output charset is UTF-8 or UTF-16BE/LE, * unknown character should be U+FFFD * (replacement character). */ if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) { size_t rbytes; if (sc->flag & SCONV_TO_UTF8) rbytes = sizeof(utf8_replacement_char); else rbytes = 2; if (avail < rbytes) { as->length = outp - as->s; bs = as->buffer_length + (remaining * to_size) + rbytes; if (NULL == archive_string_ensure(as, bs)) return (-1); outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; } if (sc->flag & SCONV_TO_UTF8) memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char)); else if (sc->flag & SCONV_TO_UTF16BE) archive_be16enc(outp, UNICODE_R_CHAR); else archive_le16enc(outp, UNICODE_R_CHAR); outp += rbytes; avail -= rbytes; } else { /* Skip the illegal input bytes. */ *outp++ = '?'; avail--; } itp += from_size; remaining -= from_size; return_value = -1; /* failure */ } else { /* E2BIG no output buffer, * Increase an output buffer. */ as->length = outp - as->s; bs = as->buffer_length + remaining * 2; if (NULL == archive_string_ensure(as, bs)) return (-1); outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; } } as->length = outp - as->s; as->s[as->length] = 0; if (to_size == 2) as->s[as->length+1] = 0; return (return_value); } #endif /* HAVE_ICONV */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Translate a string from a some CodePage to an another CodePage by * Windows APIs, and copy the result. Return -1 if conversion fails. */ static int strncat_in_codepage(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { const char *s = (const char *)_p; struct archive_wstring aws; size_t l; int r, saved_flag; archive_string_init(&aws); saved_flag = sc->flag; sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C); r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc); sc->flag = saved_flag; if (r != 0) { archive_wstring_free(&aws); if (errno != ENOMEM) archive_string_append(as, s, length); return (-1); } l = as->length; r = archive_string_append_from_wcs_in_codepage( as, aws.s, aws.length, sc); if (r != 0 && errno != ENOMEM && l == as->length) archive_string_append(as, s, length); archive_wstring_free(&aws); return (r); } /* * Test whether MBS ==> WCS is okay. */ static int invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) { const char *p = (const char *)_p; unsigned codepage; DWORD mbflag = MB_ERR_INVALID_CHARS; if (sc->flag & SCONV_FROM_CHARSET) codepage = sc->to_cp; else codepage = sc->from_cp; if (codepage == CP_C_LOCALE) return (0); if (codepage != CP_UTF8) mbflag |= MB_PRECOMPOSED; if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0) return (-1); /* Invalid */ return (0); /* Okay */ } #else /* * Test whether MBS ==> WCS is okay. */ static int invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) { const char *p = (const char *)_p; size_t r; #if HAVE_MBRTOWC mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ mbtowc(NULL, NULL, 0); #endif while (n) { wchar_t wc; #if HAVE_MBRTOWC r = mbrtowc(&wc, p, n, &shift_state); #else r = mbtowc(&wc, p, n); #endif if (r == (size_t)-1 || r == (size_t)-2) return (-1);/* Invalid. */ if (r == 0) break; p += r; n -= r; } (void)sc; /* UNUSED */ return (0); /* All Okey. */ } #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ /* * Basically returns -1 because we cannot make a conversion of charset * without iconv but in some cases this would return 0. * Returns 0 if all copied characters are ASCII. * Returns 0 if both from-locale and to-locale are the same and those * can be WCS with no error. */ static int best_effort_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { size_t remaining; const uint8_t *itp; int return_value = 0; /* success */ /* * If both from-locale and to-locale is the same, this makes a copy. * And then this checks all copied MBS can be WCS if so returns 0. */ if (sc->same) { if (archive_string_append(as, _p, length) == NULL) return (-1);/* No memory */ return (invalid_mbs(_p, length, sc)); } /* * If a character is ASCII, this just copies it. If not, this * assigns '?' character instead but in UTF-8 locale this assigns * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD, * a Replacement Character in Unicode. */ remaining = length; itp = (const uint8_t *)_p; while (*itp && remaining > 0) { if (*itp > 127) { // Non-ASCII: Substitute with suitable replacement if (sc->flag & SCONV_TO_UTF8) { if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) { __archive_errx(1, "Out of memory"); } } else { archive_strappend_char(as, '?'); } return_value = -1; } else { archive_strappend_char(as, *itp); } ++itp; } return (return_value); } /* * Unicode conversion functions. * - UTF-8 <===> UTF-8 in removing surrogate pairs. * - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs. * - UTF-8 made by libarchive 2.x ===> UTF-8. * - UTF-16BE <===> UTF-8. * */ /* * Utility to convert a single UTF-8 sequence. * * Usually return used bytes, return used byte in negative value when * a unicode character is replaced with U+FFFD. * See also http://unicode.org/review/pr-121.html Public Review Issue #121 * Recommended Practice for Replacement Characters. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch, i; int cnt; uint32_t wc; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalid sequence or there are not plenty bytes. */ if ((int)n < cnt) { cnt = (int)n; for (i = 1; i < cnt; i++) { if ((s[i] & 0xc0) != 0x80) { cnt = i; break; } } goto invalid_sequence; } /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) { cnt = 1; goto invalid_sequence; } *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) { cnt = 1; goto invalid_sequence; } if ((s[2] & 0xc0) != 0x80) { cnt = 2; goto invalid_sequence; } wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) goto invalid_sequence;/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) { cnt = 1; goto invalid_sequence; } if ((s[2] & 0xc0) != 0x80) { cnt = 2; goto invalid_sequence; } if ((s[3] & 0xc0) != 0x80) { cnt = 3; goto invalid_sequence; } wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) goto invalid_sequence;/* Overlong sequence. */ break; default: /* Others are all invalid sequence. */ if (ch == 0xc0 || ch == 0xc1) cnt = 2; else if (ch >= 0xf5 && ch <= 0xf7) cnt = 4; else if (ch >= 0xf8 && ch <= 0xfb) cnt = 5; else if (ch == 0xfc || ch == 0xfd) cnt = 6; else cnt = 1; if ((int)n < cnt) cnt = (int)n; for (i = 1; i < cnt; i++) { if ((s[i] & 0xc0) != 0x80) { cnt = i; break; } } goto invalid_sequence; } /* The code point larger than 0x10FFFF is not legal * Unicode values. */ if (wc > UNICODE_MAX) goto invalid_sequence; /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); invalid_sequence: *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */ return (cnt * -1); } static int utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { int cnt; cnt = _utf8_to_unicode(pwc, s, n); /* Any of Surrogate pair is not legal Unicode values. */ if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc)) return (-3); return (cnt); } static inline uint32_t combine_surrogate_pair(uint32_t uc, uint32_t uc2) { uc -= 0xD800; uc *= 0x400; uc += uc2 - 0xDC00; uc += 0x10000; return (uc); } /* * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in * removing surrogate pairs. * * CESU-8: The Compatibility Encoding Scheme for UTF-16. * * Usually return used bytes, return used byte in negative value when * a unicode character is replaced with U+FFFD. */ static int cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n) { uint32_t wc = 0; int cnt; cnt = _utf8_to_unicode(&wc, s, n); if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) { uint32_t wc2 = 0; if (n - 3 < 3) { /* Invalid byte sequence. */ goto invalid_sequence; } cnt = _utf8_to_unicode(&wc2, s+3, n-3); if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) { /* Invalid byte sequence. */ goto invalid_sequence; } wc = combine_surrogate_pair(wc, wc2); cnt = 6; } else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) { /* Invalid byte sequence. */ goto invalid_sequence; } *pwc = wc; return (cnt); invalid_sequence: *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */ if (cnt > 0) cnt *= -1; return (cnt); } /* * Convert a Unicode code point to a single UTF-8 sequence. * * NOTE:This function does not check if the Unicode is legal or not. * Please you definitely check it before calling this. */ static size_t unicode_to_utf8(char *p, size_t remaining, uint32_t uc) { char *_p = p; /* Invalid Unicode char maps to Replacement character */ if (uc > UNICODE_MAX) uc = UNICODE_R_CHAR; /* Translate code point to UTF8 */ if (uc <= 0x7f) { if (remaining == 0) return (0); *p++ = (char)uc; } else if (uc <= 0x7ff) { if (remaining < 2) return (0); *p++ = 0xc0 | ((uc >> 6) & 0x1f); *p++ = 0x80 | (uc & 0x3f); } else if (uc <= 0xffff) { if (remaining < 3) return (0); *p++ = 0xe0 | ((uc >> 12) & 0x0f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); } else { if (remaining < 4) return (0); *p++ = 0xf0 | ((uc >> 18) & 0x07); *p++ = 0x80 | ((uc >> 12) & 0x3f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); } return (p - _p); } static int utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n) { return (utf16_to_unicode(pwc, s, n, 1)); } static int utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n) { return (utf16_to_unicode(pwc, s, n, 0)); } static int utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be) { const char *utf16 = s; unsigned uc; if (n == 0) return (0); if (n == 1) { /* set the Replacement Character instead. */ *pwc = UNICODE_R_CHAR; return (-1); } if (be) uc = archive_be16dec(utf16); else uc = archive_le16dec(utf16); utf16 += 2; /* If this is a surrogate pair, assemble the full code point.*/ if (IS_HIGH_SURROGATE_LA(uc)) { unsigned uc2; if (n >= 4) { if (be) uc2 = archive_be16dec(utf16); else uc2 = archive_le16dec(utf16); } else uc2 = 0; if (IS_LOW_SURROGATE_LA(uc2)) { uc = combine_surrogate_pair(uc, uc2); utf16 += 2; } else { /* Undescribed code point should be U+FFFD * (replacement character). */ *pwc = UNICODE_R_CHAR; return (-2); } } /* * Surrogate pair values(0xd800 through 0xdfff) are only * used by UTF-16, so, after above calculation, the code * must not be surrogate values, and Unicode has no codes * larger than 0x10ffff. Thus, those are not legal Unicode * values. */ if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) { /* Undescribed code point should be U+FFFD * (replacement character). */ *pwc = UNICODE_R_CHAR; return (((int)(utf16 - s)) * -1); } *pwc = uc; return ((int)(utf16 - s)); } static size_t unicode_to_utf16be(char *p, size_t remaining, uint32_t uc) { char *utf16 = p; if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ if (remaining < 4) return (0); uc -= 0x10000; archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00); return (4); } else { if (remaining < 2) return (0); archive_be16enc(utf16, uc); return (2); } } static size_t unicode_to_utf16le(char *p, size_t remaining, uint32_t uc) { char *utf16 = p; if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ if (remaining < 4) return (0); uc -= 0x10000; archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00); return (4); } else { if (remaining < 2) return (0); archive_le16enc(utf16, uc); return (2); } } /* * Copy UTF-8 string in checking surrogate pair. * If any surrogate pair are found, it would be canonicalized. */ static int strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s; char *p, *endp; int n, ret = 0; (void)sc; /* UNUSED */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); s = (const char *)_p; p = as->s + as->length; endp = as->s + as->buffer_length -1; do { uint32_t uc; const char *ss = s; size_t w; /* * Forward byte sequence until a conversion of that is needed. */ while ((n = utf8_to_unicode(&uc, s, len)) > 0) { s += n; len -= n; } if (ss < s) { if (p + (s - ss) > endp) { as->length = p - as->s; if (archive_string_ensure(as, as->buffer_length + len + 1) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length -1; } memcpy(p, ss, s - ss); p += s - ss; } /* * If n is negative, current byte sequence needs a replacement. */ if (n < 0) { if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) { /* Current byte sequence may be CESU-8. */ n = cesu8_to_unicode(&uc, s, len); } if (n < 0) { ret = -1; n *= -1;/* Use a replaced unicode character. */ } /* Rebuild UTF-8 byte sequence. */ while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) { as->length = p - as->s; if (archive_string_ensure(as, as->buffer_length + len + 1) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length -1; } p += w; s += n; len -= n; } } while (n > 0); as->length = p - as->s; as->s[as->length] = '\0'; return (ret); } static int archive_string_append_unicode(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s; char *p, *endp; uint32_t uc; size_t w; int n, ret = 0, ts, tm; int (*parse)(uint32_t *, const char *, size_t); size_t (*unparse)(char *, size_t, uint32_t); if (sc->flag & SCONV_TO_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_TO_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else if (sc->flag & SCONV_TO_UTF8) { unparse = unicode_to_utf8; ts = 1; } else { /* * This case is going to be converted to another * character-set through iconv. */ if (sc->flag & SCONV_FROM_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_FROM_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else { unparse = unicode_to_utf8; ts = 1; } } if (sc->flag & SCONV_FROM_UTF16BE) { parse = utf16be_to_unicode; tm = 1; } else if (sc->flag & SCONV_FROM_UTF16LE) { parse = utf16le_to_unicode; tm = 1; } else { parse = cesu8_to_unicode; tm = ts; } if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) return (-1); s = (const char *)_p; p = as->s + as->length; endp = as->s + as->buffer_length - ts; while ((n = parse(&uc, s, len)) != 0) { if (n < 0) { /* Use a replaced unicode character. */ n *= -1; ret = -1; } s += n; len -= n; while ((w = unparse(p, endp - p, uc)) == 0) { /* There is not enough output buffer so * we have to expand it. */ as->length = p - as->s; if (archive_string_ensure(as, as->buffer_length + len * tm + ts) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length - ts; } p += w; } as->length = p - as->s; as->s[as->length] = '\0'; if (ts == 2) as->s[as->length+1] = '\0'; return (ret); } /* * Following Constants for Hangul compositions this information comes from * Unicode Standard Annex #15 http://unicode.org/reports/tr15/ */ #define HC_SBASE 0xAC00 #define HC_LBASE 0x1100 #define HC_VBASE 0x1161 #define HC_TBASE 0x11A7 #define HC_LCOUNT 19 #define HC_VCOUNT 21 #define HC_TCOUNT 28 #define HC_NCOUNT (HC_VCOUNT * HC_TCOUNT) #define HC_SCOUNT (HC_LCOUNT * HC_NCOUNT) static uint32_t get_nfc(uint32_t uc, uint32_t uc2) { int t, b; t = 0; b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1; while (b >= t) { int m = (t + b) / 2; if (u_composition_table[m].cp1 < uc) t = m + 1; else if (u_composition_table[m].cp1 > uc) b = m - 1; else if (u_composition_table[m].cp2 < uc2) t = m + 1; else if (u_composition_table[m].cp2 > uc2) b = m - 1; else return (u_composition_table[m].nfc); } return (0); } #define FDC_MAX 10 /* The maximum number of Following Decomposable * Characters. */ /* * Update first code point. */ #define UPDATE_UC(new_uc) do { \ uc = new_uc; \ ucptr = NULL; \ } while (0) /* * Replace first code point with second code point. */ #define REPLACE_UC_WITH_UC2() do { \ uc = uc2; \ ucptr = uc2ptr; \ n = n2; \ } while (0) #define EXPAND_BUFFER() do { \ as->length = p - as->s; \ if (archive_string_ensure(as, \ as->buffer_length + len * tm + ts) == NULL)\ return (-1); \ p = as->s + as->length; \ endp = as->s + as->buffer_length - ts; \ } while (0) #define UNPARSE(p, endp, uc) do { \ while ((w = unparse(p, (endp) - (p), uc)) == 0) {\ EXPAND_BUFFER(); \ } \ p += w; \ } while (0) /* * Write first code point. * If the code point has not be changed from its original code, * this just copies it from its original buffer pointer. * If not, this converts it to UTF-8 byte sequence and copies it. */ #define WRITE_UC() do { \ if (ucptr) { \ if (p + n > endp) \ EXPAND_BUFFER(); \ switch (n) { \ case 4: \ *p++ = *ucptr++; \ /* FALL THROUGH */ \ case 3: \ *p++ = *ucptr++; \ /* FALL THROUGH */ \ case 2: \ *p++ = *ucptr++; \ /* FALL THROUGH */ \ case 1: \ *p++ = *ucptr; \ break; \ } \ ucptr = NULL; \ } else { \ UNPARSE(p, endp, uc); \ } \ } while (0) /* * Collect following decomposable code points. */ #define COLLECT_CPS(start) do { \ int _i; \ for (_i = start; _i < FDC_MAX ; _i++) { \ nx = parse(&ucx[_i], s, len); \ if (nx <= 0) \ break; \ cx = CCC(ucx[_i]); \ if (cl >= cx && cl != 228 && cx != 228)\ break; \ s += nx; \ len -= nx; \ cl = cx; \ ccx[_i] = cx; \ } \ if (_i >= FDC_MAX) { \ ret = -1; \ ucx_size = FDC_MAX; \ } else \ ucx_size = _i; \ } while (0) /* * Normalize UTF-8/UTF-16BE characters to Form C and copy the result. * * TODO: Convert composition exclusions, which are never converted * from NFC,NFD,NFKC and NFKD, to Form C. */ static int archive_string_normalize_C(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s = (const char *)_p; char *p, *endp; uint32_t uc, uc2; size_t w; int always_replace, n, n2, ret = 0, spair, ts, tm; int (*parse)(uint32_t *, const char *, size_t); size_t (*unparse)(char *, size_t, uint32_t); always_replace = 1; ts = 1;/* text size. */ if (sc->flag & SCONV_TO_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; if (sc->flag & SCONV_FROM_UTF16BE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; if (sc->flag & SCONV_FROM_UTF16LE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF8) { unparse = unicode_to_utf8; if (sc->flag & SCONV_FROM_UTF8) always_replace = 0; } else { /* * This case is going to be converted to another * character-set through iconv. */ always_replace = 0; if (sc->flag & SCONV_FROM_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_FROM_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else { unparse = unicode_to_utf8; } } if (sc->flag & SCONV_FROM_UTF16BE) { parse = utf16be_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else if (sc->flag & SCONV_FROM_UTF16LE) { parse = utf16le_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else { parse = cesu8_to_unicode; tm = ts; spair = 6;/* surrogate pair size in UTF-8. */ } if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length - ts; while ((n = parse(&uc, s, len)) != 0) { const char *ucptr, *uc2ptr; if (n < 0) { /* Use a replaced unicode character. */ UNPARSE(p, endp, uc); s += n*-1; len -= n*-1; ret = -1; continue; } else if (n == spair || always_replace) /* uc is converted from a surrogate pair. * this should be treated as a changed code. */ ucptr = NULL; else ucptr = s; s += n; len -= n; /* Read second code point. */ while ((n2 = parse(&uc2, s, len)) > 0) { uint32_t ucx[FDC_MAX]; int ccx[FDC_MAX]; int cl, cx, i, nx, ucx_size; int LIndex,SIndex; uint32_t nfc; if (n2 == spair || always_replace) /* uc2 is converted from a surrogate pair. * this should be treated as a changed code. */ uc2ptr = NULL; else uc2ptr = s; s += n2; len -= n2; /* * If current second code point is out of decomposable * code points, finding compositions is unneeded. */ if (!IS_DECOMPOSABLE_BLOCK(uc2)) { WRITE_UC(); REPLACE_UC_WITH_UC2(); continue; } /* * Try to combine current code points. */ /* * We have to combine Hangul characters according to * http://uniicode.org/reports/tr15/#Hangul */ if (0 <= (LIndex = uc - HC_LBASE) && LIndex < HC_LCOUNT) { /* * Hangul Composition. * 1. Two current code points are L and V. */ int VIndex = uc2 - HC_VBASE; if (0 <= VIndex && VIndex < HC_VCOUNT) { /* Make syllable of form LV. */ UPDATE_UC(HC_SBASE + (LIndex * HC_VCOUNT + VIndex) * HC_TCOUNT); } else { WRITE_UC(); REPLACE_UC_WITH_UC2(); } continue; } else if (0 <= (SIndex = uc - HC_SBASE) && SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) { /* * Hangul Composition. * 2. Two current code points are LV and T. */ int TIndex = uc2 - HC_TBASE; if (0 < TIndex && TIndex < HC_TCOUNT) { /* Make syllable of form LVT. */ UPDATE_UC(uc + TIndex); } else { WRITE_UC(); REPLACE_UC_WITH_UC2(); } continue; } else if ((nfc = get_nfc(uc, uc2)) != 0) { /* A composition to current code points * is found. */ UPDATE_UC(nfc); continue; } else if ((cl = CCC(uc2)) == 0) { /* Clearly 'uc2' the second code point is not * a decomposable code. */ WRITE_UC(); REPLACE_UC_WITH_UC2(); continue; } /* * Collect following decomposable code points. */ cx = 0; ucx[0] = uc2; ccx[0] = cl; COLLECT_CPS(1); /* * Find a composed code in the collected code points. */ i = 1; while (i < ucx_size) { int j; if ((nfc = get_nfc(uc, ucx[i])) == 0) { i++; continue; } /* * nfc is composed of uc and ucx[i]. */ UPDATE_UC(nfc); /* * Remove ucx[i] by shifting * following code points. */ for (j = i; j+1 < ucx_size; j++) { ucx[j] = ucx[j+1]; ccx[j] = ccx[j+1]; } ucx_size --; /* * Collect following code points blocked * by ucx[i] the removed code point. */ if (ucx_size > 0 && i == ucx_size && nx > 0 && cx == cl) { cl = ccx[ucx_size-1]; COLLECT_CPS(ucx_size); } /* * Restart finding a composed code with * the updated uc from the top of the * collected code points. */ i = 0; } /* * Apparently the current code points are not * decomposed characters or already composed. */ WRITE_UC(); for (i = 0; i < ucx_size; i++) UNPARSE(p, endp, ucx[i]); /* * Flush out remaining canonical combining characters. */ if (nx > 0 && cx == cl && len > 0) { while ((nx = parse(&ucx[0], s, len)) > 0) { cx = CCC(ucx[0]); if (cl > cx) break; s += nx; len -= nx; cl = cx; UNPARSE(p, endp, ucx[0]); } } break; } if (n2 < 0) { WRITE_UC(); /* Use a replaced unicode character. */ UNPARSE(p, endp, uc2); s += n2*-1; len -= n2*-1; ret = -1; continue; } else if (n2 == 0) { WRITE_UC(); break; } } as->length = p - as->s; as->s[as->length] = '\0'; if (ts == 2) as->s[as->length+1] = '\0'; return (ret); } static int get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc) { int t, b; /* * These are not converted to NFD on Mac OS. */ if ((uc >= 0x2000 && uc <= 0x2FFF) || (uc >= 0xF900 && uc <= 0xFAFF) || (uc >= 0x2F800 && uc <= 0x2FAFF)) return (0); /* * Those code points are not converted to NFD on Mac OS. * I do not know the reason because it is undocumented. * NFC NFD * 1109A ==> 11099 110BA * 1109C ==> 1109B 110BA * 110AB ==> 110A5 110BA */ if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB) return (0); t = 0; b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1; while (b >= t) { int m = (t + b) / 2; if (u_decomposition_table[m].nfc < uc) t = m + 1; else if (u_decomposition_table[m].nfc > uc) b = m - 1; else { *cp1 = u_decomposition_table[m].cp1; *cp2 = u_decomposition_table[m].cp2; return (1); } } return (0); } #define REPLACE_UC_WITH(cp) do { \ uc = cp; \ ucptr = NULL; \ } while (0) /* * Normalize UTF-8 characters to Form D and copy the result. */ static int archive_string_normalize_D(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s = (const char *)_p; char *p, *endp; uint32_t uc, uc2; size_t w; int always_replace, n, n2, ret = 0, spair, ts, tm; int (*parse)(uint32_t *, const char *, size_t); size_t (*unparse)(char *, size_t, uint32_t); always_replace = 1; ts = 1;/* text size. */ if (sc->flag & SCONV_TO_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; if (sc->flag & SCONV_FROM_UTF16BE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; if (sc->flag & SCONV_FROM_UTF16LE) always_replace = 0; } else if (sc->flag & SCONV_TO_UTF8) { unparse = unicode_to_utf8; if (sc->flag & SCONV_FROM_UTF8) always_replace = 0; } else { /* * This case is going to be converted to another * character-set through iconv. */ always_replace = 0; if (sc->flag & SCONV_FROM_UTF16BE) { unparse = unicode_to_utf16be; ts = 2; } else if (sc->flag & SCONV_FROM_UTF16LE) { unparse = unicode_to_utf16le; ts = 2; } else { unparse = unicode_to_utf8; } } if (sc->flag & SCONV_FROM_UTF16BE) { parse = utf16be_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else if (sc->flag & SCONV_FROM_UTF16LE) { parse = utf16le_to_unicode; tm = 1; spair = 4;/* surrogate pair size in UTF-16. */ } else { parse = cesu8_to_unicode; tm = ts; spair = 6;/* surrogate pair size in UTF-8. */ } if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) return (-1); p = as->s + as->length; endp = as->s + as->buffer_length - ts; while ((n = parse(&uc, s, len)) != 0) { const char *ucptr; uint32_t cp1, cp2; int SIndex; struct { uint32_t uc; int ccc; } fdc[FDC_MAX]; int fdi, fdj; int ccc; check_first_code: if (n < 0) { /* Use a replaced unicode character. */ UNPARSE(p, endp, uc); s += n*-1; len -= n*-1; ret = -1; continue; } else if (n == spair || always_replace) /* uc is converted from a surrogate pair. * this should be treated as a changed code. */ ucptr = NULL; else ucptr = s; s += n; len -= n; /* Hangul Decomposition. */ if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) { int L = HC_LBASE + SIndex / HC_NCOUNT; int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT; int T = HC_TBASE + SIndex % HC_TCOUNT; REPLACE_UC_WITH(L); WRITE_UC(); REPLACE_UC_WITH(V); WRITE_UC(); if (T != HC_TBASE) { REPLACE_UC_WITH(T); WRITE_UC(); } continue; } if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) { WRITE_UC(); continue; } fdi = 0; while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) { int k; for (k = fdi; k > 0; k--) fdc[k] = fdc[k-1]; fdc[0].ccc = CCC(cp2); fdc[0].uc = cp2; fdi++; REPLACE_UC_WITH(cp1); } /* Read following code points. */ while ((n2 = parse(&uc2, s, len)) > 0 && (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) { int j, k; s += n2; len -= n2; for (j = 0; j < fdi; j++) { if (fdc[j].ccc > ccc) break; } if (j < fdi) { for (k = fdi; k > j; k--) fdc[k] = fdc[k-1]; fdc[j].ccc = ccc; fdc[j].uc = uc2; } else { fdc[fdi].ccc = ccc; fdc[fdi].uc = uc2; } fdi++; } WRITE_UC(); for (fdj = 0; fdj < fdi; fdj++) { REPLACE_UC_WITH(fdc[fdj].uc); WRITE_UC(); } if (n2 == 0) break; REPLACE_UC_WITH(uc2); n = n2; goto check_first_code; } as->length = p - as->s; as->s[as->length] = '\0'; if (ts == 2) as->s[as->length+1] = '\0'; return (ret); } /* * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption * that WCS is Unicode. It is true for several platforms but some are false. * And then people who did not use UTF-8 locale on the non Unicode WCS * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those * now cannot get right filename from libarchive 3.x and later since we * fixed the wrong assumption and it is incompatible to older its versions. * So we provide special option, "compat-2x.x", for resolving it. * That option enable the string conversion of libarchive 2.x. * * Translates the wrong UTF-8 string made by libarchive 2.x into current * locale character set and appends to the archive_string. * Note: returns -1 if conversion fails. */ static int strncat_from_utf8_libarchive2(struct archive_string *as, const void *_p, size_t len, struct archive_string_conv *sc) { const char *s; int n; char *p; char *end; uint32_t unicode; #if HAVE_WCRTOMB mbstate_t shift_state; memset(&shift_state, 0, sizeof(shift_state)); #else /* Clear the shift state before starting. */ wctomb(NULL, L'\0'); #endif (void)sc; /* UNUSED */ /* * Allocate buffer for MBS. * We need this allocation here since it is possible that * as->s is still NULL. */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); s = (const char *)_p; p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) { wchar_t wc; if (p >= end) { as->length = p - as->s; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, as->length + max(len * 2, (size_t)MB_CUR_MAX) + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; } /* * As libarchive 2.x, translates the UTF-8 characters into * wide-characters in the assumption that WCS is Unicode. */ if (n < 0) { n *= -1; wc = L'?'; } else wc = (wchar_t)unicode; s += n; len -= n; /* * Translates the wide-character into the current locale MBS. */ #if HAVE_WCRTOMB n = (int)wcrtomb(p, wc, &shift_state); #else n = (int)wctomb(p, wc); #endif if (n == -1) return (-1); p += n; } as->length = p - as->s; as->s[as->length] = '\0'; return (0); } /* * Conversion functions between current locale dependent MBS and UTF-16BE. * strncat_from_utf16be() : UTF-16BE --> MBS * strncat_to_utf16be() : MBS --> UTF16BE */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Convert a UTF-16BE/LE string to current locale and copy the result. * Return -1 if conversion fails. */ static int win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc, int be) { struct archive_string tmp; const char *u16; int ll; BOOL defchar; char *mbs; size_t mbs_size, b; int ret = 0; bytes &= ~1; if (archive_string_ensure(as, as->length + bytes +1) == NULL) return (-1); mbs = as->s + as->length; mbs_size = as->buffer_length - as->length -1; if (sc->to_cp == CP_C_LOCALE) { /* * "C" locale special process. */ u16 = _p; ll = 0; for (b = 0; b < bytes; b += 2) { uint16_t val; if (be) val = archive_be16dec(u16+b); else val = archive_le16dec(u16+b); if (val > 255) { *mbs++ = '?'; ret = -1; } else *mbs++ = (char)(val&0xff); ll++; } as->length += ll; as->s[as->length] = '\0'; return (ret); } archive_string_init(&tmp); if (be) { if (is_big_endian()) { u16 = _p; } else { if (archive_string_ensure(&tmp, bytes+2) == NULL) return (-1); memcpy(tmp.s, _p, bytes); for (b = 0; b < bytes; b += 2) { uint16_t val = archive_be16dec(tmp.s+b); archive_le16enc(tmp.s+b, val); } u16 = tmp.s; } } else { if (!is_big_endian()) { u16 = _p; } else { if (archive_string_ensure(&tmp, bytes+2) == NULL) return (-1); memcpy(tmp.s, _p, bytes); for (b = 0; b < bytes; b += 2) { uint16_t val = archive_le16dec(tmp.s+b); archive_be16enc(tmp.s+b, val); } u16 = tmp.s; } } do { defchar = 0; ll = WideCharToMultiByte(sc->to_cp, 0, (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size, NULL, &defchar); /* Exit loop if we succeeded */ if (ll != 0 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { break; } /* Else expand buffer and loop to try again. */ ll = WideCharToMultiByte(sc->to_cp, 0, (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL); if (archive_string_ensure(as, ll +1) == NULL) return (-1); mbs = as->s + as->length; mbs_size = as->buffer_length - as->length -1; } while (1); archive_string_free(&tmp); as->length += ll; as->s[as->length] = '\0'; if (ll == 0 || defchar) ret = -1; return (ret); } static int win_strncat_from_utf16be(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (win_strncat_from_utf16(as, _p, bytes, sc, 1)); } static int win_strncat_from_utf16le(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (win_strncat_from_utf16(as, _p, bytes, sc, 0)); } static int is_big_endian(void) { uint16_t d = 1; return (archive_be16dec(&d) == 1); } /* * Convert a current locale string to UTF-16BE/LE and copy the result. * Return -1 if conversion fails. */ static int win_strncat_to_utf16(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc, int bigendian) { const char *s = (const char *)_p; char *u16; size_t count, avail; if (archive_string_ensure(as16, as16->length + (length + 1) * 2) == NULL) return (-1); u16 = as16->s + as16->length; avail = as16->buffer_length - 2; if (sc->from_cp == CP_C_LOCALE) { /* * "C" locale special process. */ count = 0; while (count < length && *s) { if (bigendian) archive_be16enc(u16, *s); else archive_le16enc(u16, *s); u16 += 2; s++; count++; } as16->length += count << 1; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; return (0); } do { count = MultiByteToWideChar(sc->from_cp, MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1); /* Exit loop if we succeeded */ if (count != 0 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { break; } /* Expand buffer and try again */ count = MultiByteToWideChar(sc->from_cp, MB_PRECOMPOSED, s, (int)length, NULL, 0); if (archive_string_ensure(as16, (count +1) * 2) == NULL) return (-1); u16 = as16->s + as16->length; avail = as16->buffer_length - 2; } while (1); as16->length += count * 2; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; if (count == 0) return (-1); if (is_big_endian()) { if (!bigendian) { while (count > 0) { uint16_t v = archive_be16dec(u16); archive_le16enc(u16, v); u16 += 2; count--; } } } else { if (bigendian) { while (count > 0) { uint16_t v = archive_le16dec(u16); archive_be16enc(u16, v); u16 += 2; count--; } } } return (0); } static int win_strncat_to_utf16be(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (win_strncat_to_utf16(as16, _p, length, sc, 1)); } static int win_strncat_to_utf16le(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (win_strncat_to_utf16(as16, _p, length, sc, 0)); } #endif /* _WIN32 && !__CYGWIN__ */ /* * Do the best effort for conversions. * We cannot handle UTF-16BE character-set without such iconv, * but there is a chance if a string consists just ASCII code or * a current locale is UTF-8. */ /* * Convert a UTF-16BE string to current locale and copy the result. * Return -1 if conversion fails. */ static int best_effort_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc, int be) { const char *utf16 = (const char *)_p; char *mbs; uint32_t uc; int n, ret; (void)sc; /* UNUSED */ /* * Other case, we should do the best effort. * If all character are ASCII(<0x7f), we can convert it. * if not , we set a alternative character and return -1. */ ret = 0; if (archive_string_ensure(as, as->length + bytes +1) == NULL) return (-1); mbs = as->s + as->length; while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) { if (n < 0) { n *= -1; ret = -1; } bytes -= n; utf16 += n; if (uc > 127) { /* We cannot handle it. */ *mbs++ = '?'; ret = -1; } else *mbs++ = (char)uc; } as->length = mbs - as->s; as->s[as->length] = '\0'; return (ret); } static int best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1)); } static int best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p, size_t bytes, struct archive_string_conv *sc) { return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0)); } /* * Convert a current locale string to UTF-16BE/LE and copy the result. * Return -1 if conversion fails. */ static int best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc, int bigendian) { const char *s = (const char *)_p; char *utf16; size_t remaining; int ret; (void)sc; /* UNUSED */ /* * Other case, we should do the best effort. * If all character are ASCII(<0x7f), we can convert it. * if not , we set a alternative character and return -1. */ ret = 0; remaining = length; if (archive_string_ensure(as16, as16->length + (length + 1) * 2) == NULL) return (-1); utf16 = as16->s + as16->length; while (remaining--) { unsigned c = *s++; if (c > 127) { /* We cannot handle it. */ c = UNICODE_R_CHAR; ret = -1; } if (bigendian) archive_be16enc(utf16, c); else archive_le16enc(utf16, c); utf16 += 2; } as16->length = utf16 - as16->s; as16->s[as16->length] = 0; as16->s[as16->length+1] = 0; return (ret); } static int best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1)); } static int best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p, size_t length, struct archive_string_conv *sc) { return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0)); } /* * Multistring operations. */ void archive_mstring_clean(struct archive_mstring *aes) { archive_wstring_free(&(aes->aes_wcs)); archive_string_free(&(aes->aes_mbs)); archive_string_free(&(aes->aes_utf8)); archive_string_free(&(aes->aes_mbs_in_locale)); aes->aes_set = 0; } void archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src) { dest->aes_set = src->aes_set; archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs)); } int archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes, const char **p) { struct archive_string_conv *sc; int r; /* If we already have a UTF8 form, return that immediately. */ if (aes->aes_set & AES_SET_UTF8) { *p = aes->aes_utf8.s; return (0); } *p = NULL; + /* Try converting WCS to MBS first if MBS does not exist yet. */ + if ((aes->aes_set & AES_SET_MBS) == 0) { + const char *pm; /* unused */ + archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */ + } if (aes->aes_set & AES_SET_MBS) { sc = archive_string_conversion_to_charset(a, "UTF-8", 1); if (sc == NULL) return (-1);/* Couldn't allocate memory for sc. */ r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s, aes->aes_mbs.length, sc); if (a == NULL) free_sconv_object(sc); if (r == 0) { aes->aes_set |= AES_SET_UTF8; *p = aes->aes_utf8.s; return (0);/* success. */ } else return (-1);/* failure. */ } return (0);/* success. */ } int archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes, const char **p) { + struct archive_string_conv *sc; int r, ret = 0; - (void)a; /* UNUSED */ /* If we already have an MBS form, return that immediately. */ if (aes->aes_set & AES_SET_MBS) { *p = aes->aes_mbs.s; return (ret); } *p = NULL; /* If there's a WCS form, try converting with the native locale. */ if (aes->aes_set & AES_SET_WCS) { archive_string_empty(&(aes->aes_mbs)); r = archive_string_append_from_wcs(&(aes->aes_mbs), aes->aes_wcs.s, aes->aes_wcs.length); *p = aes->aes_mbs.s; if (r == 0) { aes->aes_set |= AES_SET_MBS; return (ret); } else ret = -1; } - /* - * Only a UTF-8 form cannot avail because its conversion already - * failed at archive_mstring_update_utf8(). - */ + /* If there's a UTF-8 form, try converting with the native locale. */ + if (aes->aes_set & AES_SET_UTF8) { + archive_string_empty(&(aes->aes_mbs)); + sc = archive_string_conversion_from_charset(a, "UTF-8", 1); + if (sc == NULL) + return (-1);/* Couldn't allocate memory for sc. */ + r = archive_strncpy_l(&(aes->aes_mbs), + aes->aes_utf8.s, aes->aes_utf8.length, sc); + if (a == NULL) + free_sconv_object(sc); + *p = aes->aes_mbs.s; + if (r == 0) { + aes->aes_set |= AES_SET_MBS; + ret = 0;/* success; overwrite previous error. */ + } else + ret = -1;/* failure. */ + } return (ret); } int archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes, const wchar_t **wp) { int r, ret = 0; (void)a;/* UNUSED */ /* Return WCS form if we already have it. */ if (aes->aes_set & AES_SET_WCS) { *wp = aes->aes_wcs.s; return (ret); } *wp = NULL; + /* Try converting UTF8 to MBS first if MBS does not exist yet. */ + if ((aes->aes_set & AES_SET_MBS) == 0) { + const char *p; /* unused */ + archive_mstring_get_mbs(a, aes, &p); /* ignore errors, we'll handle it later */ + } /* Try converting MBS to WCS using native locale. */ if (aes->aes_set & AES_SET_MBS) { archive_wstring_empty(&(aes->aes_wcs)); r = archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s, aes->aes_mbs.length); if (r == 0) { aes->aes_set |= AES_SET_WCS; *wp = aes->aes_wcs.s; } else ret = -1;/* failure. */ } return (ret); } int -archive_mstring_get_mbs_l(struct archive_mstring *aes, +archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes, const char **p, size_t *length, struct archive_string_conv *sc) { int r, ret = 0; + (void)r; /* UNUSED */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Internationalization programming on Windows must use Wide * characters because Windows platform cannot make locale UTF-8. */ if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) { archive_string_empty(&(aes->aes_mbs_in_locale)); r = archive_string_append_from_wcs_in_codepage( &(aes->aes_mbs_in_locale), aes->aes_wcs.s, aes->aes_wcs.length, sc); if (r == 0) { *p = aes->aes_mbs_in_locale.s; if (length != NULL) *length = aes->aes_mbs_in_locale.length; return (0); } else if (errno == ENOMEM) return (-1); else ret = -1; } #endif - /* If there is not an MBS form but is a WCS form, try converting + /* If there is not an MBS form but there is a WCS or UTF8 form, try converting * with the native locale to be used for translating it to specified * character-set. */ - if ((aes->aes_set & AES_SET_MBS) == 0 && - (aes->aes_set & AES_SET_WCS) != 0) { - archive_string_empty(&(aes->aes_mbs)); - r = archive_string_append_from_wcs(&(aes->aes_mbs), - aes->aes_wcs.s, aes->aes_wcs.length); - if (r == 0) - aes->aes_set |= AES_SET_MBS; - else if (errno == ENOMEM) - return (-1); - else - ret = -1; + if ((aes->aes_set & AES_SET_MBS) == 0) { + const char *pm; /* unused */ + archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */ } /* If we already have an MBS form, use it to be translated to * specified character-set. */ if (aes->aes_set & AES_SET_MBS) { if (sc == NULL) { /* Conversion is unneeded. */ *p = aes->aes_mbs.s; if (length != NULL) *length = aes->aes_mbs.length; return (0); } ret = archive_strncpy_l(&(aes->aes_mbs_in_locale), aes->aes_mbs.s, aes->aes_mbs.length, sc); *p = aes->aes_mbs_in_locale.s; if (length != NULL) *length = aes->aes_mbs_in_locale.length; } else { *p = NULL; if (length != NULL) *length = 0; } return (ret); } int archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs) { if (mbs == NULL) { aes->aes_set = 0; return (0); } return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs))); } int archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs, size_t len) { if (mbs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ archive_strncpy(&(aes->aes_mbs), mbs, len); archive_string_empty(&(aes->aes_utf8)); archive_wstring_empty(&(aes->aes_wcs)); return (0); } int archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs) { return archive_mstring_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs)); } int archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_UTF8; archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_wcs)); archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8)); return (int)strlen(utf8); } int archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs, size_t len) { if (wcs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_utf8)); archive_wstrncpy(&(aes->aes_wcs), wcs, len); return (0); } int archive_mstring_copy_mbs_len_l(struct archive_mstring *aes, const char *mbs, size_t len, struct archive_string_conv *sc) { int r; if (mbs == NULL) { aes->aes_set = 0; return (0); } archive_string_empty(&(aes->aes_mbs)); archive_wstring_empty(&(aes->aes_wcs)); archive_string_empty(&(aes->aes_utf8)); #if defined(_WIN32) && !defined(__CYGWIN__) /* * Internationalization programming on Windows must use Wide * characters because Windows platform cannot make locale UTF-8. */ if (sc == NULL) { if (archive_string_append(&(aes->aes_mbs), mbs, mbsnbytes(mbs, len)) == NULL) { aes->aes_set = 0; r = -1; } else { aes->aes_set = AES_SET_MBS; r = 0; } #if defined(HAVE_ICONV) } else if (sc != NULL && sc->cd_w != (iconv_t)-1) { /* * This case happens only when MultiByteToWideChar() cannot * handle sc->from_cp, and we have to iconv in order to * translate character-set to wchar_t,UTF-16. */ iconv_t cd = sc->cd; unsigned from_cp; int flag; /* * Translate multi-bytes from some character-set to UTF-8. */ sc->cd = sc->cd_w; r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc); sc->cd = cd; if (r != 0) { aes->aes_set = 0; return (r); } aes->aes_set = AES_SET_UTF8; /* * Append the UTF-8 string into wstring. */ flag = sc->flag; sc->flag &= ~(SCONV_NORMALIZATION_C | SCONV_TO_UTF16| SCONV_FROM_UTF16); from_cp = sc->from_cp; sc->from_cp = CP_UTF8; r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), aes->aes_utf8.s, aes->aes_utf8.length, sc); sc->flag = flag; sc->from_cp = from_cp; if (r == 0) aes->aes_set |= AES_SET_WCS; #endif } else { r = archive_wstring_append_from_mbs_in_codepage( &(aes->aes_wcs), mbs, len, sc); if (r == 0) aes->aes_set = AES_SET_WCS; else aes->aes_set = 0; } #else r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc); if (r == 0) aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ else aes->aes_set = 0; #endif return (r); } /* * The 'update' form tries to proactively update all forms of * this string (WCS and MBS) and returns an error if any of * them fail. This is used by the 'pax' handler, for instance, * to detect and report character-conversion failures early while * still allowing clients to get potentially useful values from * the more tolerant lazy conversions. (get_mbs and get_wcs will * strive to give the user something useful, so you can get hopefully * usable values even if some of the character conversions are failing.) */ int archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes, const char *utf8) { struct archive_string_conv *sc; int r; if (utf8 == NULL) { aes->aes_set = 0; return (0); /* Succeeded in clearing everything. */ } /* Save the UTF8 string. */ archive_strcpy(&(aes->aes_utf8), utf8); /* Empty the mbs and wcs strings. */ archive_string_empty(&(aes->aes_mbs)); archive_wstring_empty(&(aes->aes_wcs)); aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ /* Try converting UTF-8 to MBS, return false on failure. */ sc = archive_string_conversion_from_charset(a, "UTF-8", 1); if (sc == NULL) return (-1);/* Couldn't allocate memory for sc. */ r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc); if (a == NULL) free_sconv_object(sc); if (r != 0) return (-1); aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */ /* Try converting MBS to WCS, return false on failure. */ if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s, aes->aes_mbs.length)) return (-1); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; /* All conversions succeeded. */ return (0); } Index: stable/11/contrib/libarchive/libarchive/archive_string.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_string.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_string.h (revision 368707) @@ -1,243 +1,243 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #ifndef ARCHIVE_STRING_H_INCLUDED #define ARCHIVE_STRING_H_INCLUDED #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif #include #ifdef HAVE_STDLIB_H #include /* required for wchar_t on some systems */ #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" /* * Basic resizable/reusable string support similar to Java's "StringBuffer." * * Unlike sbuf(9), the buffers here are fully reusable and track the * length throughout. */ struct archive_string { char *s; /* Pointer to the storage */ size_t length; /* Length of 's' in characters */ size_t buffer_length; /* Length of malloc-ed storage in bytes. */ }; struct archive_wstring { wchar_t *s; /* Pointer to the storage */ size_t length; /* Length of 's' in characters */ size_t buffer_length; /* Length of malloc-ed storage in bytes. */ }; struct archive_string_conv; /* Initialize an archive_string object on the stack or elsewhere. */ #define archive_string_init(a) \ do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0) /* Append a C char to an archive_string, resizing as necessary. */ struct archive_string * archive_strappend_char(struct archive_string *, char); /* Ditto for a wchar_t and an archive_wstring. */ struct archive_wstring * archive_wstrappend_wchar(struct archive_wstring *, wchar_t); /* Append a raw array to an archive_string, resizing as necessary */ struct archive_string * archive_array_append(struct archive_string *, const char *, size_t); /* Convert a Unicode string to current locale and append the result. */ /* Returns -1 if conversion fails. */ int archive_string_append_from_wcs(struct archive_string *, const wchar_t *, size_t); /* Create a string conversion object. * Return NULL and set a error message if the conversion is not supported * on the platform. */ struct archive_string_conv * archive_string_conversion_to_charset(struct archive *, const char *, int); struct archive_string_conv * archive_string_conversion_from_charset(struct archive *, const char *, int); /* Create the default string conversion object for reading/writing an archive. * Return NULL if the conversion is unneeded. * Note: On non Windows platform this always returns NULL. */ struct archive_string_conv * archive_string_default_conversion_for_read(struct archive *); struct archive_string_conv * archive_string_default_conversion_for_write(struct archive *); /* Dispose of a string conversion object. */ void archive_string_conversion_free(struct archive *); const char * archive_string_conversion_charset_name(struct archive_string_conv *); void archive_string_conversion_set_opt(struct archive_string_conv *, int); #define SCONV_SET_OPT_UTF8_LIBARCHIVE2X 1 #define SCONV_SET_OPT_NORMALIZATION_C 2 #define SCONV_SET_OPT_NORMALIZATION_D 4 /* Copy one archive_string to another in locale conversion. * Return -1 if conversion fails. */ int archive_strncpy_l(struct archive_string *, const void *, size_t, struct archive_string_conv *); /* Copy one archive_string to another in locale conversion. * Return -1 if conversion fails. */ int archive_strncat_l(struct archive_string *, const void *, size_t, struct archive_string_conv *); /* Copy one archive_string to another */ #define archive_string_copy(dest, src) \ ((dest)->length = 0, archive_string_concat((dest), (src))) #define archive_wstring_copy(dest, src) \ ((dest)->length = 0, archive_wstring_concat((dest), (src))) /* Concatenate one archive_string to another */ void archive_string_concat(struct archive_string *dest, struct archive_string *src); void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src); /* Ensure that the underlying buffer is at least as large as the request. */ struct archive_string * archive_string_ensure(struct archive_string *, size_t); struct archive_wstring * archive_wstring_ensure(struct archive_wstring *, size_t); /* Append C string, which may lack trailing \0. */ /* The source is declared void * here because this gets used with * "signed char *", "unsigned char *" and "char *" arguments. * Declaring it "char *" as with some of the other functions just * leads to a lot of extra casts. */ struct archive_string * archive_strncat(struct archive_string *, const void *, size_t); struct archive_wstring * archive_wstrncat(struct archive_wstring *, const wchar_t *, size_t); /* Append a C string to an archive_string, resizing as necessary. */ struct archive_string * archive_strcat(struct archive_string *, const void *); struct archive_wstring * archive_wstrcat(struct archive_wstring *, const wchar_t *); /* Copy a C string to an archive_string, resizing as necessary. */ #define archive_strcpy(as,p) \ archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p))) #define archive_wstrcpy(as,p) \ archive_wstrncpy((as), (p), ((p) == NULL ? 0 : wcslen(p))) #define archive_strcpy_l(as,p,lo) \ archive_strncpy_l((as), (p), ((p) == NULL ? 0 : strlen(p)), (lo)) /* Copy a C string to an archive_string with limit, resizing as necessary. */ #define archive_strncpy(as,p,l) \ ((as)->length=0, archive_strncat((as), (p), (l))) #define archive_wstrncpy(as,p,l) \ ((as)->length = 0, archive_wstrncat((as), (p), (l))) /* Return length of string. */ #define archive_strlen(a) ((a)->length) /* Set string length to zero. */ #define archive_string_empty(a) ((a)->length = 0) #define archive_wstring_empty(a) ((a)->length = 0) /* Release any allocated storage resources. */ void archive_string_free(struct archive_string *); void archive_wstring_free(struct archive_wstring *); /* Like 'vsprintf', but resizes the underlying string as necessary. */ /* Note: This only implements a small subset of standard printf functionality. */ void archive_string_vsprintf(struct archive_string *, const char *, va_list) __LA_PRINTF(2, 0); void archive_string_sprintf(struct archive_string *, const char *, ...) __LA_PRINTF(2, 3); /* Translates from MBS to Unicode. */ /* Returns non-zero if conversion failed in any way. */ int archive_wstring_append_from_mbs(struct archive_wstring *dest, const char *, size_t); /* A "multistring" can hold Unicode, UTF8, or MBS versions of * the string. If you set and read the same version, no translation * is done. If you set and read different versions, the library * will attempt to transparently convert. */ struct archive_mstring { struct archive_string aes_mbs; struct archive_string aes_utf8; struct archive_wstring aes_wcs; struct archive_string aes_mbs_in_locale; /* Bitmap of which of the above are valid. Because we're lazy * about malloc-ing and reusing the underlying storage, we * can't rely on NULL pointers to indicate whether a string * has been set. */ int aes_set; #define AES_SET_MBS 1 #define AES_SET_UTF8 2 #define AES_SET_WCS 4 }; void archive_mstring_clean(struct archive_mstring *); void archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src); int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **); int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **); int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **); -int archive_mstring_get_mbs_l(struct archive_mstring *, const char **, +int archive_mstring_get_mbs_l(struct archive *, struct archive_mstring *, const char **, size_t *, struct archive_string_conv *); int archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs); int archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs, size_t); int archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8); int archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs); int archive_mstring_copy_wcs_len(struct archive_mstring *, const wchar_t *wcs, size_t); int archive_mstring_copy_mbs_len_l(struct archive_mstring *, const char *mbs, size_t, struct archive_string_conv *); int archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8); #endif Index: stable/11/contrib/libarchive/libarchive/archive_util.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_util.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_util.c (revision 368707) @@ -1,650 +1,655 @@ /*- * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) #include #endif #ifdef HAVE_ZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZ4_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_string.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif static int archive_utility_string_sort_helper(char **, unsigned int); /* Generic initialization of 'struct archive' objects. */ int __archive_clean(struct archive *a) { archive_string_conversion_free(a); return (ARCHIVE_OK); } int archive_version_number(void) { return (ARCHIVE_VERSION_NUMBER); } const char * archive_version_string(void) { return (ARCHIVE_VERSION_STRING); } int archive_errno(struct archive *a) { return (a->archive_error_number); } const char * archive_error_string(struct archive *a) { if (a->error != NULL && *a->error != '\0') return (a->error); else return (NULL); } int archive_file_count(struct archive *a) { return (a->file_count); } int archive_format(struct archive *a) { return (a->archive_format); } const char * archive_format_name(struct archive *a) { return (a->archive_format_name); } int archive_compression(struct archive *a) { return archive_filter_code(a, 0); } const char * archive_compression_name(struct archive *a) { return archive_filter_name(a, 0); } /* * Return a count of the number of compressed bytes processed. */ la_int64_t archive_position_compressed(struct archive *a) { return archive_filter_bytes(a, -1); } /* * Return a count of the number of uncompressed bytes processed. */ la_int64_t archive_position_uncompressed(struct archive *a) { return archive_filter_bytes(a, 0); } void archive_clear_error(struct archive *a) { archive_string_empty(&a->error_string); a->error = NULL; a->archive_error_number = 0; } void archive_set_error(struct archive *a, int error_number, const char *fmt, ...) { va_list ap; a->archive_error_number = error_number; if (fmt == NULL) { a->error = NULL; return; } archive_string_empty(&(a->error_string)); va_start(ap, fmt); archive_string_vsprintf(&(a->error_string), fmt, ap); va_end(ap); a->error = a->error_string.s; } void archive_copy_error(struct archive *dest, struct archive *src) { dest->archive_error_number = src->archive_error_number; archive_string_copy(&dest->error_string, &src->error_string); dest->error = dest->error_string.s; } void __archive_errx(int retvalue, const char *msg) { static const char msg1[] = "Fatal Internal Error in libarchive: "; size_t s; s = write(2, msg1, strlen(msg1)); (void)s; /* UNUSED */ s = write(2, msg, strlen(msg)); (void)s; /* UNUSED */ s = write(2, "\n", 1); (void)s; /* UNUSED */ exit(retvalue); } /* * Create a temporary file */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Do not use Windows tmpfile() function. * It will make a temporary file under the root directory * and it'll cause permission error if a user who is * non-Administrator creates temporary files. * Also Windows version of mktemp family including _mktemp_s * are not secure. */ static int __archive_mktempx(const char *tmpdir, wchar_t *template) { static const wchar_t prefix[] = L"libarchive_"; static const wchar_t suffix[] = L"XXXXXXXXXX"; static const wchar_t num[] = { L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd', L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l', L'm', L'n', L'o', L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z' }; HCRYPTPROV hProv; struct archive_wstring temp_name; wchar_t *ws; DWORD attr; wchar_t *xp, *ep; int fd; hProv = (HCRYPTPROV)NULL; fd = -1; ws = NULL; if (template == NULL) { archive_string_init(&temp_name); /* Get a temporary directory. */ if (tmpdir == NULL) { size_t l; wchar_t *tmp; l = GetTempPathW(0, NULL); if (l == 0) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } tmp = malloc(l*sizeof(wchar_t)); if (tmp == NULL) { errno = ENOMEM; goto exit_tmpfile; } GetTempPathW((DWORD)l, tmp); archive_wstrcpy(&temp_name, tmp); free(tmp); } else { if (archive_wstring_append_from_mbs(&temp_name, tmpdir, strlen(tmpdir)) < 0) goto exit_tmpfile; if (temp_name.s[temp_name.length-1] != L'/') archive_wstrappend_wchar(&temp_name, L'/'); } /* Check if temp_name is a directory. */ attr = GetFileAttributesW(temp_name.s); if (attr == (DWORD)-1) { if (GetLastError() != ERROR_FILE_NOT_FOUND) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } ws = __la_win_permissive_name_w(temp_name.s); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } attr = GetFileAttributesW(ws); if (attr == (DWORD)-1) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } } if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { errno = ENOTDIR; goto exit_tmpfile; } /* * Create a temporary file. */ archive_wstrcat(&temp_name, prefix); archive_wstrcat(&temp_name, suffix); ep = temp_name.s + archive_strlen(&temp_name); xp = ep - wcslen(suffix); template = temp_name.s; } else { xp = wcschr(template, L'X'); if (xp == NULL) /* No X, programming error */ abort(); for (ep = xp; *ep == L'X'; ep++) continue; if (*ep) /* X followed by non X, programming error */ abort(); } if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } for (;;) { wchar_t *p; HANDLE h; /* Generate a random file name through CryptGenRandom(). */ p = xp; if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; free(ws); ws = __la_win_permissive_name_w(template); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } if (template == temp_name.s) { attr = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE; } else { /* mkstemp */ attr = FILE_ATTRIBUTE_NORMAL; } h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ NULL, CREATE_NEW,/* Create a new file only */ attr, NULL); if (h == INVALID_HANDLE_VALUE) { /* The same file already exists. retry with * a new filename. */ if (GetLastError() == ERROR_FILE_EXISTS) continue; /* Otherwise, fail creation temporary file. */ la_dosmaperr(GetLastError()); goto exit_tmpfile; } fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR); if (fd == -1) { la_dosmaperr(GetLastError()); CloseHandle(h); goto exit_tmpfile; } else break;/* success! */ } exit_tmpfile: if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); free(ws); if (template == temp_name.s) archive_wstring_free(&temp_name); return (fd); } int __archive_mktemp(const char *tmpdir) { return __archive_mktempx(tmpdir, NULL); } int __archive_mkstemp(wchar_t *template) { return __archive_mktempx(NULL, template); } #else static int get_tempdir(struct archive_string *temppath) { const char *tmp; tmp = getenv("TMPDIR"); if (tmp == NULL) #ifdef _PATH_TMP tmp = _PATH_TMP; #else tmp = "/tmp"; #endif archive_strcpy(temppath, tmp); if (temppath->s[temppath->length-1] != '/') archive_strappend_char(temppath, '/'); return (ARCHIVE_OK); } #if defined(HAVE_MKSTEMP) /* * We can use mkstemp(). */ int __archive_mktemp(const char *tmpdir) { struct archive_string temp_name; int fd = -1; archive_string_init(&temp_name); if (tmpdir == NULL) { if (get_tempdir(&temp_name) != ARCHIVE_OK) goto exit_tmpfile; } else { archive_strcpy(&temp_name, tmpdir); if (temp_name.s[temp_name.length-1] != '/') archive_strappend_char(&temp_name, '/'); } +#ifdef O_TMPFILE + fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600); + if(fd >= 0) + goto exit_tmpfile; +#endif archive_strcat(&temp_name, "libarchive_XXXXXX"); fd = mkstemp(temp_name.s); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); return (fd); } int __archive_mkstemp(char *template) { int fd = -1; fd = mkstemp(template); if (fd >= 0) __archive_ensure_cloexec_flag(fd); return (fd); } #else /* !HAVE_MKSTEMP */ /* * We use a private routine. */ static int __archive_mktempx(const char *tmpdir, char *template) { static const char num[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; struct archive_string temp_name; struct stat st; int fd; char *tp, *ep; fd = -1; if (template == NULL) { archive_string_init(&temp_name); if (tmpdir == NULL) { if (get_tempdir(&temp_name) != ARCHIVE_OK) goto exit_tmpfile; } else archive_strcpy(&temp_name, tmpdir); if (temp_name.s[temp_name.length-1] == '/') { temp_name.s[temp_name.length-1] = '\0'; temp_name.length --; } if (la_stat(temp_name.s, &st) < 0) goto exit_tmpfile; if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; goto exit_tmpfile; } archive_strcat(&temp_name, "/libarchive_"); tp = temp_name.s + archive_strlen(&temp_name); archive_strcat(&temp_name, "XXXXXXXXXX"); ep = temp_name.s + archive_strlen(&temp_name); template = temp_name.s; } else { tp = strchr(template, 'X'); if (tp == NULL) /* No X, programming error */ abort(); for (ep = tp; *ep == 'X'; ep++) continue; if (*ep) /* X followed by non X, programming error */ abort(); } do { char *p; p = tp; archive_random(p, ep - p); while (p < ep) { int d = *((unsigned char *)p) % sizeof(num); *p++ = num[d]; } fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); } while (fd < 0 && errno == EEXIST); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); if (template == temp_name.s) unlink(temp_name.s); exit_tmpfile: if (template == temp_name.s) archive_string_free(&temp_name); return (fd); } int __archive_mktemp(const char *tmpdir) { return __archive_mktempx(tmpdir, NULL); } int __archive_mkstemp(char *template) { return __archive_mktempx(NULL, template); } #endif /* !HAVE_MKSTEMP */ #endif /* !_WIN32 || __CYGWIN__ */ /* * Set FD_CLOEXEC flag to a file descriptor if it is not set. * We have to set the flag if the platform does not provide O_CLOEXEC * or F_DUPFD_CLOEXEC flags. * * Note: This function is absolutely called after creating a new file * descriptor even if the platform seemingly provides O_CLOEXEC or * F_DUPFD_CLOEXEC macros because it is possible that the platform * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it. */ void __archive_ensure_cloexec_flag(int fd) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)fd; /* UNUSED */ #else int flags; if (fd >= 0) { flags = fcntl(fd, F_GETFD); if (flags != -1 && (flags & FD_CLOEXEC) == 0) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } #endif } /* * Utility function to sort a group of strings using quicksort. */ static int archive_utility_string_sort_helper(char **strings, unsigned int n) { unsigned int i, lesser_count, greater_count; char **lesser, **greater, **tmp, *pivot; int retval1, retval2; /* A list of 0 or 1 elements is already sorted */ if (n <= 1) return (ARCHIVE_OK); lesser_count = greater_count = 0; lesser = greater = NULL; pivot = strings[0]; for (i = 1; i < n; i++) { if (strcmp(strings[i], pivot) < 0) { lesser_count++; tmp = (char **)realloc(lesser, lesser_count * sizeof(char *)); if (!tmp) { free(greater); free(lesser); return (ARCHIVE_FATAL); } lesser = tmp; lesser[lesser_count - 1] = strings[i]; } else { greater_count++; tmp = (char **)realloc(greater, greater_count * sizeof(char *)); if (!tmp) { free(greater); free(lesser); return (ARCHIVE_FATAL); } greater = tmp; greater[greater_count - 1] = strings[i]; } } /* quicksort(lesser) */ retval1 = archive_utility_string_sort_helper(lesser, lesser_count); for (i = 0; i < lesser_count; i++) strings[i] = lesser[i]; free(lesser); /* pivot */ strings[lesser_count] = pivot; /* quicksort(greater) */ retval2 = archive_utility_string_sort_helper(greater, greater_count); for (i = 0; i < greater_count; i++) strings[lesser_count + 1 + i] = greater[i]; free(greater); return (retval1 < retval2) ? retval1 : retval2; } int archive_utility_string_sort(char **strings) { unsigned int size = 0; while (strings[size] != NULL) size++; return archive_utility_string_sort_helper(strings, size); } Index: stable/11/contrib/libarchive/libarchive/archive_write.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write.c (revision 368707) @@ -1,789 +1,813 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* * This file contains the "essential" portions of the write API, that * is, stuff that will essentially always be used by any client that * actually needs to write an archive. Optional pieces have been, as * far as possible, separated out into separate files to reduce * needlessly bloating statically-linked clients. */ #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" static struct archive_vtable *archive_write_vtable(void); static int _archive_filter_code(struct archive *, int); static const char *_archive_filter_name(struct archive *, int); static int64_t _archive_filter_bytes(struct archive *, int); static int _archive_write_filter_count(struct archive *); static int _archive_write_close(struct archive *); static int _archive_write_free(struct archive *); static int _archive_write_header(struct archive *, struct archive_entry *); static int _archive_write_finish_entry(struct archive *); static ssize_t _archive_write_data(struct archive *, const void *, size_t); struct archive_none { size_t buffer_size; size_t avail; char *buffer; char *next; }; static struct archive_vtable * archive_write_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_close; av.archive_filter_bytes = _archive_filter_bytes; av.archive_filter_code = _archive_filter_code; av.archive_filter_name = _archive_filter_name; av.archive_filter_count = _archive_write_filter_count; av.archive_free = _archive_write_free; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; av.archive_write_data = _archive_write_data; inited = 1; } return (&av); } /* * Allocate, initialize and return an archive object. */ struct archive * archive_write_new(void) { struct archive_write *a; unsigned char *nulls; a = (struct archive_write *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_WRITE_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_write_vtable(); /* * The value 10240 here matches the traditional tar default, * but is otherwise arbitrary. * TODO: Set the default block size from the format selected. */ a->bytes_per_block = 10240; a->bytes_in_last_block = -1; /* Default */ /* Initialize a block of nulls for padding purposes. */ a->null_length = 1024; nulls = (unsigned char *)calloc(1, a->null_length); if (nulls == NULL) { free(a); return (NULL); } a->nulls = nulls; return (&a->archive); } /* * Set the block size. Returns 0 if successful. */ int archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block"); a->bytes_per_block = bytes_per_block; return (ARCHIVE_OK); } /* * Get the current block size. -1 if it has never been set. */ int archive_write_get_bytes_per_block(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block"); return (a->bytes_per_block); } /* * Set the size for the last block. * Returns 0 if successful. */ int archive_write_set_bytes_in_last_block(struct archive *_a, int bytes) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block"); a->bytes_in_last_block = bytes; return (ARCHIVE_OK); } /* * Return the value set above. -1 indicates it has not been set. */ int archive_write_get_bytes_in_last_block(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block"); return (a->bytes_in_last_block); } /* * dev/ino of a file to be rejected. Used to prevent adding * an archive to itself recursively. */ int archive_write_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_skip_file"); a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } /* * Allocate and return the next filter structure. */ struct archive_write_filter * __archive_write_allocate_filter(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f; f = calloc(1, sizeof(*f)); f->archive = _a; f->state = ARCHIVE_WRITE_FILTER_STATE_NEW; if (a->filter_first == NULL) a->filter_first = f; else a->filter_last->next_filter = f; a->filter_last = f; return f; } /* * Write data to a particular filter. */ int __archive_write_filter(struct archive_write_filter *f, const void *buff, size_t length) { int r; /* Never write to non-open filters */ if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN) return(ARCHIVE_FATAL); if (length == 0) return(ARCHIVE_OK); if (f->write == NULL) /* If unset, a fatal error has already occurred, so this filter * didn't open. We cannot write anything. */ return(ARCHIVE_FATAL); r = (f->write)(f, buff, length); f->bytes_written += length; return (r); } /* * Recursive function for opening the filter chain * Last filter is opened first */ static int __archive_write_open_filter(struct archive_write_filter *f) { int ret; ret = ARCHIVE_OK; if (f->next_filter != NULL) ret = __archive_write_open_filter(f->next_filter); if (ret != ARCHIVE_OK) return (ret); if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW) return (ARCHIVE_FATAL); if (f->open == NULL) { f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN; return (ARCHIVE_OK); } ret = (f->open)(f); if (ret == ARCHIVE_OK) f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN; else f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; return (ret); } /* * Open all filters */ static int __archive_write_filters_open(struct archive_write *a) { return (__archive_write_open_filter(a->filter_first)); } /* * Close all filtes */ static int __archive_write_filters_close(struct archive_write *a) { struct archive_write_filter *f; int ret, ret1; ret = ARCHIVE_OK; for (f = a->filter_first; f != NULL; f = f->next_filter) { /* Do not close filters that are not open */ if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) { if (f->close != NULL) { ret1 = (f->close)(f); if (ret1 < ret) ret = ret1; if (ret1 == ARCHIVE_OK) { f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; } else { f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; } } else f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; } } return (ret); } int __archive_write_output(struct archive_write *a, const void *buff, size_t length) { return (__archive_write_filter(a->filter_first, buff, length)); } int __archive_write_nulls(struct archive_write *a, size_t length) { if (length == 0) return (ARCHIVE_OK); while (length > 0) { size_t to_write = length < a->null_length ? length : a->null_length; int r = __archive_write_output(a, a->nulls, to_write); if (r < ARCHIVE_OK) return (r); length -= to_write; } return (ARCHIVE_OK); } static int archive_write_client_open(struct archive_write_filter *f) { struct archive_write *a = (struct archive_write *)f->archive; struct archive_none *state; void *buffer; size_t buffer_size; int ret; f->bytes_per_block = archive_write_get_bytes_per_block(f->archive); f->bytes_in_last_block = archive_write_get_bytes_in_last_block(f->archive); buffer_size = f->bytes_per_block; state = (struct archive_none *)calloc(1, sizeof(*state)); buffer = (char *)malloc(buffer_size); if (state == NULL || buffer == NULL) { free(state); free(buffer); archive_set_error(f->archive, ENOMEM, "Can't allocate data for output buffering"); return (ARCHIVE_FATAL); } state->buffer_size = buffer_size; state->buffer = buffer; state->next = state->buffer; state->avail = state->buffer_size; f->data = state; if (a->client_opener == NULL) return (ARCHIVE_OK); ret = a->client_opener(f->archive, a->client_data); if (ret != ARCHIVE_OK) { free(state->buffer); free(state); f->data = NULL; } return (ret); } static int archive_write_client_write(struct archive_write_filter *f, const void *_buff, size_t length) { struct archive_write *a = (struct archive_write *)f->archive; struct archive_none *state = (struct archive_none *)f->data; const char *buff = (const char *)_buff; ssize_t remaining, to_copy; ssize_t bytes_written; remaining = length; /* * If there is no buffer for blocking, just pass the data * straight through to the client write callback. In * particular, this supports "no write delay" operation for * special applications. Just set the block size to zero. */ if (state->buffer_size == 0) { while (remaining > 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, buff, remaining); if (bytes_written <= 0) return (ARCHIVE_FATAL); remaining -= bytes_written; buff += bytes_written; } return (ARCHIVE_OK); } /* If the copy buffer isn't empty, try to fill it. */ if (state->avail < state->buffer_size) { /* If buffer is not empty... */ /* ... copy data into buffer ... */ to_copy = ((size_t)remaining > state->avail) ? state->avail : (size_t)remaining; memcpy(state->next, buff, to_copy); state->next += to_copy; state->avail -= to_copy; buff += to_copy; remaining -= to_copy; /* ... if it's full, write it out. */ if (state->avail == 0) { char *p = state->buffer; size_t to_write = state->buffer_size; while (to_write > 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, p, to_write); if (bytes_written <= 0) return (ARCHIVE_FATAL); if ((size_t)bytes_written > to_write) { archive_set_error(&(a->archive), -1, "write overrun"); return (ARCHIVE_FATAL); } p += bytes_written; to_write -= bytes_written; } state->next = state->buffer; state->avail = state->buffer_size; } } while ((size_t)remaining >= state->buffer_size) { /* Write out full blocks directly to client. */ bytes_written = (a->client_writer)(&a->archive, a->client_data, buff, state->buffer_size); if (bytes_written <= 0) return (ARCHIVE_FATAL); buff += bytes_written; remaining -= bytes_written; } if (remaining > 0) { /* Copy last bit into copy buffer. */ memcpy(state->next, buff, remaining); state->next += remaining; state->avail -= remaining; } return (ARCHIVE_OK); } static int +archive_write_client_free(struct archive_write_filter *f) +{ + struct archive_write *a = (struct archive_write *)f->archive; + + if (a->client_freer) + (*a->client_freer)(&a->archive, a->client_data); + a->client_data = NULL; + + /* Clear passphrase. */ + if (a->passphrase != NULL) { + memset(a->passphrase, 0, strlen(a->passphrase)); + free(a->passphrase); + a->passphrase = NULL; + } + + return (ARCHIVE_OK); +} + +static int archive_write_client_close(struct archive_write_filter *f) { struct archive_write *a = (struct archive_write *)f->archive; struct archive_none *state = (struct archive_none *)f->data; ssize_t block_length; ssize_t target_block_length; ssize_t bytes_written; int ret = ARCHIVE_OK; /* If there's pending data, pad and write the last block */ if (state->next != state->buffer) { block_length = state->buffer_size - state->avail; /* Tricky calculation to determine size of last block */ if (a->bytes_in_last_block <= 0) /* Default or Zero: pad to full block */ target_block_length = a->bytes_per_block; else /* Round to next multiple of bytes_in_last_block. */ target_block_length = a->bytes_in_last_block * ( (block_length + a->bytes_in_last_block - 1) / a->bytes_in_last_block); if (target_block_length > a->bytes_per_block) target_block_length = a->bytes_per_block; if (block_length < target_block_length) { memset(state->next, 0, target_block_length - block_length); block_length = target_block_length; } bytes_written = (a->client_writer)(&a->archive, a->client_data, state->buffer, block_length); ret = bytes_written <= 0 ? ARCHIVE_FATAL : ARCHIVE_OK; } if (a->client_closer) (*a->client_closer)(&a->archive, a->client_data); free(state->buffer); free(state); - a->client_data = NULL; - /* Clear passphrase. */ - if (a->passphrase != NULL) { - memset(a->passphrase, 0, strlen(a->passphrase)); - free(a->passphrase); - a->passphrase = NULL; - } + /* Clear the close handler myself not to be called again. */ f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; return (ret); } /* * Open the archive using the current settings. */ int -archive_write_open(struct archive *_a, void *client_data, +archive_write_open2(struct archive *_a, void *client_data, archive_open_callback *opener, archive_write_callback *writer, - archive_close_callback *closer) + archive_close_callback *closer, archive_free_callback *freer) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *client_filter; int ret, r1; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open"); archive_clear_error(&a->archive); a->client_writer = writer; a->client_opener = opener; a->client_closer = closer; + a->client_freer = freer; a->client_data = client_data; client_filter = __archive_write_allocate_filter(_a); client_filter->open = archive_write_client_open; client_filter->write = archive_write_client_write; client_filter->close = archive_write_client_close; + client_filter->free = archive_write_client_free; ret = __archive_write_filters_open(a); if (ret < ARCHIVE_WARN) { r1 = __archive_write_filters_close(a); __archive_write_filters_free(_a); return (r1 < ret ? r1 : ret); } a->archive.state = ARCHIVE_STATE_HEADER; if (a->format_init) ret = (a->format_init)(a); return (ret); +} + +int +archive_write_open(struct archive *_a, void *client_data, + archive_open_callback *opener, archive_write_callback *writer, + archive_close_callback *closer) +{ + return archive_write_open2(_a, client_data, opener, writer, + closer, NULL); } /* * Close out the archive. */ static int _archive_write_close(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_close"); if (a->archive.state == ARCHIVE_STATE_NEW || a->archive.state == ARCHIVE_STATE_CLOSED) return (ARCHIVE_OK); /* Okay to close() when not open. */ archive_clear_error(&a->archive); /* Finish the last entry if a finish callback is specified */ if (a->archive.state == ARCHIVE_STATE_DATA && a->format_finish_entry != NULL) r = ((a->format_finish_entry)(a)); /* Finish off the archive. */ /* TODO: have format closers invoke compression close. */ if (a->format_close != NULL) { r1 = (a->format_close)(a); if (r1 < r) r = r1; } /* Finish the compression and close the stream. */ r1 = __archive_write_filters_close(a); if (r1 < r) r = r1; if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; return (r); } static int _archive_write_filter_count(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *p = a->filter_first; int count = 0; while(p) { count++; p = p->next_filter; } return count; } void __archive_write_filters_free(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK, r1; while (a->filter_first != NULL) { struct archive_write_filter *next = a->filter_first->next_filter; if (a->filter_first->free != NULL) { r1 = (*a->filter_first->free)(a->filter_first); if (r > r1) r = r1; } free(a->filter_first); a->filter_first = next; } a->filter_last = NULL; } /* * Destroy the archive structure. * * Be careful: user might just call write_new and then write_free. * Don't assume we actually wrote anything or performed any non-trivial * initialization. */ static int _archive_write_free(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK, r1; if (_a == NULL) return (ARCHIVE_OK); /* It is okay to call free() in state FATAL. */ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_free"); if (a->archive.state != ARCHIVE_STATE_FATAL) r = archive_write_close(&a->archive); /* Release format resources. */ if (a->format_free != NULL) { r1 = (a->format_free)(a); if (r1 < r) r = r1; } __archive_write_filters_free(_a); /* Release various dynamic buffers. */ free((void *)(uintptr_t)(const void *)a->nulls); archive_string_free(&a->archive.error_string); if (a->passphrase != NULL) { /* A passphrase should be cleaned. */ memset(a->passphrase, 0, strlen(a->passphrase)); free(a->passphrase); } a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (r); } /* * Write the appropriate header. */ static int _archive_write_header(struct archive *_a, struct archive_entry *entry) { struct archive_write *a = (struct archive_write *)_a; int ret, r2; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header"); archive_clear_error(&a->archive); if (a->format_write_header == NULL) { archive_set_error(&(a->archive), -1, "Format must be set before you can write to an archive."); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* In particular, "retry" and "fatal" get returned immediately. */ ret = archive_write_finish_entry(&a->archive); if (ret == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN) return (ret); if (a->skip_file_set && archive_entry_dev_is_set(entry) && archive_entry_ino_is_set(entry) && archive_entry_dev(entry) == (dev_t)a->skip_file_dev && archive_entry_ino64(entry) == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Can't add archive to itself"); return (ARCHIVE_FAILED); } /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); if (r2 == ARCHIVE_FAILED) { return (ARCHIVE_FAILED); } if (r2 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (r2 < ret) ret = r2; a->archive.state = ARCHIVE_STATE_DATA; return (ret); } static int _archive_write_finish_entry(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int ret = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_DATA && a->format_finish_entry != NULL) ret = (a->format_finish_entry)(a); a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } /* * Note that the compressor is responsible for blocking. */ static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t s) { struct archive_write *a = (struct archive_write *)_a; const size_t max_write = INT_MAX; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); /* In particular, this catches attempts to pass negative values. */ if (s > max_write) s = max_write; archive_clear_error(&a->archive); return ((a->format_write_data)(a, buff, s)); } static struct archive_write_filter * filter_lookup(struct archive *_a, int n) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f = a->filter_first; if (n == -1) return a->filter_last; if (n < 0) return NULL; while (n > 0 && f != NULL) { f = f->next_filter; --n; } return f; } static int _archive_filter_code(struct archive *_a, int n) { struct archive_write_filter *f = filter_lookup(_a, n); return f == NULL ? -1 : f->code; } static const char * _archive_filter_name(struct archive *_a, int n) { struct archive_write_filter *f = filter_lookup(_a, n); return f != NULL ? f->name : NULL; } static int64_t _archive_filter_bytes(struct archive *_a, int n) { struct archive_write_filter *f = filter_lookup(_a, n); return f == NULL ? -1 : f->bytes_written; } Index: stable/11/contrib/libarchive/libarchive/archive_write_add_filter_xz.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_add_filter_xz.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_add_filter_xz.c (revision 368707) @@ -1,545 +1,545 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * Copyright (c) 2009-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #ifdef HAVE_LZMA_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_private.h" #include "archive_write_private.h" #if ARCHIVE_VERSION_NUMBER < 4000000 int archive_write_set_compression_lzip(struct archive *a) { __archive_write_filters_free(a); return (archive_write_add_filter_lzip(a)); } int archive_write_set_compression_lzma(struct archive *a) { __archive_write_filters_free(a); return (archive_write_add_filter_lzma(a)); } int archive_write_set_compression_xz(struct archive *a) { __archive_write_filters_free(a); return (archive_write_add_filter_xz(a)); } #endif #ifndef HAVE_LZMA_H int archive_write_add_filter_xz(struct archive *a) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "xz compression not supported on this platform"); return (ARCHIVE_FATAL); } int archive_write_add_filter_lzma(struct archive *a) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzma compression not supported on this platform"); return (ARCHIVE_FATAL); } int archive_write_add_filter_lzip(struct archive *a) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzma compression not supported on this platform"); return (ARCHIVE_FATAL); } #else /* Don't compile this if we don't have liblzma. */ struct private_data { int compression_level; uint32_t threads; lzma_stream stream; lzma_filter lzmafilters[2]; lzma_options_lzma lzma_opt; int64_t total_in; unsigned char *compressed; size_t compressed_buffer_size; int64_t total_out; /* the CRC32 value of uncompressed data for lzip */ uint32_t crc32; }; static int archive_compressor_xz_options(struct archive_write_filter *, const char *, const char *); static int archive_compressor_xz_open(struct archive_write_filter *); static int archive_compressor_xz_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_xz_close(struct archive_write_filter *); static int archive_compressor_xz_free(struct archive_write_filter *); static int drive_compressor(struct archive_write_filter *, struct private_data *, int finishing); struct option_value { uint32_t dict_size; uint32_t nice_len; lzma_match_finder mf; }; static const struct option_value option_values[] = { { 1 << 16, 32, LZMA_MF_HC3}, { 1 << 20, 32, LZMA_MF_HC3}, { 3 << 19, 32, LZMA_MF_HC4}, { 1 << 21, 32, LZMA_MF_BT4}, { 3 << 20, 32, LZMA_MF_BT4}, { 1 << 22, 32, LZMA_MF_BT4}, { 1 << 23, 64, LZMA_MF_BT4}, { 1 << 24, 64, LZMA_MF_BT4}, { 3 << 23, 64, LZMA_MF_BT4}, { 1 << 25, 64, LZMA_MF_BT4} }; static int common_setup(struct archive_write_filter *f) { struct private_data *data; struct archive_write *a = (struct archive_write *)f->archive; data = calloc(1, sizeof(*data)); if (data == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } f->data = data; data->compression_level = LZMA_PRESET_DEFAULT; data->threads = 1; f->open = &archive_compressor_xz_open; f->close = archive_compressor_xz_close; f->free = archive_compressor_xz_free; f->options = &archive_compressor_xz_options; return (ARCHIVE_OK); } /* * Add an xz compression filter to this write handle. */ int archive_write_add_filter_xz(struct archive *_a) { struct archive_write_filter *f; int r; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_xz"); f = __archive_write_allocate_filter(_a); r = common_setup(f); if (r == ARCHIVE_OK) { f->code = ARCHIVE_FILTER_XZ; f->name = "xz"; } return (r); } /* LZMA is handled identically, we just need a different compression * code set. (The liblzma setup looks at the code to determine * the one place that XZ and LZMA require different handling.) */ int archive_write_add_filter_lzma(struct archive *_a) { struct archive_write_filter *f; int r; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma"); f = __archive_write_allocate_filter(_a); r = common_setup(f); if (r == ARCHIVE_OK) { f->code = ARCHIVE_FILTER_LZMA; f->name = "lzma"; } return (r); } int archive_write_add_filter_lzip(struct archive *_a) { struct archive_write_filter *f; int r; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip"); f = __archive_write_allocate_filter(_a); r = common_setup(f); if (r == ARCHIVE_OK) { f->code = ARCHIVE_FILTER_LZIP; f->name = "lzip"; } return (r); } static int archive_compressor_xz_init_stream(struct archive_write_filter *f, struct private_data *data) { static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT; int ret; #ifdef HAVE_LZMA_STREAM_ENCODER_MT lzma_mt mt_options; #endif data->stream = lzma_stream_init_data; data->stream.next_out = data->compressed; data->stream.avail_out = data->compressed_buffer_size; if (f->code == ARCHIVE_FILTER_XZ) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT if (data->threads != 1) { memset(&mt_options, 0, sizeof(mt_options)); mt_options.threads = data->threads; mt_options.timeout = 300; mt_options.filters = data->lzmafilters; mt_options.check = LZMA_CHECK_CRC64; ret = lzma_stream_encoder_mt(&(data->stream), &mt_options); } else #endif ret = lzma_stream_encoder(&(data->stream), data->lzmafilters, LZMA_CHECK_CRC64); } else if (f->code == ARCHIVE_FILTER_LZMA) { ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt); } else { /* ARCHIVE_FILTER_LZIP */ int dict_size = data->lzma_opt.dict_size; int ds, log2dic, wedges; /* Calculate a coded dictionary size */ if (dict_size < (1 << 12) || dict_size > (1 << 27)) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Unacceptable dictionary size for lzip: %d", dict_size); return (ARCHIVE_FATAL); } for (log2dic = 27; log2dic >= 12; log2dic--) { if (dict_size & (1 << log2dic)) break; } if (dict_size > (1 << log2dic)) { log2dic++; wedges = ((1 << log2dic) - dict_size) / (1 << (log2dic - 4)); } else wedges = 0; ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f); data->crc32 = 0; /* Make a header */ data->compressed[0] = 0x4C; data->compressed[1] = 0x5A; data->compressed[2] = 0x49; data->compressed[3] = 0x50; data->compressed[4] = 1;/* Version */ data->compressed[5] = (unsigned char)ds; data->stream.next_out += 6; data->stream.avail_out -= 6; ret = lzma_raw_encoder(&(data->stream), data->lzmafilters); } if (ret == LZMA_OK) return (ARCHIVE_OK); switch (ret) { case LZMA_MEM_ERROR: archive_set_error(f->archive, ENOMEM, "Internal error initializing compression library: " "Cannot allocate memory"); break; default: archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "It's a bug in liblzma"); break; } return (ARCHIVE_FATAL); } /* * Setup callback. */ static int archive_compressor_xz_open(struct archive_write_filter *f) { struct private_data *data = f->data; int ret; if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the of bytes * per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) bs = bpb; else if (bpb != 0) bs -= bs % bpb; } data->compressed_buffer_size = bs; data->compressed = (unsigned char *)malloc(data->compressed_buffer_size); if (data->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } f->write = archive_compressor_xz_write; /* Initialize compression library. */ if (f->code == ARCHIVE_FILTER_LZIP) { const struct option_value *val = &option_values[data->compression_level]; data->lzma_opt.dict_size = val->dict_size; data->lzma_opt.preset_dict = NULL; data->lzma_opt.preset_dict_size = 0; data->lzma_opt.lc = LZMA_LC_DEFAULT; data->lzma_opt.lp = LZMA_LP_DEFAULT; data->lzma_opt.pb = LZMA_PB_DEFAULT; data->lzma_opt.mode = data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL; data->lzma_opt.nice_len = val->nice_len; data->lzma_opt.mf = val->mf; data->lzma_opt.depth = 0; data->lzmafilters[0].id = LZMA_FILTER_LZMA1; data->lzmafilters[0].options = &data->lzma_opt; data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ } else { if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library"); } data->lzmafilters[0].id = LZMA_FILTER_LZMA2; data->lzmafilters[0].options = &data->lzma_opt; data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ } ret = archive_compressor_xz_init_stream(f, data); if (ret == LZMA_OK) { f->data = data; return (0); } return (ARCHIVE_FATAL); } /* * Set write options. */ static int archive_compressor_xz_options(struct archive_write_filter *f, const char *key, const char *value) { struct private_data *data = (struct private_data *)f->data; if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || value[1] != '\0') return (ARCHIVE_WARN); data->compression_level = value[0] - '0'; - if (data->compression_level > 6) - data->compression_level = 6; + if (data->compression_level > 9) + data->compression_level = 9; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { char *endptr; if (value == NULL) return (ARCHIVE_WARN); errno = 0; data->threads = (int)strtoul(value, &endptr, 10); if (errno != 0 || *endptr != '\0') { data->threads = 1; return (ARCHIVE_WARN); } if (data->threads == 0) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT data->threads = lzma_cputhreads(); #else data->threads = 1; #endif } return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } /* * Write data to the compressed stream. */ static int archive_compressor_xz_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; int ret; /* Update statistics */ data->total_in += length; if (f->code == ARCHIVE_FILTER_LZIP) data->crc32 = lzma_crc32(buff, length, data->crc32); /* Compress input data to output buffer */ data->stream.next_in = buff; data->stream.avail_in = length; if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) return (ret); return (ARCHIVE_OK); } /* * Finish the compression... */ static int archive_compressor_xz_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; int ret; ret = drive_compressor(f, data, 1); if (ret == ARCHIVE_OK) { data->total_out += data->compressed_buffer_size - data->stream.avail_out; ret = __archive_write_filter(f->next_filter, data->compressed, data->compressed_buffer_size - data->stream.avail_out); if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) { archive_le32enc(data->compressed, data->crc32); archive_le64enc(data->compressed+4, data->total_in); archive_le64enc(data->compressed+12, data->total_out + 20); ret = __archive_write_filter(f->next_filter, data->compressed, 20); } } lzma_end(&(data->stream)); return ret; } static int archive_compressor_xz_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; free(data->compressed); free(data); f->data = NULL; return (ARCHIVE_OK); } /* * Utility function to push input data through compressor, * writing full output blocks as necessary. * * Note that this handles both the regular write case (finishing == * false) and the end-of-archive case (finishing == true). */ static int drive_compressor(struct archive_write_filter *f, struct private_data *data, int finishing) { int ret; for (;;) { if (data->stream.avail_out == 0) { data->total_out += data->compressed_buffer_size; ret = __archive_write_filter(f->next_filter, data->compressed, data->compressed_buffer_size); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); data->stream.next_out = data->compressed; data->stream.avail_out = data->compressed_buffer_size; } /* If there's nothing to do, we're done. */ if (!finishing && data->stream.avail_in == 0) return (ARCHIVE_OK); ret = lzma_code(&(data->stream), finishing ? LZMA_FINISH : LZMA_RUN ); switch (ret) { case LZMA_OK: /* In non-finishing case, check if compressor * consumed everything */ if (!finishing && data->stream.avail_in == 0) return (ARCHIVE_OK); /* In finishing case, this return always means * there's more work */ break; case LZMA_STREAM_END: /* This return can only occur in finishing case. */ if (finishing) return (ARCHIVE_OK); archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "lzma compression data error"); return (ARCHIVE_FATAL); case LZMA_MEMLIMIT_ERROR: archive_set_error(f->archive, ENOMEM, "lzma compression error: " "%ju MiB would have been needed", (uintmax_t)((lzma_memusage(&(data->stream)) + 1024 * 1024 -1) / (1024 * 1024))); return (ARCHIVE_FATAL); default: /* Any other return value indicates an error. */ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "lzma compression failed:" " lzma_code() call returned status %d", ret); return (ARCHIVE_FATAL); } } } #endif /* HAVE_LZMA_H */ Index: stable/11/contrib/libarchive/libarchive/archive_write_disk_posix.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 368707) @@ -1,4540 +1,4582 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #if HAVE_SYS_XATTR_H #include #elif HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_UTIME_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_PWD_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_UTIME_H #include #endif #ifdef F_GETTIMES /* Tru64 specific */ #include #endif /* * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared. * * It assumes that the input is an integer type of no more than 64 bits. * If the number is less than zero, t must be a signed type, so it fits in * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t * without loss. But it could be a large unsigned value, so we have to clip it * to INT64_MAX.* */ #define to_int64_time(t) \ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t)) #if __APPLE__ #include #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H #include #define HAVE_QUARANTINE 1 #endif #endif #ifdef HAVE_ZLIB_H #include #endif /* TODO: Support Mac OS 'quarantine' feature. This is really just a * standard tag to mark files that have been downloaded as "tainted". * On Mac OS, we should mark the extracted files as tainted if the * archive being read was tainted. Windows has a similar feature; we * should investigate ways to support this generically. */ #include "archive.h" #include "archive_acl_private.h" #include "archive_string.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* Ignore non-int O_NOFOLLOW constant. */ /* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */ #if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX) #undef O_NOFOLLOW #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif #ifndef AT_FDCWD #define AT_FDCWD -100 #endif struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; int64_t ctime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long ctime_nanos; unsigned long fflags_set; size_t mac_metadata_size; void *mac_metadata; int fixup; /* bitmask of what needs fixing */ char *name; }; /* * We use a bitmask to track which operations remain to be done for * this file. In particular, this helps us avoid unnecessary * operations when it's possible to take care of one step as a * side-effect of another. For example, mkdir() can specify the mode * for the newly-created object but symlink() cannot. This means we * can skip chmod() if mkdir() succeeded, but we must explicitly * chmod() if we're trying to create a directory that already exists * (mkdir() failed) or if we're restoring a symlink. Similarly, we * need to verify UID/GID before trying to restore SUID/SGID bits; * that verification can occur explicitly through a stat() call or * implicitly because of a successful chown() call. */ #define TODO_MODE_FORCE 0x40000000 #define TODO_MODE_BASE 0x20000000 #define TODO_SUID 0x10000000 #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 #define TODO_APPLEDOUBLE 0x01000000 #define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) #define TODO_TIMES ARCHIVE_EXTRACT_TIME #define TODO_OWNER ARCHIVE_EXTRACT_OWNER #define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS #define TODO_ACLS ARCHIVE_EXTRACT_ACL #define TODO_XATTR ARCHIVE_EXTRACT_XATTR #define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA #define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; int64_t user_uid; int skip_file_set; int64_t skip_file_dev; int64_t skip_file_ino; time_t start_time; int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid); void (*cleanup_uid)(void *private); void *lookup_uid_data; /* * Full path of last file to satisfy symlink checks. */ struct archive_string path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ struct stat st; struct stat *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ char *name; /* Name of entry, possibly edited. */ struct archive_string _name_data; /* backing store for 'name' */ char *tmpname; /* Temporary name * */ struct archive_string _tmpname_data; /* backing store for 'tmpname' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ int deferred; /* Options requested by the client. */ int flags; /* Handle for the file we're restoring. */ int fd; /* Current offset for writing data to the file. */ int64_t offset; /* Last offset actually written to disk. */ int64_t fd_offset; /* Total bytes actually written to files. */ int64_t total_bytes_written; /* Maximum size of file, -1 if unknown. */ int64_t filesize; /* Dir we were in before this restore; only for deep paths. */ int restore_pwd; /* Mode we should use for this entry; affected by _PERM and umask. */ mode_t mode; /* UID/GID to use in restoring this entry. */ int64_t uid; int64_t gid; /* * HFS+ Compression. */ /* Xattr "com.apple.decmpfs". */ uint32_t decmpfs_attr_size; unsigned char *decmpfs_header_p; /* ResourceFork set options used for fsetxattr. */ int rsrc_xattr_options; /* Xattr "com.apple.ResourceFork". */ unsigned char *resource_fork; size_t resource_fork_allocated_size; unsigned int decmpfs_block_count; uint32_t *decmpfs_block_info; /* Buffer for compressed data. */ unsigned char *compressed_buffer; size_t compressed_buffer_size; size_t compressed_buffer_remaining; /* The offset of the ResourceFork where compressed data will * be placed. */ uint32_t compressed_rsrc_position; uint32_t compressed_rsrc_position_v; /* Buffer for uncompressed data. */ char *uncompressed_buffer; size_t block_remaining_bytes; size_t file_remaining_bytes; #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; int decmpfs_compression_level; #endif }; /* * Default mode for dirs created automatically (will be modified by umask). * Note that POSIX specifies 0777 for implicitly-created dirs, "modified * by the process' file creation mask." */ #define DEFAULT_DIR_MODE 0777 /* * Dir modes are restored in two steps: During the extraction, the permissions * in the archive are modified to match the following limits. During * the post-extract fixup pass, the permissions from the archive are * applied. */ #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 /* * Maximum uncompressed size of a decmpfs block. */ #define MAX_DECMPFS_BLOCK_SIZE (64 * 1024) /* * HFS+ compression type. */ #define CMP_XATTR 3/* Compressed data in xattr. */ #define CMP_RESOURCE_FORK 4/* Compressed data in resource fork. */ /* * HFS+ compression resource fork. */ #define RSRC_H_SIZE 260 /* Base size of Resource fork header. */ #define RSRC_F_SIZE 50 /* Size of Resource fork footer. */ /* Size to write compressed data to resource fork. */ #define COMPRESSED_W_SIZE (64 * 1024) /* decmpfs definitions. */ #define MAX_DECMPFS_XATTR_SIZE 3802 #ifndef DECMPFS_XATTR_NAME #define DECMPFS_XATTR_NAME "com.apple.decmpfs" #endif #define DECMPFS_MAGIC 0x636d7066 #define DECMPFS_COMPRESSION_MAGIC 0 #define DECMPFS_COMPRESSION_TYPE 4 #define DECMPFS_UNCOMPRESSED_SIZE 8 #define DECMPFS_HEADER_SIZE 16 #define HFS_BLOCKS(s) ((s) >> 12) static int la_opendirat(int, const char *); static int la_mktemp(struct archive_write_disk *); static void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, int); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname_fsobj(char *, int *, struct archive_string *, int); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); static ssize_t hfs_write_data_block(struct archive_write_disk *, const char *, size_t); static int fixup_appledouble(struct archive_write_disk *, const char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); static int set_mac_metadata(struct archive_write_disk *, const char *, const void *, size_t); static int set_xattrs(struct archive_write_disk *); static int clear_nochange_fflags(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_fflags_platform(struct archive_write_disk *, int fd, const char *name, mode_t mode, unsigned long fflags_set, unsigned long fflags_clear); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_time(int, int, const char *, time_t, long, time_t, long); static int set_times(struct archive_write_disk *, int, int, const char *, time_t, long, time_t, long, time_t, long, time_t, long); static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); static struct archive_vtable *archive_write_disk_vtable(void); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); static int _archive_write_disk_header(struct archive *, struct archive_entry *); static int64_t _archive_write_disk_filter_bytes(struct archive *, int); static int _archive_write_disk_finish_entry(struct archive *); static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); static int la_mktemp(struct archive_write_disk *a) { int oerrno, fd; mode_t mode; archive_string_empty(&a->_tmpname_data); archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name); a->tmpname = a->_tmpname_data.s; fd = __archive_mkstemp(a->tmpname); if (fd == -1) return -1; mode = a->mode & 0777 & ~a->user_umask; if (fchmod(fd, mode) == -1) { oerrno = errno; close(fd); errno = oerrno; return -1; } return fd; } static int la_opendirat(int fd, const char *path) { const int flags = O_CLOEXEC #if defined(O_BINARY) | O_BINARY #endif #if defined(O_DIRECTORY) | O_DIRECTORY #endif #if defined(O_PATH) | O_PATH #elif defined(O_SEARCH) | O_SEARCH #elif defined(__FreeBSD__) && defined(O_EXEC) | O_EXEC #else | O_RDONLY #endif ; #if !defined(HAVE_OPENAT) if (fd != AT_FDCWD) { errno = ENOTSUP; return (-1); } else return (open(path, flags)); #else return (openat(fd, path, flags)); #endif } static int lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ return (ARCHIVE_OK); } #ifdef HAVE_FSTAT if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } #endif /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ if (lstat(a->name, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static struct archive_vtable * archive_write_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_disk_close; av.archive_filter_bytes = _archive_write_disk_filter_bytes; av.archive_free = _archive_write_disk_free; av.archive_write_header = _archive_write_disk_header; av.archive_write_finish_entry = _archive_write_disk_finish_entry; av.archive_write_data = _archive_write_disk_data; av.archive_write_data_block = _archive_write_disk_data_block; inited = 1; } return (&av); } static int64_t _archive_write_disk_filter_bytes(struct archive *_a, int n) { struct archive_write_disk *a = (struct archive_write_disk *)_a; (void)n; /* UNUSED */ if (n == -1 || n == 0) return (a->total_bytes_written); return (-1); } int archive_write_disk_set_options(struct archive *_a, int flags) { struct archive_write_disk *a = (struct archive_write_disk *)_a; a->flags = flags; return (ARCHIVE_OK); } /* * Extract this entry to disk. * * TODO: Validate hardlinks. According to the standards, we're * supposed to check each extracted hardlink and squawk if it refers * to a file that we didn't restore. I'm not entirely convinced this * is a good idea, but more importantly: Is there any way to validate * hardlinks without keeping a complete list of filenames from the * entire archive?? Ugh. * */ static int _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; + const char *linkname; int ret, r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_header"); archive_clear_error(&a->archive); if (a->archive.state & ARCHIVE_STATE_DATA) { r = _archive_write_disk_finish_entry(&a->archive); if (r == ARCHIVE_FATAL) return (r); } /* Set up for this particular entry. */ a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->entry = archive_entry_clone(entry); a->fd = -1; a->fd_offset = 0; a->offset = 0; a->restore_pwd = -1; a->uid = a->user_uid; a->mode = archive_entry_mode(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); /* * Clean up the requested path. This is necessary for correct * dir restores; the dir restore logic otherwise gets messed * up by nonsense like "dir/.". */ ret = cleanup_pathname(a); if (ret != ARCHIVE_OK) return (ret); /* + * Check if we have a hardlink that points to itself. + */ + linkname = archive_entry_hardlink(a->entry); + if (linkname != NULL && strcmp(a->name, linkname) == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Skipping hardlink pointing to itself: %s", + a->name); + return (ARCHIVE_WARN); + } + + /* * Query the umask so we get predictable mode settings. * This gets done on every call to _write_header in case the * user edits their umask during the extraction for some * reason. */ umask(a->user_umask = umask(0)); /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; if (a->flags & ARCHIVE_EXTRACT_PERM) { a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */ /* * SGID requires an extra "check" step because we * cannot easily predict the GID that the system will * assign. (Different systems assign GIDs to files * based on a variety of criteria, including process * credentials and the gid of the enclosing * directory.) We can only restore the SGID bit if * the file has the right GID, and we only know the * GID if we either set it (see set_ownership) or if * we've actually called stat() on the file after it * was restored. Since there are several places at * which we might verify the GID, we need a TODO bit * to keep track. */ if (a->mode & S_ISGID) a->todo |= TODO_SGID | TODO_SGID_CHECK; /* * Verifying the SUID is simpler, but can still be * done in multiple ways, hence the separate "check" bit. */ if (a->mode & S_ISUID) a->todo |= TODO_SUID | TODO_SUID_CHECK; } else { /* * User didn't request full permissions, so don't * restore SUID, SGID bits and obey umask. */ a->mode &= ~S_ISUID; a->mode &= ~S_ISGID; a->mode &= ~S_ISVTX; a->mode &= ~a->user_umask; } if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) { #if ARCHIVE_ACL_DARWIN /* * On MacOS, platform ACLs get stored in mac_metadata, too. * If we intend to extract mac_metadata and it is present * we skip extracting libarchive NFSv4 ACLs. */ size_t metadata_size; if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 || archive_entry_mac_metadata(a->entry, &metadata_size) == NULL || metadata_size == 0) #endif #if ARCHIVE_ACL_LIBRICHACL /* * RichACLs are stored in an extended attribute. * If we intend to extract extended attributes and have this * attribute we skip extracting libarchive NFSv4 ACLs. */ short extract_acls = 1; if (a->flags & ARCHIVE_EXTRACT_XATTR && ( archive_entry_acl_types(a->entry) & ARCHIVE_ENTRY_ACL_TYPE_NFS4)) { const char *attr_name; const void *attr_value; size_t attr_size; int i = archive_entry_xattr_reset(a->entry); while (i--) { archive_entry_xattr_next(a->entry, &attr_name, &attr_value, &attr_size); if (attr_name != NULL && attr_value != NULL && attr_size > 0 && strcmp(attr_name, "trusted.richacl") == 0) { extract_acls = 0; break; } } } if (extract_acls) #endif #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL { #endif if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_ACLS; else a->todo |= TODO_ACLS; #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL } #endif } if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) { if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_MAC_METADATA; else a->todo |= TODO_MAC_METADATA; } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) { unsigned long set, clear; archive_entry_fflags(a->entry, &set, &clear); if ((set & ~clear) & UF_COMPRESSED) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } } if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 && (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } { const char *p; /* Check if the current file name is a type of the * resource fork file. */ p = strrchr(a->name, '/'); if (p == NULL) p = a->name; else p++; if (p[0] == '.' && p[1] == '_') { /* Do not compress "._XXX" files. */ a->todo &= ~TODO_HFS_COMPRESSION; if (a->filesize > 0) a->todo |= TODO_APPLEDOUBLE; } } #endif if (a->flags & ARCHIVE_EXTRACT_XATTR) { #if ARCHIVE_XATTR_DARWIN /* * On MacOS, extended attributes get stored in mac_metadata, * too. If we intend to extract mac_metadata and it is present * we skip extracting extended attributes. */ size_t metadata_size; if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 || archive_entry_mac_metadata(a->entry, &metadata_size) == NULL || metadata_size == 0) #endif a->todo |= TODO_XATTR; } if (a->flags & ARCHIVE_EXTRACT_FFLAGS) a->todo |= TODO_FFLAGS; if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { ret = check_symlinks(a); if (ret != ARCHIVE_OK) return (ret); } #if defined(HAVE_FCHDIR) && defined(PATH_MAX) /* If path exceeds PATH_MAX, shorten the path. */ edit_deep_directories(a); #endif ret = restore_entry(a); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) /* * Check if the filesystem the file is restoring on supports * HFS+ Compression. If not, cancel HFS+ Compression. */ if (a->todo | TODO_HFS_COMPRESSION) { /* * NOTE: UF_COMPRESSED is ignored even if the filesystem * supports HFS+ Compression because the file should * have at least an extended attribute "com.apple.decmpfs" * before the flag is set to indicate that the file have * been compressed. If the filesystem does not support * HFS+ Compression the system call will fail. */ if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0) a->todo &= ~TODO_HFS_COMPRESSION; } #endif /* * TODO: There are rumours that some extended attributes must * be restored before file data is written. If this is true, * then we either need to write all extended attributes both * before and after restoring the data, or find some rule for * determining which must go first and which last. Due to the * many ways people are using xattrs, this may prove to be an * intractable problem. */ #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { archive_set_error(&a->archive, errno, "chdir() failure"); ret = ARCHIVE_FATAL; } close(a->restore_pwd); a->restore_pwd = -1; } #endif /* * Fixup uses the unedited pathname from archive_entry_pathname(), * because it is relative to the base dir and the edited path * might be relative to some intermediate dir as a result of the * deep restore logic. */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } 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)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mode = a->mode; fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { fe->atime = archive_entry_atime(entry); fe->atime_nanos = archive_entry_atime_nsec(entry); } else { /* If atime is unset, use start time. */ fe->atime = a->start_time; 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 { /* If mtime is unset, use start time. */ fe->mtime = a->start_time; fe->mtime_nanos = 0; } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); fe->birthtime_nanos = archive_entry_birthtime_nsec( entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; fe->birthtime_nanos = fe->mtime_nanos; } } if (a->deferred & TODO_ACLS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_ACLS; archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } if (a->deferred & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { memcpy(fe->mac_metadata, metadata, metadata_size); fe->mac_metadata_size = metadata_size; fe->fixup |= TODO_MAC_METADATA; } } } if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } /* We've created the object and are ready to pour data into it. */ if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. * In particular, dirs, links, etc, don't get written to. */ if (a->fd < 0) { archive_entry_set_size(entry, 0); a->filesize = 0; } return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file"); a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } static ssize_t write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t block_size = 0, bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } if (a->flags & ARCHIVE_EXTRACT_SPARSE) { #if HAVE_STRUCT_STAT_ST_BLKSIZE int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; #else /* XXX TODO XXX Is there a more appropriate choice here ? */ /* This needn't match the filesystem allocation size. */ block_size = 16*1024; #endif } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { if (block_size == 0) { bytes_to_write = size; } else { /* We're sparsifying the file. */ const char *p, *end; int64_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { if (*p != '\0') break; } a->offset += p - buff; size -= p - buff; buff = p; if (size == 0) break; /* Calculate next block boundary after offset. */ block_end = (a->offset / block_size + 1) * block_size; /* If the adjusted write would cross block boundary, * truncate it to the block boundary. */ bytes_to_write = size; if (a->offset + bytes_to_write > block_end) bytes_to_write = block_end - a->offset; } /* Seek if necessary to the specified offset. */ if (a->offset != a->fd_offset) { if (lseek(a->fd, a->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } a->fd_offset = a->offset; } 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 += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) /* * Set UF_COMPRESSED file flag. * This have to be called after hfs_write_decmpfs() because if the * file does not have "com.apple.decmpfs" xattr the flag is ignored. */ static int hfs_set_compressed_fflag(struct archive_write_disk *a) { int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags |= UF_COMPRESSED; if (fchflags(a->fd, a->st.st_flags) != 0) { archive_set_error(&a->archive, errno, "Failed to set UF_COMPRESSED file flag"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression decmpfs * * +------------------------------+ +0 * | Magic(LE 4 bytes) | * +------------------------------+ * | Type(LE 4 bytes) | * +------------------------------+ * | Uncompressed size(LE 8 bytes)| * +------------------------------+ +16 * | | * | Compressed data | * | (Placed only if Type == 3) | * | | * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE * * Type is 3: decmpfs has compressed data. * Type is 4: Resource Fork has compressed data. */ /* * Write "com.apple.decmpfs" */ static int hfs_write_decmpfs(struct archive_write_disk *a) { int r; uint32_t compression_type; r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p, a->decmpfs_attr_size, 0, 0); if (r < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr:%s", DECMPFS_XATTR_NAME); compression_type = archive_le32dec( &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]); if (compression_type == CMP_RESOURCE_FORK) fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, XATTR_SHOWCOMPRESSION); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression Resource Fork * * +-----------------------------+ * | Header(260 bytes) | * +-----------------------------+ * | Block count(LE 4 bytes) | * +-----------------------------+ --+ * +-- | Offset (LE 4 bytes) | | * | | [distance from Block count] | | Block 0 * | +-----------------------------+ | * | | Compressed size(LE 4 bytes) | | * | +-----------------------------+ --+ * | | | * | | .................. | * | | | * | +-----------------------------+ --+ * | | Offset (LE 4 bytes) | | * | +-----------------------------+ | Block (Block count -1) * | | Compressed size(LE 4 bytes) | | * +-> +-----------------------------+ --+ * | Compressed data(n bytes) | Block 0 * +-----------------------------+ * | | * | .................. | * | | * +-----------------------------+ * | Compressed data(n bytes) | Block (Block count -1) * +-----------------------------+ * | Footer(50 bytes) | * +-----------------------------+ * */ /* * Write the header of "com.apple.ResourceFork" */ static int hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff, size_t bytes, uint32_t position) { int ret; ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes, position, a->rsrc_xattr_options); if (ret < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr: %s at %u pos %u bytes", XATTR_RESOURCEFORK_NAME, (unsigned)position, (unsigned)bytes); return (ARCHIVE_WARN); } a->rsrc_xattr_options &= ~XATTR_CREATE; return (ARCHIVE_OK); } static int hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed) { int ret; ret = hfs_write_resource_fork(a, a->compressed_buffer, bytes_compressed, a->compressed_rsrc_position); if (ret == ARCHIVE_OK) a->compressed_rsrc_position += bytes_compressed; return (ret); } static int hfs_write_resource_fork_header(struct archive_write_disk *a) { unsigned char *buff; uint32_t rsrc_bytes; uint32_t rsrc_header_bytes; /* * Write resource fork header + block info. */ buff = a->resource_fork; rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE; rsrc_header_bytes = RSRC_H_SIZE + /* Header base size. */ 4 + /* Block count. */ (a->decmpfs_block_count * 8);/* Block info */ archive_be32enc(buff, 0x100); archive_be32enc(buff + 4, rsrc_bytes); archive_be32enc(buff + 8, rsrc_bytes - 256); archive_be32enc(buff + 12, 0x32); memset(buff + 16, 0, 240); archive_be32enc(buff + 256, rsrc_bytes - 260); return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0); } static size_t hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size) { static const char rsrc_footer[RSRC_F_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm', 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (buff_size < sizeof(rsrc_footer)) return (0); memcpy(buff, rsrc_footer, sizeof(rsrc_footer)); return (sizeof(rsrc_footer)); } static int hfs_reset_compressor(struct archive_write_disk *a) { int ret; if (a->stream_valid) ret = deflateReset(&a->stream); else ret = deflateInit(&a->stream, a->decmpfs_compression_level); if (ret != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize compressor"); return (ARCHIVE_FATAL); } else a->stream_valid = 1; return (ARCHIVE_OK); } static int hfs_decompress(struct archive_write_disk *a) { uint32_t *block_info; unsigned int block_count; uint32_t data_pos, data_size; ssize_t r; ssize_t bytes_written, bytes_to_write; unsigned char *b; block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); block_count = archive_le32dec(block_info++); while (block_count--) { data_pos = RSRC_H_SIZE + archive_le32dec(block_info++); data_size = archive_le32dec(block_info++); r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME, a->compressed_buffer, data_size, data_pos, 0); if (r != data_size) { archive_set_error(&a->archive, (r < 0)?errno:ARCHIVE_ERRNO_MISC, "Failed to read resource fork"); return (ARCHIVE_WARN); } if (a->compressed_buffer[0] == 0xff) { bytes_to_write = data_size -1; b = a->compressed_buffer + 1; } else { uLong dest_len = MAX_DECMPFS_BLOCK_SIZE; int zr; zr = uncompress((Bytef *)a->uncompressed_buffer, &dest_len, a->compressed_buffer, data_size); if (zr != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to decompress resource fork"); return (ARCHIVE_WARN); } bytes_to_write = dest_len; b = (unsigned char *)a->uncompressed_buffer; } do { bytes_written = write(a->fd, b, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } bytes_to_write -= bytes_written; b += bytes_written; } while (bytes_to_write > 0); } r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0); if (r == -1) { archive_set_error(&a->archive, errno, "Failed to remove resource fork"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int hfs_drive_compressor(struct archive_write_disk *a, const char *buff, size_t size) { unsigned char *buffer_compressed; size_t bytes_compressed; size_t bytes_used; int ret; ret = hfs_reset_compressor(a); if (ret != ARCHIVE_OK) return (ret); if (a->compressed_buffer == NULL) { size_t block_size; block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE + + compressBound(MAX_DECMPFS_BLOCK_SIZE); a->compressed_buffer = malloc(block_size); if (a->compressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Resource Fork"); return (ARCHIVE_FATAL); } a->compressed_buffer_size = block_size; a->compressed_buffer_remaining = block_size; } buffer_compressed = a->compressed_buffer + a->compressed_buffer_size - a->compressed_buffer_remaining; a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; a->stream.avail_in = size; a->stream.next_out = buffer_compressed; a->stream.avail_out = a->compressed_buffer_remaining; do { ret = deflate(&a->stream, Z_FINISH); switch (ret) { case Z_OK: case Z_STREAM_END: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to compress data"); return (ARCHIVE_FAILED); } } while (ret == Z_OK); bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out; /* * If the compressed size is larger than the original size, * throw away compressed data, use uncompressed data instead. */ if (bytes_compressed > size) { buffer_compressed[0] = 0xFF;/* uncompressed marker. */ memcpy(buffer_compressed + 1, buff, size); bytes_compressed = size + 1; } a->compressed_buffer_remaining -= bytes_compressed; /* * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE * and the block count in the file is only one, store compressed * data to decmpfs xattr instead of the resource fork. */ if (a->decmpfs_block_count == 1 && (a->decmpfs_attr_size + bytes_compressed) <= MAX_DECMPFS_XATTR_SIZE) { archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_XATTR); memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE, buffer_compressed, bytes_compressed); a->decmpfs_attr_size += bytes_compressed; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); return (ret); } /* Update block info. */ archive_le32enc(a->decmpfs_block_info++, a->compressed_rsrc_position_v - RSRC_H_SIZE); archive_le32enc(a->decmpfs_block_info++, bytes_compressed); a->compressed_rsrc_position_v += bytes_compressed; /* * Write the compressed data to the resource fork. */ bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining; while (bytes_used >= COMPRESSED_W_SIZE) { ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE); if (ret != ARCHIVE_OK) return (ret); bytes_used -= COMPRESSED_W_SIZE; if (bytes_used > COMPRESSED_W_SIZE) memmove(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); else memcpy(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); } a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used; /* * If the current block is the last block, write the remaining * compressed data and the resource fork footer. */ if (a->file_remaining_bytes == 0) { size_t rsrc_size; int64_t bk; /* Append the resource footer. */ rsrc_size = hfs_set_resource_fork_footer( a->compressed_buffer + bytes_used, a->compressed_buffer_remaining); ret = hfs_write_compressed_data(a, bytes_used + rsrc_size); a->compressed_buffer_remaining = a->compressed_buffer_size; /* If the compressed size is not enough smaller than * the uncompressed size. cancel HFS+ compression. * TODO: study a behavior of ditto utility and improve * the condition to fall back into no HFS+ compression. */ bk = HFS_BLOCKS(a->compressed_rsrc_position); bk += bk >> 7; if (bk > HFS_BLOCKS(a->filesize)) return hfs_decompress(a); /* * Write the resourcefork header. */ if (ret == ARCHIVE_OK) ret = hfs_write_resource_fork_header(a); /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ if (ret == ARCHIVE_OK) ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); } return (ret); } static ssize_t hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, size_t size) { const char *buffer_to_write; size_t bytes_to_write; int ret; if (a->decmpfs_block_count == (unsigned)-1) { void *new_block; size_t new_size; unsigned int block_count; if (a->decmpfs_header_p == NULL) { new_block = malloc(MAX_DECMPFS_XATTR_SIZE + sizeof(uint32_t)); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->decmpfs_header_p = new_block; } a->decmpfs_attr_size = DECMPFS_HEADER_SIZE; archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC], DECMPFS_MAGIC); archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_RESOURCE_FORK); archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE], a->filesize); /* Calculate a block count of the file. */ block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) / MAX_DECMPFS_BLOCK_SIZE; /* * Allocate buffer for resource fork. * Set up related pointers; */ new_size = RSRC_H_SIZE + /* header */ 4 + /* Block count */ (block_count * sizeof(uint32_t) * 2) + RSRC_F_SIZE; /* footer */ if (new_size > a->resource_fork_allocated_size) { new_block = realloc(a->resource_fork, new_size); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for ResourceFork"); return (ARCHIVE_FATAL); } a->resource_fork_allocated_size = new_size; a->resource_fork = new_block; } /* Allocate uncompressed buffer */ if (a->uncompressed_buffer == NULL) { new_block = malloc(MAX_DECMPFS_BLOCK_SIZE); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->uncompressed_buffer = new_block; } a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; a->file_remaining_bytes = a->filesize; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Set up a resource fork. */ a->rsrc_xattr_options = XATTR_CREATE; /* Get the position where we are going to set a bunch * of block info. */ a->decmpfs_block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); /* Set the block count to the resource fork. */ archive_le32enc(a->decmpfs_block_info++, block_count); /* Get the position where we are going to set compressed * data. */ a->compressed_rsrc_position = RSRC_H_SIZE + 4 + (block_count * 8); a->compressed_rsrc_position_v = a->compressed_rsrc_position; a->decmpfs_block_count = block_count; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); /* Do not overrun a block size. */ if (size > a->block_remaining_bytes) bytes_to_write = a->block_remaining_bytes; else bytes_to_write = size; /* Do not overrun the file size. */ if (bytes_to_write > a->file_remaining_bytes) bytes_to_write = a->file_remaining_bytes; /* For efficiency, if a copy length is full of the uncompressed * buffer size, do not copy writing data to it. */ if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE) buffer_to_write = buff; else { memcpy(a->uncompressed_buffer + MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes, buff, bytes_to_write); buffer_to_write = a->uncompressed_buffer; } a->block_remaining_bytes -= bytes_to_write; a->file_remaining_bytes -= bytes_to_write; if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) { ret = hfs_drive_compressor(a, buffer_to_write, MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes); if (ret < 0) return (ret); a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); return (bytes_to_write); } static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { bytes_to_write = size; /* Seek if necessary to the specified offset. */ if (a->offset < a->fd_offset) { /* Can't support backward move. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek failed"); return (ARCHIVE_FATAL); } else if (a->offset > a->fd_offset) { int64_t skip = a->offset - a->fd_offset; char nullblock[1024]; memset(nullblock, 0, sizeof(nullblock)); while (skip > 0) { if (skip > (int64_t)sizeof(nullblock)) bytes_written = hfs_write_decmpfs_block( a, nullblock, sizeof(nullblock)); else bytes_written = hfs_write_decmpfs_block( a, nullblock, skip); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } skip -= bytes_written; } a->fd_offset = a->offset; } bytes_written = hfs_write_decmpfs_block(a, buff, bytes_to_write); if (bytes_written < 0) return (bytes_written); buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #else static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { return (write_data_block(a, buff, size)); } #endif static ssize_t _archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, int64_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_data_block"); a->offset = offset; if (a->todo & TODO_HFS_COMPRESSION) r = hfs_write_data_block(a, buff, size); else r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 return (ARCHIVE_OK); #else return (size); #endif } static ssize_t _archive_write_disk_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); if (a->todo & TODO_HFS_COMPRESSION) return (hfs_write_data_block(a, buff, size)); return (write_data_block(a, buff, size)); } static int _archive_write_disk_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_HEADER) return (ARCHIVE_OK); archive_clear_error(&a->archive); /* 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->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) } else if (a->todo & TODO_HFS_COMPRESSION) { char null_d[1024]; ssize_t r; if (a->file_remaining_bytes) memset(null_d, 0, sizeof(null_d)); while (a->file_remaining_bytes) { if (a->file_remaining_bytes > sizeof(null_d)) r = hfs_write_data_block( a, null_d, sizeof(null_d)); else r = hfs_write_data_block( a, null_d, a->file_remaining_bytes); if (r < 0) return ((int)r); } #endif } else { #if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } #endif /* * Not all platforms implement the XSI option to * extend files via ftruncate. Stat() the file again * to see what happened. */ a->pst = NULL; if ((ret = lazy_stat(a)) != ARCHIVE_OK) return (ret); /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ if (a->st.st_size < a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); return (ARCHIVE_FATAL); } a->pst = NULL; } } /* Restore metadata. */ /* * This is specific to Mac OS X. * If the current file is an AppleDouble file, it should be * linked with the data fork file and remove it. */ if (a->todo & TODO_APPLEDOUBLE) { int r2 = fixup_appledouble(a, a->name); if (r2 == ARCHIVE_EOF) { /* The current file has been successfully linked * with the data fork file and removed. So there * is nothing to do on the current file. */ goto finish_metadata; } if (r2 < ret) ret = r2; } /* * Look up the "real" UID only if we're going to need it. * TODO: the TODO_SGID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) { a->uid = archive_write_disk_uid(&a->archive, archive_entry_uname(a->entry), archive_entry_uid(a->entry)); } /* Look up the "real" GID only if we're going to need it. */ /* TODO: the TODO_SUID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) { a->gid = archive_write_disk_gid(&a->archive, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * Restore ownership before set_mode tries to restore suid/sgid * bits. If we set the owner, we know what it is and can skip * a stat() call to examine the ownership of the file on disk. */ if (a->todo & TODO_OWNER) { int r2 = set_ownership(a); if (r2 < ret) ret = r2; } /* * HYPOTHESIS: * If we're not root, we won't be setting any security * attributes that may be wiped by the set_mode() routine * below. We also can't set xattr on non-owner-writable files, * which may be the state after set_mode(). Perform * set_xattrs() first based on these constraints. */ if (a->user_uid != 0 && (a->todo & TODO_XATTR)) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } /* * set_mode must precede ACLs on systems such as Solaris and * FreeBSD where setting the mode implicitly clears extended ACLs */ if (a->todo & TODO_MODE) { int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } /* * Security-related extended attributes (such as * security.capability on Linux) have to be restored last, * since they're implicitly removed by other file changes. * We do this last only when root. */ if (a->user_uid == 0 && (a->todo & TODO_XATTR)) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } /* * Some flags prevent file modification; they must be restored after * file contents are written. */ if (a->todo & TODO_FFLAGS) { int r2 = set_fflags(a); if (r2 < ret) ret = r2; } /* * Time must follow most other metadata; * otherwise atime will get changed. */ if (a->todo & TODO_TIMES) { int r2 = set_times_from_entry(a); if (r2 < ret) ret = r2; } /* * Mac extended metadata includes ACLs. */ if (a->todo & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { int r2 = set_mac_metadata(a, archive_entry_pathname( a->entry), metadata, metadata_size); if (r2 < ret) ret = r2; } } /* * ACLs must be restored after timestamps because there are * ACLs that prevent attribute changes (including time). */ if (a->todo & TODO_ACLS) { int r2; r2 = archive_write_disk_set_acls(&a->archive, a->fd, archive_entry_pathname(a->entry), archive_entry_acl(a->entry), archive_entry_mode(a->entry)); if (r2 < ret) ret = r2; } finish_metadata: /* If there's an fd, we can close it now. */ if (a->fd >= 0) { close(a->fd); a->fd = -1; if (a->tmpname) { if (rename(a->tmpname, a->name) == -1) { archive_set_error(&a->archive, errno, "Failed to rename temporary file"); ret = ARCHIVE_FAILED; unlink(a->tmpname); } a->tmpname = NULL; } } /* If there's an entry, we can release it now. */ archive_entry_free(a->entry); a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup"); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); a->lookup_gid = lookup_gid; a->cleanup_gid = cleanup_gid; a->lookup_gid_data = private_data; return (ARCHIVE_OK); } int archive_write_disk_set_user_lookup(struct archive *_a, void *private_data, int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid), void (*cleanup_uid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup"); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } int64_t archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_gid"); if (a->lookup_gid) return (a->lookup_gid)(a->lookup_gid_data, name, id); return (id); } int64_t archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_uid"); if (a->lookup_uid) return (a->lookup_uid)(a->lookup_uid_data, name, id); return (id); } /* * Create a new archive_write_disk object and initialize it with global state. */ struct archive * archive_write_disk_new(void) { struct archive_write_disk *a; a = (struct archive_write_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = archive_write_disk_vtable(); a->start_time = time(NULL); /* Query and restore the umask. */ umask(a->user_umask = umask(0)); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ if (archive_string_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } #ifdef HAVE_ZLIB_H a->decmpfs_compression_level = 5; #endif return (&a->archive); } /* * If pathname is longer than PATH_MAX, chdir to a suitable * intermediate dir and edit the path down to a shorter suffix. Note * that this routine never returns an error; if the chdir() attempt * fails for any reason, we just go ahead with the long pathname. The * object creation is likely to fail, but any error will get handled * at that time. */ #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *a) { int ret; char *tail = a->name; /* If path is short, avoid the open() below. */ if (strlen(tail) < PATH_MAX) return; /* Try to record our starting dir. */ a->restore_pwd = la_opendirat(AT_FDCWD, "."); __archive_ensure_cloexec_flag(a->restore_pwd); if (a->restore_pwd < 0) return; /* As long as the path is too long... */ while (strlen(tail) >= PATH_MAX) { /* Locate a dir prefix shorter than PATH_MAX. */ tail += PATH_MAX - 8; while (tail > a->name && *tail != '/') tail--; /* Exit if we find a too-long path component. */ if (tail <= a->name) return; /* Create the intermediate dir and chdir to it. */ *tail = '\0'; /* Terminate dir portion */ ret = create_dir(a, a->name); if (ret == ARCHIVE_OK && chdir(a->name) != 0) ret = ARCHIVE_FAILED; *tail = '/'; /* Restore the / we removed. */ if (ret != ARCHIVE_OK) return; tail++; /* The chdir() succeeded; we've now shortened the path. */ a->name = tail; } return; } #endif /* * The main restore function. */ static int restore_entry(struct archive_write_disk *a) { int ret = ARCHIVE_OK, en; if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { /* * TODO: Fix this. Apparently, there are platforms * that still allow root to hose the entire filesystem * by unlinking a dir. The S_ISDIR() test above * prevents us from using unlink() here if the new * object is a dir, but that doesn't mean the old * object isn't a dir. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); return(ARCHIVE_FAILED); } } /* Try creating it first; if this fails, we'll try to recover. */ en = create_filesystem_object(a); if ((en == ENOTDIR || en == ENOENT) && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) { /* If the parent dir doesn't exist, try creating it. */ create_parent_dir(a, a->name); /* Now try to create the object again. */ en = create_filesystem_object(a); } if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, "Hard-link target '%s' does not exist.", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ if (S_ISDIR(a->mode)) { /* Don't overwrite any settings on existing directories. */ a->todo = 0; } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } /* * Some platforms return EISDIR if you call * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some * return EEXIST. POSIX is ambiguous, requiring EISDIR * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) * on an existing item. */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; /* * The SECURE_SYMLINKS logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) r = la_stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) r = lstat(a->name, &a->st); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); } /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) && !S_ISDIR(a->st.st_mode)) { if (!older(&(a->st), a->entry)) { archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } } /* If it's our archive, we're done. */ if (a->skip_file_set && a->st.st_dev == (dev_t)a->skip_file_dev && a->st.st_ino == (ino_t)a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } if (!S_ISDIR(a->st.st_mode)) { if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) && S_ISREG(a->st.st_mode)) { /* Use a temporary file to extract */ if ((a->fd = la_mktemp(a)) == -1) { archive_set_error(&a->archive, errno, "Can't create temporary file"); return ARCHIVE_FAILED; } a->pst = NULL; en = 0; } else { /* A non-dir is in the way, unlink it. */ if (unlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing " "object"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't replace existing directory with non-directory"); return (ARCHIVE_FAILED); } /* Try again. */ en = create_filesystem_object(a); } else { /* * There's a dir in the way of a dir. Don't * waste time with rmdir()/mkdir(), just fix * up the permissions on the existing dir. * Note that we don't change perms on existing * dirs unless _EXTRACT_PERM is specified. */ if ((a->mode != a->st.st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ en = 0; /* Forget the EEXIST. */ } } if (en) { /* Everything failed; give up here. */ if ((&a->archive)->error == NULL) archive_set_error(&a->archive, en, "Can't create '%s'", a->name); return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ return (ret); } /* * Returns 0 if creation succeeds, or else returns errno value from * the failed system call. Note: This function should only ever perform * a single system call. */ static int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const char *linkname; mode_t final_mode, mode; int r; /* these for check_symlinks_fsobj */ char *linkname_copy; /* non-const copy of linkname */ struct stat st; struct archive_string error_string; int error_number; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ linkname = archive_entry_hardlink(a->entry); if (linkname != NULL) { #if !HAVE_LINK return (EPERM); #else archive_string_init(&error_string); linkname_copy = strdup(linkname); if (linkname_copy == NULL) { return (EPERM); } /* * TODO: consider using the cleaned-up path as the link * target? */ r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } free(linkname_copy); archive_string_free(&error_string); /* * Unlinking and linking here is really not atomic, * but doing it right, would require us to construct * an mktemplink() function, and then use rename(2). */ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) unlink(a->name); r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. * * If the hardlink was successfully created and * the archive doesn't have carry data for it, * consider it to be non-authoritative for meta data. * This is consistent with GNU tar and BSD pax. * If the hardlink does carry data, let the last * archive entry decide ownership. */ if (r == 0 && a->filesize <= 0) { a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { #ifdef HAVE_LSTAT r = lstat(a->name, &st); #else r = la_stat(a->name, &st); #endif if (r != 0) r = errno; else if ((st.st_mode & AE_IFMT) == AE_IFREG) { a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC | O_NOFOLLOW); __archive_ensure_cloexec_flag(a->fd); if (a->fd < 0) r = errno; } } return (r); #endif } linkname = archive_entry_symlink(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK /* * Unlinking and linking here is really not atomic, * but doing it right, would require us to construct * an mktempsymlink() function, and then use rename(2). */ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) unlink(a->name); return symlink(linkname, a->name) ? errno : 0; #else return (EPERM); #endif } /* * The remaining system calls all set permissions, so let's * try to take advantage of that to avoid an extra chmod() * call. (Recall that umask is set to zero right now!) */ /* Mode we want for the final restored object (w/o file type bits). */ final_mode = a->mode & 07777; /* * The mode that will actually be restored in this step. Note * that SUID, SGID, etc, require additional work to ensure * security, so we never restore them at this point. */ mode = final_mode & 0777 & ~a->user_umask; /* * Always create writable such that [f]setxattr() works if we're not * root. */ if (a->user_uid != 0 && a->todo & (TODO_HFS_COMPRESSION | TODO_XATTR)) { mode |= 0200; } switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: a->tmpname = NULL; a->fd = open(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); __archive_ensure_cloexec_flag(a->fd); r = (a->fd < 0); break; case AE_IFCHR: #ifdef HAVE_MKNOD /* Note: we use AE_IFCHR for the case label, and * S_IFCHR for the mknod() call. This is correct. */ r = mknod(a->name, mode | S_IFCHR, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a char device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFBLK: #ifdef HAVE_MKNOD r = mknod(a->name, mode | S_IFBLK, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; r = mkdir(a->name, mode); if (r == 0) { /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; /* Never use an immediate chmod(). */ /* We can't avoid the chmod() entirely if EXTRACT_PERM * because of SysV SGID inheritance. */ if ((mode != final_mode) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; } break; case AE_IFIFO: #ifdef HAVE_MKFIFO r = mkfifo(a->name, mode); break; #else /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); #endif /* HAVE_MKFIFO */ } /* All the system calls above set errno on failure. */ if (r) return (errno); /* If we managed to set the final mode, we've avoided a chmod(). */ if (mode == final_mode) a->todo &= ~TODO_MODE; return (0); } /* * Cleanup function for archive_extract. Mostly, this involves processing * the fixup list, which is used to address a number of problems: * * Dir permissions might prevent us from restoring a file in that * dir, so we restore the dir with minimum 0700 permissions first, * then correct the mode at the end. * * Similarly, the act of restoring a file touches the directory * and changes the timestamp on the dir, so we have to touch-up dir * timestamps at the end as well. * * Some file flags can interfere with the restore by, for example, * preventing the creation of hardlinks to those files. * * Mac OS extended metadata includes ACLs, so must be deferred on dirs. * * Note that tar/cpio do not require that archives be in a particular * order; there is no way to know when the last file has been restored * within a directory, so there's no way to optimize the memory usage * here by fixing up the directory any earlier than the * end-of-archive. * * XXX TODO: Directory ACLs should be restored here, for the same * reason we set directory perms here. XXX */ static int _archive_write_disk_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; int fd, ret; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_close"); ret = _archive_write_disk_finish_entry(&a->archive); /* Sort dir list so directories are fixed up in depth-first order. */ p = sort_dir_list(a->fixup_list); while (p != NULL) { fd = -1; a->pst = NULL; /* Mark stat cache as out-of-date. */ if (p->fixup & (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { fd = open(p->name, O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); } if (p->fixup & TODO_TIMES) { set_times(a, fd, p->mode, p->name, p->atime, p->atime_nanos, p->birthtime, p->birthtime_nanos, p->mtime, p->mtime_nanos, p->ctime, p->ctime_nanos); } if (p->fixup & TODO_MODE_BASE) { #ifdef HAVE_FCHMOD if (fd >= 0) fchmod(fd, p->mode); else #endif chmod(p->name, p->mode); } if (p->fixup & TODO_ACLS) archive_write_disk_set_acls(&a->archive, fd, p->name, &p->acl, p->mode); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, fd, p->name, p->mode, p->fflags_set, 0); if (p->fixup & TODO_MAC_METADATA) set_mac_metadata(a, p->name, p->mac_metadata, p->mac_metadata_size); next = p->next; archive_acl_clear(&p->acl); free(p->mac_metadata); free(p->name); if (fd >= 0) close(fd); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_disk_free(struct archive *_a) { struct archive_write_disk *a; int ret; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free"); a = (struct archive_write_disk *)_a; ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->_tmpname_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); free(a->decmpfs_header_p); free(a->resource_fork); free(a->compressed_buffer); free(a->uncompressed_buffer); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) if (a->stream_valid) { switch (deflateEnd(&a->stream)) { case Z_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; break; } } #endif free(a); return (ret); } /* * Simple O(n log n) merge sort to order the fixup list. In * particular, we want to restore dir timestamps depth-first. */ static struct fixup_entry * sort_dir_list(struct fixup_entry *p) { struct fixup_entry *a, *b, *t; if (p == NULL) return (NULL); /* A one-item list is already sorted. */ if (p->next == NULL) return (p); /* Step 1: split the list. */ t = p; a = p->next->next; while (a != NULL) { /* Step a twice, t once. */ a = a->next; if (a != NULL) a = a->next; t = t->next; } /* Now, t is at the mid-point, so break the list here. */ b = t->next; t->next = NULL; a = p; /* Step 2: Recursively sort the two sub-lists. */ a = sort_dir_list(a); b = sort_dir_list(b); /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ if (strcmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { t = p = b; b = b->next; } /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { if (strcmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { t->next = b; b = b->next; } t = t->next; } /* Only one list is non-empty, so just splice it on. */ if (a != NULL) t->next = a; if (b != NULL) t->next = b; return (p); } /* * Returns a new, initialized fixup entry. * * TODO: Reduce the memory requirements for this list by using a tree * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive_write_disk *a, const char *pathname) { struct fixup_entry *fe; fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry)); if (fe == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a fixup"); return (NULL); } fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; fe->name = strdup(pathname); return (fe); } /* * Returns a fixup structure for the current entry. */ static struct fixup_entry * current_fixup(struct archive_write_disk *a, const char *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); return (a->current_fixup); } /* Error helper for new *_fsobj functions */ static void fsobj_error(int *a_eno, struct archive_string *a_estr, int err, const char *errstr, const char *path) { if (a_eno) *a_eno = err; if (a_estr) archive_string_sprintf(a_estr, "%s%s", errstr, path); } /* * TODO: Someday, integrate this with the deep dir support; they both * scan the path and both can be optimized by comparing against other * recent paths. */ /* * Checks the given path to see if any elements along it are symlinks. Returns * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. */ static int check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { #if !defined(HAVE_LSTAT) && \ !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ (void)error_number; /* UNUSED */ (void)error_string; /* UNUSED */ (void)flags; /* UNUSED */ return (ARCHIVE_OK); #else int res = ARCHIVE_OK; char *tail; char *head; int last; char c; int r; struct stat st; int chdir_fd; #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) int fd; #endif /* Nothing to do here if name is empty */ if(path[0] == '\0') return (ARCHIVE_OK); /* * Guard against symlink tricks. Reject any archive entry whose * destination would be altered by a symlink. * * Walk the filename in chunks separated by '/'. For each segment: * - if it doesn't exist, continue * - if it's symlink, abort or remove it * - if it's a directory and it's not the last chunk, cd into it * As we go: * head points to the current (relative) path * tail points to the temporary \0 terminating the segment we're * currently examining * c holds what used to be in *tail * last is 1 if this is the last tail */ chdir_fd = la_opendirat(AT_FDCWD, "."); __archive_ensure_cloexec_flag(chdir_fd); if (chdir_fd < 0) { fsobj_error(a_eno, a_estr, errno, "Could not open ", path); return (ARCHIVE_FATAL); } head = path; tail = path; last = 0; /* TODO: reintroduce a safe cache here? */ /* Skip the root directory if the path is absolute. */ if(tail == path && tail[0] == '/') ++tail; /* Keep going until we've checked the entire name. * head, tail, path all alias the same string, which is * temporarily zeroed at tail, so be careful restoring the * stashed (c=tail[0]) for error messages. * Exiting the loop with break is okay; continue is not. */ while (!last) { /* * Skip the separator we just consumed, plus any adjacent ones */ while (*tail == '/') ++tail; /* Skip the next path element. */ while (*tail != '\0' && *tail != '/') ++tail; /* is this the last path component? */ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0'); /* temporarily truncate the string here */ c = tail[0]; tail[0] = '\0'; /* Check that we haven't hit a symlink. */ #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW); #else r = lstat(head, &st); #endif if (r != 0) { tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) { break; } else { /* * Treat any other error as fatal - best to be * paranoid here. * Note: This effectively disables deep * directory support when security checks are * enabled. Otherwise, very long pathnames that * trigger an error here could evade the * sandbox. * TODO: We could do better, but it would * probably require merging the symlink checks * with the deep-directory editing. */ fsobj_error(a_eno, a_estr, errno, "Could not stat ", path); res = ARCHIVE_FAILED; break; } } else if (S_ISDIR(st.st_mode)) { if (!last) { #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) fd = la_opendirat(chdir_fd, head); if (fd < 0) r = -1; else { r = 0; close(chdir_fd); chdir_fd = fd; } #else r = chdir(head); #endif if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); res = (ARCHIVE_FATAL); break; } /* Our view is now from inside this dir: */ head = tail + 1; } } else if (S_ISLNK(st.st_mode)) { if (last) { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) r = unlinkat(chdir_fd, head, 0); #else r = unlink(head); #endif if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not remove symlink ", path); res = ARCHIVE_FAILED; break; } /* * Even if we did remove it, a warning * is in order. The warning is silly, * though, if we're just replacing one * symlink with another symlink. */ tail[0] = c; /* * FIXME: not sure how important this is to * restore */ /* if (!S_ISLNK(path)) { fsobj_error(a_eno, a_estr, 0, "Removing symlink ", path); } */ /* Symlink gone. No more problem! */ res = ARCHIVE_OK; break; } else if (flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) r = unlinkat(chdir_fd, head, 0); #else r = unlink(head); #endif if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot remove intervening " "symlink ", path); res = ARCHIVE_FAILED; break; } tail[0] = c; } else if ((flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) { /* * We are not the last element and we want to * follow symlinks if they are a directory. * * This is needed to extract hardlinks over * symlinks. */ #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) r = fstatat(chdir_fd, head, &st, 0); #else r = la_stat(head, &st); #endif if (r != 0) { tail[0] = c; if (errno == ENOENT) { break; } else { fsobj_error(a_eno, a_estr, errno, "Could not stat ", path); res = (ARCHIVE_FAILED); break; } } else if (S_ISDIR(st.st_mode)) { #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) fd = la_opendirat(chdir_fd, head); if (fd < 0) r = -1; else { r = 0; close(chdir_fd); chdir_fd = fd; } #else r = chdir(head); #endif if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); res = (ARCHIVE_FATAL); break; } /* * Our view is now from inside * this dir: */ head = tail + 1; } else { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot extract through " "symlink ", path); res = ARCHIVE_FAILED; break; } } else { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot extract through symlink ", path); res = ARCHIVE_FAILED; break; } } /* be sure to always maintain this */ tail[0] = c; if (tail[0] != '\0') tail++; /* Advance to the next segment. */ } /* Catches loop exits via break */ tail[0] = c; #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) /* If we operate with openat(), fstatat() and unlinkat() there was * no chdir(), so just close the fd */ if (chdir_fd >= 0) close(chdir_fd); #elif HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (chdir_fd >= 0) { r = fchdir(chdir_fd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } close(chdir_fd); chdir_fd = -1; if (r != 0) { res = (ARCHIVE_FATAL); } } #endif /* TODO: reintroduce a safe cache here? */ return res; #endif } /* * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED} */ static int check_symlinks(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); a->pst = NULL; /* to be safe */ return rc; } #if defined(__CYGWIN__) /* * 1. Convert a path separator from '\' to '/' . * We shouldn't check multibyte character directly because some * character-set have been using the '\' character for a part of * its multibyte character code. * 2. Replace unusable characters in Windows with underscore('_'). * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static void cleanup_pathname_win(char *path) { wchar_t wc; char *p; size_t alen, l; int mb, complete, utf8; alen = 0; mb = 0; complete = 1; utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0; for (p = path; *p != '\0'; p++) { ++alen; if (*p == '\\') { /* If previous byte is smaller than 128, * this is not second byte of multibyte characters, * so we can replace '\' with '/'. */ if (utf8 || !mb) *p = '/'; else complete = 0;/* uncompleted. */ } else if (*(unsigned char *)p > 127) mb = 1; else mb = 0; /* Rewrite the path name if its next character is unusable. */ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; } if (complete) return; /* * Convert path separator in wide-character. */ p = path; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == (size_t)-1) { while (*p != '\0') { if (*p == '\\') *p = '/'; ++p; } break; } if (l == 1 && wc == L'\\') *p = '/'; p += l; alen -= l; } } #endif /* * Canonicalize the pathname. In particular, this strips duplicate * '/' characters, '.' elements, and trailing '/'. It also raises an * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS * is set) if the path is absolute. */ static int cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { char *dest, *src; char separator = '\0'; dest = src = path; if (*src == '\0') { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Invalid empty ", "pathname"); return (ARCHIVE_FAILED); } #if defined(__CYGWIN__) cleanup_pathname_win(path); #endif /* Skip leading '/'. */ if (*src == '/') { if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path is ", "absolute"); return (ARCHIVE_FAILED); } separator = *src++; } /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '/' */ if (src[0] == '\0') { break; } else if (src[0] == '/') { /* Found '//', ignore second one. */ src++; continue; } else if (src[0] == '.') { if (src[1] == '\0') { /* Ignore trailing '.' */ break; } else if (src[1] == '/') { /* Skip './'. */ src += 2; continue; } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path contains ", "'..'"); return (ARCHIVE_FAILED); } } /* * Note: Under no circumstances do we * remove '..' elements. In * particular, restoring * '/foo/../bar/' should create the * 'foo' dir as a side-effect. */ } } /* Copy current element, including leading '/'. */ if (separator) *dest++ = '/'; while (*src != '\0' && *src != '/') { *dest++ = *src++; } if (*src == '\0') break; /* Skip '/' separator. */ separator = *src++; } /* * We've just copied zero or more path elements, not including the * final '/'. */ if (dest == path) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. */ if (separator) *dest++ = '/'; else *dest++ = '.'; } /* Terminate the result. */ *dest = '\0'; return (ARCHIVE_OK); } static int cleanup_pathname(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); return rc; } /* * Create the parent directory of the specified path, assuming path * is already in mutable storage. */ static int create_parent_dir(struct archive_write_disk *a, char *path) { char *slash; int r; /* Remove tail element to obtain parent name. */ slash = strrchr(path, '/'); if (slash == NULL) return (ARCHIVE_OK); *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } /* * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int create_dir(struct archive_write_disk *a, char *path) { struct stat st; struct fixup_entry *le; char *slash, *base; mode_t mode_final, mode; int r; /* Check for special names and just skip them. */ slash = strrchr(path, '/'); if (slash == NULL) base = path; else base = slash + 1; if (base[0] == '\0' || (base[0] == '.' && base[1] == '\0') || (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } return (ARCHIVE_OK); } /* * Yes, this should be stat() and not lstat(). Using lstat() * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ if (la_stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); return (ARCHIVE_FAILED); } if (unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed", path); return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; if (r != ARCHIVE_OK) return (r); } /* * Mode we want for the final restored directory. Per POSIX, * implicitly-created dirs must be created obeying the umask. * There's no mention whether this is different for privileged * restores (which the rest of this code handles by pretending * umask=0). I've chosen here to always obey the user's umask for * implicit dirs, even if _EXTRACT_PERM was specified. */ mode_final = DEFAULT_DIR_MODE & ~a->user_umask; /* Mode we want on disk during the restore process. */ mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; if (mkdir(path, mode) == 0) { if (mode != mode_final) { le = new_fixup(a, path); if (le == NULL) return (ARCHIVE_FATAL); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); } /* * Without the following check, a/b/../b/c/d fails at the * second visit to 'b', so 'd' can't be created. Note that we * don't add it to the fixup list here, as it's already been * added. */ if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); return (ARCHIVE_FAILED); } /* * Note: Although we can skip setting the user id if the desired user * id matches the current user, we cannot skip setting the group, as * many systems set the gid based on the containing directory. So * we have to perform a chown syscall if we want to set the SGID * bit. (The alternative is to stat() and then possibly chown(); it's * more efficient to skip the stat() and just always chown().) Note * that a successful chown() here clears the TODO_SGID_CHECK bit, which * allows set_mode to skip the stat() check for the GID. */ static int set_ownership(struct archive_write_disk *a) { #if !defined(__CYGWIN__) && !defined(__linux__) /* * On Linux, a process may have the CAP_CHOWN capability. * On Windows there is no 'root' user with uid 0. * Elsewhere we can skip calling chown if we are not root and the desired * user id does not match the current user. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%jd", (intmax_t)a->uid); return (ARCHIVE_WARN); } #endif #ifdef HAVE_FCHOWN /* If we have an fd, we can avoid a race. */ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif /* We prefer lchown() but will use chown() if that's all we have. */ /* Of course, if we have neither, this will always fail. */ #ifdef HAVE_LCHOWN if (lchown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #elif HAVE_CHOWN if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif archive_set_error(&a->archive, errno, "Can't set user=%jd/group=%jd for %s", (intmax_t)a->uid, (intmax_t)a->gid, a->name); return (ARCHIVE_WARN); } /* * Note: Returns 0 on success, non-zero on failure. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { /* Select the best implementation for this platform. */ #if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS) /* * utimensat() and futimens() are defined in * POSIX.1-2008. They support ns resolution and setting times * on fds and symlinks. */ struct timespec ts[2]; (void)mode; /* UNUSED */ ts[0].tv_sec = atime; ts[0].tv_nsec = atime_nsec; ts[1].tv_sec = mtime; ts[1].tv_nsec = mtime_nsec; if (fd >= 0) return futimens(fd, ts); return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW); #elif HAVE_UTIMES /* * The utimes()-family functions support µs-resolution and * setting times fds and symlinks. utimes() is documented as * LEGACY by POSIX, futimes() and lutimes() are not described * in POSIX. */ struct timeval times[2]; times[0].tv_sec = atime; times[0].tv_usec = atime_nsec / 1000; times[1].tv_sec = mtime; times[1].tv_usec = mtime_nsec / 1000; #ifdef HAVE_FUTIMES if (fd >= 0) return (futimes(fd, times)); #else (void)fd; /* UNUSED */ #endif #ifdef HAVE_LUTIMES (void)mode; /* UNUSED */ return (lutimes(name, times)); #else if (S_ISLNK(mode)) return (0); return (utimes(name, times)); #endif #elif defined(HAVE_UTIME) /* * utime() is POSIX-standard but only supports 1s resolution and * does not support fds or symlinks. */ struct utimbuf times; (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ times.actime = atime; times.modtime = mtime; if (S_ISLNK(mode)) return (ARCHIVE_OK); return (utime(name, ×)); #else /* * We don't know how to set the time on this platform. */ (void)fd; /* UNUSED */ (void)mode; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ return (ARCHIVE_WARN); #endif } #ifdef F_SETTIMES static int set_time_tru64(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec, time_t ctime, long ctime_nsec) { struct attr_timbuf tstamp; tstamp.atime.tv_sec = atime; tstamp.mtime.tv_sec = mtime; tstamp.ctime.tv_sec = ctime; #if defined (__hpux) && defined (__ia64) tstamp.atime.tv_nsec = atime_nsec; tstamp.mtime.tv_nsec = mtime_nsec; tstamp.ctime.tv_nsec = ctime_nsec; #else tstamp.atime.tv_usec = atime_nsec / 1000; tstamp.mtime.tv_usec = mtime_nsec / 1000; tstamp.ctime.tv_usec = ctime_nsec / 1000; #endif return (fcntl(fd,F_SETTIMES,&tstamp)); } #endif /* F_SETTIMES */ static int set_times(struct archive_write_disk *a, int fd, int mode, const char *name, time_t atime, long atime_nanos, time_t birthtime, long birthtime_nanos, time_t mtime, long mtime_nanos, time_t cctime, long ctime_nanos) { /* Note: set_time doesn't use libarchive return conventions! * It uses syscall conventions. So 0 here instead of ARCHIVE_OK. */ int r1 = 0, r2 = 0; #ifdef F_SETTIMES /* * on Tru64 try own fcntl first which can restore even the * ctime, fall back to default code path below if it fails * or if we are not running as root */ if (a->user_uid == 0 && set_time_tru64(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos, cctime, ctime_nanos) == 0) { return (ARCHIVE_OK); } #else /* Tru64 */ (void)cctime; /* UNUSED */ (void)ctime_nanos; /* UNUSED */ #endif /* Tru64 */ #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME /* * If you have struct stat.st_birthtime, we assume BSD * birthtime semantics, in which {f,l,}utimes() updates * birthtime to earliest mtime. So we set the time twice, * first using the birthtime, then using the mtime. If * birthtime == mtime, this isn't necessary, so we skip it. * If birthtime > mtime, then this won't work, so we skip it. */ if (birthtime < mtime || (birthtime == mtime && birthtime_nanos < mtime_nanos)) r1 = set_time(fd, mode, name, atime, atime_nanos, birthtime, birthtime_nanos); #else (void)birthtime; /* UNUSED */ (void)birthtime_nanos; /* UNUSED */ #endif r2 = set_time(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos); if (r1 != 0 || r2 != 0) { archive_set_error(&a->archive, errno, "Can't restore time"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int set_times_from_entry(struct archive_write_disk *a) { time_t atime, birthtime, mtime, cctime; long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec; /* Suitable defaults. */ atime = birthtime = mtime = cctime = a->start_time; atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0; /* If no time was provided, we're done. */ if (!archive_entry_atime_is_set(a->entry) #if HAVE_STRUCT_STAT_ST_BIRTHTIME && !archive_entry_birthtime_is_set(a->entry) #endif && !archive_entry_mtime_is_set(a->entry)) return (ARCHIVE_OK); if (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } if (archive_entry_birthtime_is_set(a->entry)) { birthtime = archive_entry_birthtime(a->entry); birthtime_nsec = archive_entry_birthtime_nsec(a->entry); } if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (archive_entry_ctime_is_set(a->entry)) { cctime = archive_entry_ctime(a->entry); ctime_nsec = archive_entry_ctime_nsec(a->entry); } return set_times(a, a->fd, a->mode, a->name, atime, atime_nsec, birthtime, birthtime_nsec, mtime, mtime_nsec, cctime, ctime_nsec); } static int set_mode(struct archive_write_disk *a, int mode) { int r = ARCHIVE_OK; int r2; mode &= 07777; /* Strip off file type bits. */ if (a->todo & TODO_SGID_CHECK) { /* * If we don't know the GID is right, we must stat() * to verify it. We can't just check the GID of this * process, since systems sometimes set GID from * the enclosing dir or based on ACLs. */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { /* * This is only an error if you * requested owner restore. If you * didn't, we'll try to restore * sgid/suid, but won't consider it a * problem if we can't. */ archive_set_error(&a->archive, -1, "Can't restore SGID bit"); r = ARCHIVE_WARN; } } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; } else if (a->todo & TODO_SUID_CHECK) { /* * If we don't know the UID is right, we can just check * the user, since all systems set the file UID from * the process UID. */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } } a->todo &= ~TODO_SUID_CHECK; } if (S_ISLNK(a->mode)) { #ifdef HAVE_LCHMOD /* * If this is a symlink, use lchmod(). If the * platform doesn't support lchmod(), just skip it. A * platform that doesn't provide a way to set * permissions on symlinks probably ignores * permissions on symlinks, so a failure here has no * impact. */ if (lchmod(a->name, mode) != 0) { switch (errno) { case ENOTSUP: case ENOSYS: #if ENOTSUP != EOPNOTSUPP case EOPNOTSUPP: #endif /* * if lchmod is defined but the platform * doesn't support it, silently ignore * error */ break; default: archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } #endif } else if (!S_ISDIR(a->mode)) { /* * If it's not a symlink and not a dir, then use * fchmod() or chmod(), depending on whether we have * an fd. Dirs get their perms set during the * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD if (a->fd >= 0) r2 = fchmod(a->fd, mode); else #endif /* If this platform lacks fchmod(), then * we'll just use chmod(). */ r2 = chmod(a->name, mode); if (r2 != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } return (r); } static int set_fflags(struct archive_write_disk *a) { struct fixup_entry *le; unsigned long set, clear; int r; mode_t mode = archive_entry_mode(a->entry); /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, * SF_IMMUTABLE prevents hardlinks from being created, so * should not be set until after any hardlinks are created. To * preserve some semblance of portability, this uses #ifdef * extensively. Ugly, but it works. * * Yes, Virginia, this does create a security race. It's mitigated * somewhat by the practice of creating dirs 0700 until the extract * is done, but it would be nice if we could do more than that. * People restoring critical file systems should be wary of * other programs that might try to muck with files as they're * being restored. */ const int critical_flags = 0 #ifdef SF_IMMUTABLE | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE | UF_IMMUTABLE #endif #ifdef SF_APPEND | SF_APPEND #endif #ifdef UF_APPEND | UF_APPEND #endif #if defined(FS_APPEND_FL) | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) | EXT2_APPEND_FL #endif #if defined(FS_IMMUTABLE_FL) | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) | EXT2_IMMUTABLE_FL #endif #ifdef FS_JOURNAL_DATA_FL | FS_JOURNAL_DATA_FL #endif ; if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); /* * The first test encourages the compiler to eliminate * all of this if it's not necessary. */ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, a->name); if (le == NULL) return (ARCHIVE_FATAL); le->fixup |= TODO_FFLAGS; le->fflags_set = set; /* Store the mode if it's not already there. */ if ((le->fixup & TODO_MODE) == 0) le->mode = mode; } else { r = set_fflags_platform(a, a->fd, a->name, mode, set, clear); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } static int clear_nochange_fflags(struct archive_write_disk *a) { mode_t mode = archive_entry_mode(a->entry); const int nochange_flags = 0 #ifdef SF_IMMUTABLE | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE | UF_IMMUTABLE #endif #ifdef SF_APPEND | SF_APPEND #endif #ifdef UF_APPEND | UF_APPEND #endif #ifdef EXT2_APPEND_FL | EXT2_APPEND_FL #endif #ifdef EXT2_IMMUTABLE_FL | EXT2_IMMUTABLE_FL #endif ; return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); } #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS) /* * BSD reads flags using stat() and sets them with one of {f,l,}chflags() */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; const int sf_mask = 0 #ifdef SF_APPEND | SF_APPEND #endif #ifdef SF_ARCHIVED | SF_ARCHIVED #endif #ifdef SF_IMMUTABLE | SF_IMMUTABLE #endif #ifdef SF_NOUNLINK | SF_NOUNLINK #endif ; (void)mode; /* UNUSED */ if (set == 0 && clear == 0) return (ARCHIVE_OK); /* * XXX Is the stat here really necessary? Or can I just use * the 'set' flags directly? In particular, I'm not sure * about the correct approach if we're overwriting an existing * file that already has flags on it. XXX */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags &= ~clear; a->st.st_flags |= set; /* Only super-user may change SF_* flags */ if (a->user_uid != 0) a->st.st_flags &= ~sf_mask; #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif /* * If we can't use the fd to set the flags, we'll use the * pathname to set flags. We prefer lchflags() but will use * chflags() if we must. */ #ifdef HAVE_LCHFLAGS if (lchflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #elif defined(HAVE_CHFLAGS) if (S_ISLNK(a->st.st_mode)) { archive_set_error(&a->archive, errno, "Can't set file flags on symlink."); return (ARCHIVE_WARN); } if (chflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif archive_set_error(&a->archive, errno, "Failed to set file flags"); return (ARCHIVE_WARN); } #elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* * Linux uses ioctl() to read and write file flags. */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int ret; int myfd = fd; int newflags, oldflags; /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but * there seem to be some Linux systems that lack these * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ const int sf_mask = 0 #if defined(FS_IMMUTABLE_FL) | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) | EXT2_IMMUTABLE_FL #endif #if defined(FS_APPEND_FL) | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) | EXT2_APPEND_FL #endif #if defined(FS_JOURNAL_DATA_FL) | FS_JOURNAL_DATA_FL #endif ; if (set == 0 && clear == 0) return (ARCHIVE_OK); /* Only regular files and dirs can have flags. */ if (!S_ISREG(mode) && !S_ISDIR(mode)) return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ if (myfd < 0) { myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(myfd); } if (myfd < 0) return (ARCHIVE_OK); /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX */ ret = ARCHIVE_OK; /* Read the current file flags. */ if (ioctl(myfd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &oldflags) < 0) goto fail; /* Try setting the flags as given. */ newflags = (oldflags & ~clear) | set; if (ioctl(myfd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &newflags) >= 0) goto cleanup; if (errno != EPERM) goto fail; /* If we couldn't set all the flags, try again with a subset. */ newflags &= ~sf_mask; oldflags &= sf_mask; newflags |= oldflags; if (ioctl(myfd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &newflags) >= 0) goto cleanup; /* We couldn't set the flags, so report the failure. */ fail: archive_set_error(&a->archive, errno, "Failed to set file flags"); ret = ARCHIVE_WARN; cleanup: if (fd < 0) close(myfd); return (ret); } #else /* * Of course, some systems have neither BSD chflags() nor Linux' flags * support through ioctl(). */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)mode; /* UNUSED */ (void)set; /* UNUSED */ (void)clear; /* UNUSED */ return (ARCHIVE_OK); } #endif /* __linux */ #ifndef HAVE_COPYFILE_H /* Default is to simply drop Mac extended metadata. */ static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ (void)metadata; /* UNUSED */ (void)metadata_size; /* UNUSED */ return (ARCHIVE_OK); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ return (ARCHIVE_OK); } #else /* * On Mac OS, we use copyfile() to unpack the metadata and * apply it to the target file. */ #if defined(HAVE_SYS_XATTR_H) static int copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) { ssize_t xattr_size; char *xattr_names = NULL, *xattr_val = NULL; int ret = ARCHIVE_OK, xattr_i; xattr_size = flistxattr(tmpfd, NULL, 0, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_names = malloc(xattr_size); if (xattr_names == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for metadata(xattr)"); ret = ARCHIVE_FATAL; goto exit_xattr; } xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } for (xattr_i = 0; xattr_i < xattr_size; xattr_i += strlen(xattr_names + xattr_i) + 1) { char *xattr_val_saved; ssize_t s; int f; s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_val_saved = xattr_val; xattr_val = realloc(xattr_val, s); if (xattr_val == NULL) { archive_set_error(&a->archive, ENOMEM, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; free(xattr_val_saved); goto exit_xattr; } s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (f == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } } exit_xattr: free(xattr_names); free(xattr_val); return (ret); } #endif static int copy_acls(struct archive_write_disk *a, int tmpfd, int dffd) { #ifndef HAVE_SYS_ACL_H return 0; #else acl_t acl, dfacl = NULL; int acl_r, ret = ARCHIVE_OK; acl = acl_get_fd(tmpfd); if (acl == NULL) { if (errno == ENOENT) /* There are not any ACLs. */ return (ret); archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } dfacl = acl_dup(acl); acl_r = acl_set_fd(dffd, dfacl); if (acl_r == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } exit_acl: if (acl) acl_free(acl); if (dfacl) acl_free(dfacl); return (ret); #endif } static int create_tempdatafork(struct archive_write_disk *a, const char *pathname) { struct archive_string tmpdatafork; int tmpfd; archive_string_init(&tmpdatafork); archive_strcpy(&tmpdatafork, "tar.md.XXXXXX"); tmpfd = mkstemp(tmpdatafork.s); if (tmpfd < 0) { archive_set_error(&a->archive, errno, "Failed to mkstemp"); archive_string_free(&tmpdatafork); return (-1); } if (copyfile(pathname, tmpdatafork.s, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); close(tmpfd); tmpfd = -1; } unlink(tmpdatafork.s); archive_string_free(&tmpdatafork); return (tmpfd); } static int copy_metadata(struct archive_write_disk *a, const char *metadata, const char *datafork, int datafork_compressed) { int ret = ARCHIVE_OK; if (datafork_compressed) { int dffd, tmpfd; tmpfd = create_tempdatafork(a, metadata); if (tmpfd == -1) return (ARCHIVE_WARN); /* * Do not open the data fork compressed by HFS+ compression * with at least a writing mode(O_RDWR or O_WRONLY). it * makes the data fork uncompressed. */ dffd = open(datafork, 0); if (dffd == -1) { archive_set_error(&a->archive, errno, "Failed to open the data fork for metadata"); close(tmpfd); return (ARCHIVE_WARN); } #if defined(HAVE_SYS_XATTR_H) ret = copy_xattrs(a, tmpfd, dffd); if (ret == ARCHIVE_OK) #endif ret = copy_acls(a, tmpfd, dffd); close(tmpfd); close(dffd); } else { if (copyfile(metadata, datafork, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } } return (ret); } static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { struct archive_string tmp; ssize_t written; int fd; int ret = ARCHIVE_OK; /* This would be simpler if copyfile() could just accept the * metadata as a block of memory; then we could sidestep this * silly dance of writing the data to disk just so that * copyfile() can read it back in again. */ archive_string_init(&tmp); archive_strcpy(&tmp, pathname); archive_strcat(&tmp, ".XXXXXX"); fd = mkstemp(tmp.s); if (fd < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); archive_string_free(&tmp); return (ARCHIVE_WARN); } written = write(fd, metadata, metadata_size); close(fd); if ((size_t)written != metadata_size) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } else { int compressed; #if defined(UF_COMPRESSED) if ((a->todo & TODO_HFS_COMPRESSION) != 0 && (ret = lazy_stat(a)) == ARCHIVE_OK) compressed = a->st.st_flags & UF_COMPRESSED; else #endif compressed = 0; ret = copy_metadata(a, tmp.s, pathname, compressed); } unlink(tmp.s); archive_string_free(&tmp); return (ret); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { char buff[8]; struct stat st; const char *p; struct archive_string datafork; int fd = -1, ret = ARCHIVE_OK; archive_string_init(&datafork); /* Check if the current file name is a type of the resource * fork file. */ p = strrchr(pathname, '/'); if (p == NULL) p = pathname; else p++; if (p[0] != '.' || p[1] != '_') goto skip_appledouble; /* * Check if the data fork file exists. * * TODO: Check if this write disk object has handled it. */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); if (lstat(datafork.s, &st) == -1 || (st.st_mode & AE_IFMT) != AE_IFREG) goto skip_appledouble; /* * Check if the file is in the AppleDouble form. */ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd == -1) { archive_set_error(&a->archive, errno, "Failed to open a restoring file"); ret = ARCHIVE_WARN; goto skip_appledouble; } if (read(fd, buff, 8) == -1) { archive_set_error(&a->archive, errno, "Failed to read a restoring file"); close(fd); ret = ARCHIVE_WARN; goto skip_appledouble; } close(fd); /* Check AppleDouble Magic Code. */ if (archive_be32dec(buff) != 0x00051607) goto skip_appledouble; /* Check AppleDouble Version. */ if (archive_be32dec(buff+4) != 0x00020000) goto skip_appledouble; ret = copy_metadata(a, pathname, datafork.s, #if defined(UF_COMPRESSED) st.st_flags & UF_COMPRESSED); #else 0); #endif if (ret == ARCHIVE_OK) { unlink(pathname); ret = ARCHIVE_EOF; } skip_appledouble: archive_string_free(&datafork); return (ret); } #endif #if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX /* * Restore extended attributes - Linux, Darwin and AIX implementations: * AIX' ea interface is syntaxwise identical to the Linux xattr interface. */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; struct archive_string errlist; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); short fail = 0; archive_string_init(&errlist); while (i--) { const char *name; const void *value; size_t size; int e; archive_entry_xattr_next(entry, &name, &value, &size); if (name == NULL) continue; #if ARCHIVE_XATTR_LINUX /* Linux: quietly skip POSIX.1e ACL extended attributes */ if (strncmp(name, "system.", 7) == 0 && (strcmp(name + 7, "posix_acl_access") == 0 || strcmp(name + 7, "posix_acl_default") == 0)) continue; if (strncmp(name, "trusted.SGI_", 12) == 0 && (strcmp(name + 12, "ACL_DEFAULT") == 0 || strcmp(name + 12, "ACL_FILE") == 0)) continue; /* Linux: xfsroot namespace is obsolete and unsupported */ if (strncmp(name, "xfsroot.", 8) == 0) { fail = 1; archive_strcat(&errlist, name); archive_strappend_char(&errlist, ' '); continue; } #endif if (a->fd >= 0) { #if ARCHIVE_XATTR_LINUX e = fsetxattr(a->fd, name, value, size, 0); #elif ARCHIVE_XATTR_DARWIN e = fsetxattr(a->fd, name, value, size, 0, 0); #elif ARCHIVE_XATTR_AIX e = fsetea(a->fd, name, value, size, 0); #endif } else { #if ARCHIVE_XATTR_LINUX e = lsetxattr(archive_entry_pathname(entry), name, value, size, 0); #elif ARCHIVE_XATTR_DARWIN e = setxattr(archive_entry_pathname(entry), name, value, size, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX e = lsetea(archive_entry_pathname(entry), name, value, size, 0); #endif } if (e == -1) { ret = ARCHIVE_WARN; archive_strcat(&errlist, name); archive_strappend_char(&errlist, ' '); if (errno != ENOTSUP && errno != ENOSYS) fail = 1; } } if (ret == ARCHIVE_WARN) { if (fail && errlist.length > 0) { errlist.length--; errlist.s[errlist.length] = '\0'; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore extended attributes: %s", errlist.s); } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore extended " "attributes on this file system."); } archive_string_free(&errlist); return (ret); } #elif ARCHIVE_XATTR_FREEBSD /* * Restore extended attributes - FreeBSD implementation */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; struct archive_string errlist; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); short fail = 0; archive_string_init(&errlist); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL) { ssize_t e; int namespace; + namespace = EXTATTR_NAMESPACE_USER; + if (strncmp(name, "user.", 5) == 0) { /* "user." attributes go to user namespace */ name += 5; namespace = EXTATTR_NAMESPACE_USER; + } else if (strncmp(name, "system.", 7) == 0) { + name += 7; + namespace = EXTATTR_NAMESPACE_SYSTEM; + if (!strcmp(name, "nfs4.acl") || + !strcmp(name, "posix1e.acl_access") || + !strcmp(name, "posix1e.acl_default")) + continue; } else { /* Other namespaces are unsupported */ archive_strcat(&errlist, name); archive_strappend_char(&errlist, ' '); fail = 1; ret = ARCHIVE_WARN; continue; } if (a->fd >= 0) { + /* + * On FreeBSD, extattr_set_fd does not + * return the same as + * extattr_set_file. It returns zero + * on success, non-zero on failure. + * + * We can detect the failure by + * manually setting errno prior to the + * call and checking after. + * + * If errno remains zero, fake the + * return value by setting e to size. + * + * This is a hack for now until I + * (Shawn Webb) get FreeBSD to fix the + * issue, if that's even possible. + */ + errno = 0; e = extattr_set_fd(a->fd, namespace, name, value, size); + if (e == 0 && errno == 0) { + e = size; + } } else { e = extattr_set_link( archive_entry_pathname(entry), namespace, name, value, size); } if (e != (ssize_t)size) { archive_strcat(&errlist, name); archive_strappend_char(&errlist, ' '); ret = ARCHIVE_WARN; if (errno != ENOTSUP && errno != ENOSYS) fail = 1; } } } if (ret == ARCHIVE_WARN) { if (fail && errlist.length > 0) { errlist.length--; errlist.s[errlist.length] = '\0'; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore extended attributes: %s", errlist.s); } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore extended " "attributes on this file system."); } archive_string_free(&errlist); return (ret); } #else /* * Restore extended attributes - stub implementation for unsupported systems */ static int set_xattrs(struct archive_write_disk *a) { static int warning_done = 0; /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); } /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); } #endif /* * Test if file on disk is older than entry. */ static int older(struct stat *st, struct archive_entry *entry) { /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry))) return (1); /* Definitely younger. */ if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry))) return (0); /* If this platform supports fractional seconds, try those. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC /* Definitely older. */ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Definitely older. */ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_N /* older. */ if (st->st_mtime_n < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_UMTIME /* older. */ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC /* older. */ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry)) return (1); #else /* This system doesn't have high-res timestamps. */ #endif /* Same age or newer, so not older. */ return (0); } #ifndef ARCHIVE_ACL_SUPPORT int archive_write_disk_set_acls(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl, __LA_MODE_T mode) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)abstract_acl; /* UNUSED */ (void)mode; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !_WIN32 || __CYGWIN__ */ Index: stable/11/contrib/libarchive/libarchive/archive_write_open.3 =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_open.3 (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_open.3 (revision 368707) @@ -1,246 +1,271 @@ .\" Copyright (c) 2003-2011 Tim Kientzle .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd February 2, 2012 +.Dd November 12, 2020 .Dt ARCHIVE_WRITE_OPEN 3 .Os .Sh NAME .Nm archive_write_open , +.Nm archive_write_open2 , .Nm archive_write_open_fd , .Nm archive_write_open_FILE , .Nm archive_write_open_filename , .Nm archive_write_open_memory .Nd functions for creating archives .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive.h .Ft int .Fo archive_write_open .Fa "struct archive *" .Fa "void *client_data" .Fa "archive_open_callback *" .Fa "archive_write_callback *" .Fa "archive_close_callback *" .Fc .Ft int +.Fo archive_write_open2 +.Fa "struct archive *" +.Fa "void *client_data" +.Fa "archive_open_callback *" +.Fa "archive_write_callback *" +.Fa "archive_close_callback *" +.Fa "archive_free_callback *" +.Fc +.Ft int .Fn archive_write_open_fd "struct archive *" "int fd" .Ft int .Fn archive_write_open_FILE "struct archive *" "FILE *file" .Ft int .Fn archive_write_open_filename "struct archive *" "const char *filename" .Ft int .Fo archive_write_open_memory .Fa "struct archive *" .Fa "void *buffer" .Fa "size_t bufferSize" .Fa "size_t *outUsed" .Fc .Sh DESCRIPTION .Bl -tag -width indent .It Fn archive_write_open Freeze the settings, open the archive, and prepare for writing entries. This is the most generic form of this function, which accepts pointers to three callback functions which will be invoked by the compression layer to write the constructed archive. This does not alter the default archive padding. +.It Fn archive_write_open2 +Same as +.Fn archive_write_open +with an additional fourth free callback. This function should be preferred to +.Fn archive_write_open . .It Fn archive_write_open_fd A convenience form of .Fn archive_write_open that accepts a file descriptor. The .Fn archive_write_open_fd function is safe for use with tape drives or other block-oriented devices. .It Fn archive_write_open_FILE A convenience form of .Fn archive_write_open that accepts a .Ft "FILE *" pointer. Note that .Fn archive_write_open_FILE is not safe for writing to tape drives or other devices that require correct blocking. .It Fn archive_write_open_file A deprecated synonym for .Fn archive_write_open_filename . .It Fn archive_write_open_filename A convenience form of .Fn archive_write_open that accepts a filename. A NULL argument indicates that the output should be written to standard output; an argument of .Dq - will open a file with that name. If you have not invoked .Fn archive_write_set_bytes_in_last_block , then .Fn archive_write_open_filename will adjust the last-block padding depending on the file: it will enable padding when writing to standard output or to a character or block device node, it will disable padding otherwise. You can override this by manually invoking .Fn archive_write_set_bytes_in_last_block before calling -.Fn archive_write_open . +.Fn archive_write_open2 . The .Fn archive_write_open_filename function is safe for use with tape drives or other block-oriented devices. .It Fn archive_write_open_memory A convenience form of -.Fn archive_write_open +.Fn archive_write_open2 that accepts a pointer to a block of memory that will receive the archive. The final .Ft "size_t *" argument points to a variable that will be updated after each write to reflect how much of the buffer is currently in use. You should be careful to ensure that this variable remains allocated until after the archive is closed. This function will disable padding unless you have specifically set the block size. .El More information about the .Va struct archive object and the overall design of the library can be found in the .Xr libarchive 3 overview. .Pp Note that the convenience forms above vary in how they block the output. See .Xr archive_write_blocksize 3 if you need to control the block size used for writes or the end-of-file padding behavior. .\" .Sh CLIENT CALLBACKS To use this library, you will need to define and register callback functions that will be invoked to write data to the resulting archive. These functions are registered by calling -.Fn archive_write_open : +.Fn archive_write_open2 : .Bl -item -offset indent .It .Ft typedef int .Fn archive_open_callback "struct archive *" "void *client_data" .El .Pp The open callback is invoked by .Fn archive_write_open . It should return .Cm ARCHIVE_OK if the underlying file or data source is successfully opened. If the open fails, it should call .Fn archive_set_error to register an error code and message and return .Cm ARCHIVE_FATAL . +Please note that if open fails, close is not called and resources must be +freed inside the open callback or with the free callback. .Bl -item -offset indent .It .Ft typedef la_ssize_t .Fo archive_write_callback .Fa "struct archive *" .Fa "void *client_data" .Fa "const void *buffer" .Fa "size_t length" .Fc .El .Pp The write callback is invoked whenever the library needs to write raw bytes to the archive. For correct blocking, each call to the write callback function should translate into a single .Xr write 2 system call. This is especially critical when writing archives to tape drives. On success, the write callback should return the number of bytes actually written. On error, the callback should invoke .Fn archive_set_error to register an error code and message and return -1. .Bl -item -offset indent .It .Ft typedef int .Fn archive_close_callback "struct archive *" "void *client_data" .El .Pp The close callback is invoked by archive_close when -the archive processing is complete. +the archive processing is complete. If the open callback fails, the close +callback is not invoked. The callback should return .Cm ARCHIVE_OK on success. On failure, the callback should invoke .Fn archive_set_error to register an error code and message and return -.Cm ARCHIVE_FATAL . +.Bl -item -offset indent +.It +.Ft typedef int +.Fn archive_free_callback "struct archive *" "void *client_data" +.El +.Pp +The free callback is always invoked on archive_free. +The return code of this callback is not processed. .Pp Note that if the client-provided write callback function returns a non-zero value, that error will be propagated back to the caller through whatever API function resulted in that call, which may include .Fn archive_write_header , .Fn archive_write_data , .Fn archive_write_close , .Fn archive_write_finish , or .Fn archive_write_free . The client callback can call .Fn archive_set_error to provide values that can then be retrieved by .Fn archive_errno and .Fn archive_error_string . .\" .Sh EXAMPLE .Sh RETURN VALUES These functions return .Cm ARCHIVE_OK on success, or .Cm ARCHIVE_FATAL . .\" .Sh ERRORS Detailed error codes and textual descriptions are available from the .Fn archive_errno and .Fn archive_error_string functions. .\" .Sh SEE ALSO .Xr tar 1 , .Xr archive_write 3 , .Xr archive_write_blocksize 3 , .Xr archive_write_filter 3 , .Xr archive_write_format 3 , .Xr archive_write_new 3 , .Xr archive_write_set_options 3 , .Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 Index: stable/11/contrib/libarchive/libarchive/archive_write_open_fd.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_open_fd.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_open_fd.c (revision 368707) @@ -1,144 +1,146 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct write_fd_data { int fd; }; -static int file_close(struct archive *, void *); +static int file_free(struct archive *, void *); static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, const void *buff, size_t); int archive_write_open_fd(struct archive *a, int fd) { struct write_fd_data *mine; mine = (struct write_fd_data *)malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } mine->fd = fd; #if defined(__CYGWIN__) || defined(_WIN32) setmode(mine->fd, O_BINARY); #endif - return (archive_write_open(a, mine, - file_open, file_write, file_close)); + return (archive_write_open2(a, mine, + file_open, file_write, NULL, file_free)); } static int file_open(struct archive *a, void *client_data) { struct write_fd_data *mine; struct stat st; mine = (struct write_fd_data *)client_data; if (fstat(mine->fd, &st) != 0) { archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd); return (ARCHIVE_FATAL); } /* * If this is a regular file, don't add it to itself. */ if (S_ISREG(st.st_mode)) archive_write_set_skip_file(a, st.st_dev, st.st_ino); /* * If client hasn't explicitly set the last block handling, * then set it here. */ if (archive_write_get_bytes_in_last_block(a) < 0) { /* If the output is a block or character device, fifo, * or stdout, pad the last block, otherwise leave it * unpadded. */ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || (mine->fd == 1)) /* Last block will be fully padded. */ archive_write_set_bytes_in_last_block(a, 0); else archive_write_set_bytes_in_last_block(a, 1); } return (ARCHIVE_OK); } static ssize_t file_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_fd_data *mine; ssize_t bytesWritten; mine = (struct write_fd_data *)client_data; for (;;) { bytesWritten = write(mine->fd, buff, length); if (bytesWritten <= 0) { if (errno == EINTR) continue; archive_set_error(a, errno, "Write error"); return (-1); } return (bytesWritten); } } static int -file_close(struct archive *a, void *client_data) +file_free(struct archive *a, void *client_data) { struct write_fd_data *mine = (struct write_fd_data *)client_data; (void)a; /* UNUSED */ + if (mine == NULL) + return (ARCHIVE_OK); free(mine); return (ARCHIVE_OK); } Index: stable/11/contrib/libarchive/libarchive/archive_write_open_file.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_open_file.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_open_file.c (revision 368707) @@ -1,109 +1,111 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct write_FILE_data { FILE *f; }; -static int file_close(struct archive *, void *); +static int file_free(struct archive *, void *); static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, const void *buff, size_t); int archive_write_open_FILE(struct archive *a, FILE *f) { struct write_FILE_data *mine; mine = (struct write_FILE_data *)malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } mine->f = f; - return (archive_write_open(a, mine, - file_open, file_write, file_close)); + return (archive_write_open2(a, mine, file_open, file_write, + NULL, file_free)); } static int file_open(struct archive *a, void *client_data) { (void)a; /* UNUSED */ (void)client_data; /* UNUSED */ return (ARCHIVE_OK); } static ssize_t file_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_FILE_data *mine; size_t bytesWritten; mine = client_data; for (;;) { bytesWritten = fwrite(buff, 1, length, mine->f); if (bytesWritten <= 0) { if (errno == EINTR) continue; archive_set_error(a, errno, "Write error"); return (-1); } return (bytesWritten); } } static int -file_close(struct archive *a, void *client_data) +file_free(struct archive *a, void *client_data) { struct write_FILE_data *mine = client_data; (void)a; /* UNUSED */ + if (mine == NULL) + return (ARCHIVE_OK); free(mine); return (ARCHIVE_OK); } Index: stable/11/contrib/libarchive/libarchive/archive_write_open_filename.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_open_filename.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_open_filename.c (revision 368707) @@ -1,253 +1,270 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_string.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif struct write_file_data { int fd; struct archive_mstring filename; }; static int file_close(struct archive *, void *); +static int file_free(struct archive *, void *); static int file_open(struct archive *, void *); static ssize_t file_write(struct archive *, void *, const void *buff, size_t); static int open_filename(struct archive *, int, const void *); int archive_write_open_file(struct archive *a, const char *filename) { return (archive_write_open_filename(a, filename)); } int archive_write_open_filename(struct archive *a, const char *filename) { if (filename == NULL || filename[0] == '\0') return (archive_write_open_fd(a, 1)); return (open_filename(a, 1, filename)); } int archive_write_open_filename_w(struct archive *a, const wchar_t *filename) { if (filename == NULL || filename[0] == L'\0') return (archive_write_open_fd(a, 1)); return (open_filename(a, 0, filename)); } static int open_filename(struct archive *a, int mbs_fn, const void *filename) { struct write_file_data *mine; int r; mine = (struct write_file_data *)calloc(1, sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } if (mbs_fn) r = archive_mstring_copy_mbs(&mine->filename, filename); else r = archive_mstring_copy_wcs(&mine->filename, filename); if (r < 0) { if (errno == ENOMEM) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } if (mbs_fn) archive_set_error(a, ARCHIVE_ERRNO_MISC, "Can't convert '%s' to WCS", (const char *)filename); else archive_set_error(a, ARCHIVE_ERRNO_MISC, "Can't convert '%S' to MBS", (const wchar_t *)filename); return (ARCHIVE_FAILED); } mine->fd = -1; - return (archive_write_open(a, mine, - file_open, file_write, file_close)); + return (archive_write_open2(a, mine, + file_open, file_write, file_close, file_free)); } static int file_open(struct archive *a, void *client_data) { int flags; struct write_file_data *mine; struct stat st; #if defined(_WIN32) && !defined(__CYGWIN__) wchar_t *fullpath; #endif const wchar_t *wcs; const char *mbs; mine = (struct write_file_data *)client_data; flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC; /* * Open the file. */ mbs = NULL; wcs = NULL; #if defined(_WIN32) && !defined(__CYGWIN__) if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) { if (errno == ENOMEM) archive_set_error(a, errno, "No memory"); else { archive_mstring_get_mbs(a, &mine->filename, &mbs); archive_set_error(a, errno, "Can't convert '%s' to WCS", mbs); } return (ARCHIVE_FATAL); } fullpath = __la_win_permissive_name_w(wcs); if (fullpath != NULL) { mine->fd = _wopen(fullpath, flags, 0666); free(fullpath); } else mine->fd = _wopen(wcs, flags, 0666); #else if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) { if (errno == ENOMEM) archive_set_error(a, errno, "No memory"); else { archive_mstring_get_wcs(a, &mine->filename, &wcs); archive_set_error(a, errno, "Can't convert '%S' to MBS", wcs); } return (ARCHIVE_FATAL); } mine->fd = open(mbs, flags, 0666); __archive_ensure_cloexec_flag(mine->fd); #endif if (mine->fd < 0) { if (mbs != NULL) archive_set_error(a, errno, "Failed to open '%s'", mbs); else archive_set_error(a, errno, "Failed to open '%S'", wcs); return (ARCHIVE_FATAL); } if (fstat(mine->fd, &st) != 0) { if (mbs != NULL) archive_set_error(a, errno, "Couldn't stat '%s'", mbs); else archive_set_error(a, errno, "Couldn't stat '%S'", wcs); return (ARCHIVE_FATAL); } /* * Set up default last block handling. */ if (archive_write_get_bytes_in_last_block(a) < 0) { if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode)) /* Pad last block when writing to device or FIFO. */ archive_write_set_bytes_in_last_block(a, 0); else /* Don't pad last block otherwise. */ archive_write_set_bytes_in_last_block(a, 1); } /* * If the output file is a regular file, don't add it to * itself. If it's a device file, it's okay to add the device * entry to the output archive. */ if (S_ISREG(st.st_mode)) archive_write_set_skip_file(a, st.st_dev, st.st_ino); return (ARCHIVE_OK); } static ssize_t file_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_file_data *mine; ssize_t bytesWritten; mine = (struct write_file_data *)client_data; for (;;) { bytesWritten = write(mine->fd, buff, length); if (bytesWritten <= 0) { if (errno == EINTR) continue; archive_set_error(a, errno, "Write error"); return (-1); } return (bytesWritten); } } static int file_close(struct archive *a, void *client_data) { struct write_file_data *mine = (struct write_file_data *)client_data; (void)a; /* UNUSED */ + if (mine == NULL) + return (ARCHIVE_FATAL); + if (mine->fd >= 0) close(mine->fd); + + return (ARCHIVE_OK); +} + +static int +file_free(struct archive *a, void *client_data) +{ + struct write_file_data *mine = (struct write_file_data *)client_data; + + (void)a; /* UNUSED */ + + if (mine == NULL) + return (ARCHIVE_OK); archive_mstring_clean(&mine->filename); free(mine); return (ARCHIVE_OK); } Index: stable/11/contrib/libarchive/libarchive/archive_write_open_memory.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_open_memory.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_open_memory.c (revision 368707) @@ -1,113 +1,115 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #include #include #include #include "archive.h" struct write_memory_data { size_t used; size_t size; size_t * client_size; unsigned char * buff; }; -static int memory_write_close(struct archive *, void *); +static int memory_write_free(struct archive *, void *); static int memory_write_open(struct archive *, void *); static ssize_t memory_write(struct archive *, void *, const void *buff, size_t); /* * Client provides a pointer to a block of memory to receive * the data. The 'size' param both tells us the size of the * client buffer and lets us tell the client the final size. */ int archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used) { struct write_memory_data *mine; mine = (struct write_memory_data *)calloc(1, sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } mine->buff = buff; mine->size = buffSize; mine->client_size = used; - return (archive_write_open(a, mine, - memory_write_open, memory_write, memory_write_close)); + return (archive_write_open2(a, mine, + memory_write_open, memory_write, NULL, memory_write_free)); } static int memory_write_open(struct archive *a, void *client_data) { struct write_memory_data *mine; mine = client_data; mine->used = 0; if (mine->client_size != NULL) *mine->client_size = mine->used; /* Disable padding if it hasn't been set explicitly. */ if (-1 == archive_write_get_bytes_in_last_block(a)) archive_write_set_bytes_in_last_block(a, 1); return (ARCHIVE_OK); } /* * Copy the data into the client buffer. * Note that we update mine->client_size on every write. * In particular, this means the client can follow exactly * how much has been written into their buffer at any time. */ static ssize_t memory_write(struct archive *a, void *client_data, const void *buff, size_t length) { struct write_memory_data *mine; mine = client_data; if (mine->used + length > mine->size) { archive_set_error(a, ENOMEM, "Buffer exhausted"); return (ARCHIVE_FATAL); } memcpy(mine->buff + mine->used, buff, length); mine->used += length; if (mine->client_size != NULL) *mine->client_size = mine->used; return (length); } static int -memory_write_close(struct archive *a, void *client_data) +memory_write_free(struct archive *a, void *client_data) { struct write_memory_data *mine; (void)a; /* UNUSED */ mine = client_data; + if (mine == NULL) + return (ARCHIVE_OK); free(mine); return (ARCHIVE_OK); } Index: stable/11/contrib/libarchive/libarchive/archive_write_private.h =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_private.h (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_private.h (revision 368707) @@ -1,164 +1,165 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED #define ARCHIVE_WRITE_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif #include "archive.h" #include "archive_string.h" #include "archive_private.h" #define ARCHIVE_WRITE_FILTER_STATE_NEW 1U #define ARCHIVE_WRITE_FILTER_STATE_OPEN 2U #define ARCHIVE_WRITE_FILTER_STATE_CLOSED 4U #define ARCHIVE_WRITE_FILTER_STATE_FATAL 0x8000U struct archive_write; struct archive_write_filter { int64_t bytes_written; struct archive *archive; /* Associated archive. */ struct archive_write_filter *next_filter; /* Who I write to. */ int (*options)(struct archive_write_filter *, const char *key, const char *value); int (*open)(struct archive_write_filter *); int (*write)(struct archive_write_filter *, const void *, size_t); int (*close)(struct archive_write_filter *); int (*free)(struct archive_write_filter *); void *data; const char *name; int code; int bytes_per_block; int bytes_in_last_block; int state; }; #if ARCHIVE_VERSION < 4000000 void __archive_write_filters_free(struct archive *); #endif struct archive_write_filter *__archive_write_allocate_filter(struct archive *); int __archive_write_output(struct archive_write *, const void *, size_t); int __archive_write_nulls(struct archive_write *, size_t); int __archive_write_filter(struct archive_write_filter *, const void *, size_t); struct archive_write { struct archive archive; /* Dev/ino of the archive being written. */ int skip_file_set; int64_t skip_file_dev; int64_t skip_file_ino; /* Utility: Pointer to a block of nulls. */ const unsigned char *nulls; size_t null_length; /* Callbacks to open/read/write/close archive stream. */ archive_open_callback *client_opener; archive_write_callback *client_writer; archive_close_callback *client_closer; + archive_free_callback *client_freer; void *client_data; /* * Blocking information. Note that bytes_in_last_block is * misleadingly named; I should find a better name. These * control the final output from all compressors, including * compression_none. */ int bytes_per_block; int bytes_in_last_block; /* * First and last write filters in the pipeline. */ struct archive_write_filter *filter_first; struct archive_write_filter *filter_last; /* * Pointers to format-specific functions for writing. They're * initialized by archive_write_set_format_XXX() calls. */ void *format_data; const char *format_name; int (*format_init)(struct archive_write *); int (*format_options)(struct archive_write *, const char *key, const char *value); int (*format_finish_entry)(struct archive_write *); int (*format_write_header)(struct archive_write *, struct archive_entry *); ssize_t (*format_write_data)(struct archive_write *, const void *buff, size_t); int (*format_close)(struct archive_write *); int (*format_free)(struct archive_write *); /* * Encryption passphrase. */ char *passphrase; archive_passphrase_callback *passphrase_callback; void *passphrase_client_data; }; /* * Utility function to format a USTAR header into a buffer. If * "strict" is set, this tries to create the absolutely most portable * version of a ustar header. If "strict" is set to 0, then it will * relax certain requirements. * * Generally, format-specific declarations don't belong in this * header; this is a rare example of a function that is shared by * two very similar formats (ustar and pax). */ int __archive_write_format_header_ustar(struct archive_write *, char buff[512], struct archive_entry *, int tartype, int strict, struct archive_string_conv *); struct archive_write_program_data; struct archive_write_program_data * __archive_write_program_allocate(const char *program_name); int __archive_write_program_free(struct archive_write_program_data *); int __archive_write_program_open(struct archive_write_filter *, struct archive_write_program_data *, const char *); int __archive_write_program_close(struct archive_write_filter *, struct archive_write_program_data *); int __archive_write_program_write(struct archive_write_filter *, struct archive_write_program_data *, const void *, size_t); /* * Get a encryption passphrase. */ const char * __archive_write_get_passphrase(struct archive_write *a); #endif Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_7zip.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_7zip.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_7zip.c (revision 368707) @@ -1,2317 +1,2317 @@ /*- * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_BZLIB_H #include #endif #if HAVE_LZMA_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_string.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" /* * Codec ID */ #define _7Z_COPY 0 #define _7Z_LZMA1 0x030101 #define _7Z_LZMA2 0x21 #define _7Z_DEFLATE 0x040108 #define _7Z_BZIP2 0x040202 #define _7Z_PPMD 0x030401 /* * 7-Zip header property IDs. */ #define kEnd 0x00 #define kHeader 0x01 #define kArchiveProperties 0x02 #define kAdditionalStreamsInfo 0x03 #define kMainStreamsInfo 0x04 #define kFilesInfo 0x05 #define kPackInfo 0x06 #define kUnPackInfo 0x07 #define kSubStreamsInfo 0x08 #define kSize 0x09 #define kCRC 0x0A #define kFolder 0x0B #define kCodersUnPackSize 0x0C #define kNumUnPackStream 0x0D #define kEmptyStream 0x0E #define kEmptyFile 0x0F #define kAnti 0x10 #define kName 0x11 #define kCTime 0x12 #define kATime 0x13 #define kMTime 0x14 #define kAttributes 0x15 #define kEncodedHeader 0x17 enum la_zaction { ARCHIVE_Z_FINISH, ARCHIVE_Z_RUN }; /* * A stream object of universal compressor. */ struct la_zstream { const uint8_t *next_in; size_t avail_in; uint64_t total_in; uint8_t *next_out; size_t avail_out; uint64_t total_out; uint32_t prop_size; uint8_t *props; int valid; void *real_stream; int (*code) (struct archive *a, struct la_zstream *lastrm, enum la_zaction action); int (*end)(struct archive *a, struct la_zstream *lastrm); }; #define PPMD7_DEFAULT_ORDER 6 #define PPMD7_DEFAULT_MEM_SIZE (1 << 24) struct ppmd_stream { int stat; CPpmd7 ppmd7_context; CPpmd7z_RangeEnc range_enc; IByteOut byteout; uint8_t *buff; uint8_t *buff_ptr; uint8_t *buff_end; size_t buff_bytes; }; struct coder { unsigned codec; size_t prop_size; uint8_t *props; }; struct file { struct archive_rb_node rbnode; struct file *next; unsigned name_len; uint8_t *utf16name;/* UTF16-LE name. */ uint64_t size; unsigned flg; #define MTIME_IS_SET (1<<0) #define ATIME_IS_SET (1<<1) #define CTIME_IS_SET (1<<2) #define CRC32_IS_SET (1<<3) #define HAS_STREAM (1<<4) struct { time_t time; long time_ns; } times[3]; #define MTIME 0 #define ATIME 1 #define CTIME 2 mode_t mode; uint32_t crc32; signed int dir:1; }; struct _7zip { int temp_fd; uint64_t temp_offset; struct file *cur_file; size_t total_number_entry; size_t total_number_nonempty_entry; size_t total_number_empty_entry; size_t total_number_dir_entry; size_t total_bytes_entry_name; size_t total_number_time_defined[3]; uint64_t total_bytes_compressed; uint64_t total_bytes_uncompressed; uint64_t entry_bytes_remaining; uint32_t entry_crc32; uint32_t precode_crc32; uint32_t encoded_crc32; int crc32flg; #define PRECODE_CRC32 1 #define ENCODED_CRC32 2 unsigned opt_compression; int opt_compression_level; struct la_zstream stream; struct coder coder; struct archive_string_conv *sconv; /* * Compressed data buffer. */ unsigned char wbuff[512 * 20 * 6]; size_t wbuff_remaining; /* * The list of the file entries which has its contents is used to * manage struct file objects. * We use 'next' (a member of struct file) to chain. */ struct { struct file *first; struct file **last; } file_list, empty_list; struct archive_rb_tree rbtree;/* for empty files */ }; static int _7z_options(struct archive_write *, const char *, const char *); static int _7z_write_header(struct archive_write *, struct archive_entry *); static ssize_t _7z_write_data(struct archive_write *, const void *, size_t); static int _7z_finish_entry(struct archive_write *); static int _7z_close(struct archive_write *); static int _7z_free(struct archive_write *); static int file_cmp_node(const struct archive_rb_node *, const struct archive_rb_node *); static int file_cmp_key(const struct archive_rb_node *, const void *); static int file_new(struct archive_write *a, struct archive_entry *, struct file **); static void file_free(struct file *); static void file_register(struct _7zip *, struct file *); static void file_register_empty(struct _7zip *, struct file *); static void file_init_register(struct _7zip *); static void file_init_register_empty(struct _7zip *); static void file_free_register(struct _7zip *); static ssize_t compress_out(struct archive_write *, const void *, size_t , enum la_zaction); static int compression_init_encoder_copy(struct archive *, struct la_zstream *); static int compression_code_copy(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_copy(struct archive *, struct la_zstream *); static int compression_init_encoder_deflate(struct archive *, struct la_zstream *, int, int); #ifdef HAVE_ZLIB_H static int compression_code_deflate(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_deflate(struct archive *, struct la_zstream *); #endif static int compression_init_encoder_bzip2(struct archive *, struct la_zstream *, int); #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) static int compression_code_bzip2(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_bzip2(struct archive *, struct la_zstream *); #endif static int compression_init_encoder_lzma1(struct archive *, struct la_zstream *, int); static int compression_init_encoder_lzma2(struct archive *, struct la_zstream *, int); #if defined(HAVE_LZMA_H) static int compression_code_lzma(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_lzma(struct archive *, struct la_zstream *); #endif static int compression_init_encoder_ppmd(struct archive *, struct la_zstream *, unsigned, uint32_t); static int compression_code_ppmd(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_ppmd(struct archive *, struct la_zstream *); static int _7z_compression_init_encoder(struct archive_write *, unsigned, int); static int compression_code(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end(struct archive *, struct la_zstream *); static int enc_uint64(struct archive_write *, uint64_t); static int make_header(struct archive_write *, uint64_t, uint64_t, uint64_t, int, struct coder *); static int make_streamsInfo(struct archive_write *, uint64_t, uint64_t, uint64_t, int, struct coder *, int, uint32_t); int archive_write_set_format_7zip(struct archive *_a) { static const struct archive_rb_tree_ops rb_ops = { file_cmp_node, file_cmp_key }; struct archive_write *a = (struct archive_write *)_a; struct _7zip *zip; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_7zip"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); zip = calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate 7-Zip data"); return (ARCHIVE_FATAL); } zip->temp_fd = -1; __archive_rb_tree_init(&(zip->rbtree), &rb_ops); file_init_register(zip); file_init_register_empty(zip); /* Set default compression type and its level. */ #if HAVE_LZMA_H zip->opt_compression = _7Z_LZMA1; #elif defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) zip->opt_compression = _7Z_BZIP2; #elif defined(HAVE_ZLIB_H) zip->opt_compression = _7Z_DEFLATE; #else zip->opt_compression = _7Z_COPY; #endif zip->opt_compression_level = 6; a->format_data = zip; a->format_name = "7zip"; a->format_options = _7z_options; a->format_write_header = _7z_write_header; a->format_write_data = _7z_write_data; a->format_finish_entry = _7z_finish_entry; a->format_close = _7z_close; a->format_free = _7z_free; a->archive.archive_format = ARCHIVE_FORMAT_7ZIP; a->archive.archive_format_name = "7zip"; return (ARCHIVE_OK); } static int _7z_options(struct archive_write *a, const char *key, const char *value) { struct _7zip *zip; zip = (struct _7zip *)a->format_data; if (strcmp(key, "compression") == 0) { const char *name = NULL; if (value == NULL || strcmp(value, "copy") == 0 || strcmp(value, "COPY") == 0 || strcmp(value, "store") == 0 || strcmp(value, "STORE") == 0) zip->opt_compression = _7Z_COPY; else if (strcmp(value, "deflate") == 0 || strcmp(value, "DEFLATE") == 0) #if HAVE_ZLIB_H zip->opt_compression = _7Z_DEFLATE; #else name = "deflate"; #endif else if (strcmp(value, "bzip2") == 0 || strcmp(value, "BZIP2") == 0) #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) zip->opt_compression = _7Z_BZIP2; #else name = "bzip2"; #endif else if (strcmp(value, "lzma1") == 0 || strcmp(value, "LZMA1") == 0) #if HAVE_LZMA_H zip->opt_compression = _7Z_LZMA1; #else name = "lzma1"; #endif else if (strcmp(value, "lzma2") == 0 || strcmp(value, "LZMA2") == 0) #if HAVE_LZMA_H zip->opt_compression = _7Z_LZMA2; #else name = "lzma2"; #endif else if (strcmp(value, "ppmd") == 0 || strcmp(value, "PPMD") == 0 || strcmp(value, "PPMd") == 0) zip->opt_compression = _7Z_PPMD; else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unknown compression name: `%s'", value); return (ARCHIVE_FAILED); } if (name != NULL) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "`%s' compression not supported " "on this platform", name); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || value[1] != '\0') { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Illegal value `%s'", value); return (ARCHIVE_FAILED); } zip->opt_compression_level = value[0] - '0'; return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int _7z_write_header(struct archive_write *a, struct archive_entry *entry) { struct _7zip *zip; struct file *file; int r; zip = (struct _7zip *)a->format_data; zip->cur_file = NULL; zip->entry_bytes_remaining = 0; if (zip->sconv == NULL) { zip->sconv = archive_string_conversion_to_charset( &a->archive, "UTF-16LE", 1); if (zip->sconv == NULL) return (ARCHIVE_FATAL); } r = file_new(a, entry, &file); if (r < ARCHIVE_WARN) { if (file != NULL) file_free(file); return (r); } if (file->size == 0 && file->dir) { if (!__archive_rb_tree_insert_node(&(zip->rbtree), (struct archive_rb_node *)file)) { /* We have already had the same file. */ file_free(file); return (ARCHIVE_OK); } } if (file->flg & MTIME_IS_SET) zip->total_number_time_defined[MTIME]++; if (file->flg & CTIME_IS_SET) zip->total_number_time_defined[CTIME]++; if (file->flg & ATIME_IS_SET) zip->total_number_time_defined[ATIME]++; zip->total_number_entry++; zip->total_bytes_entry_name += file->name_len + 2; if (file->size == 0) { /* Count up the number of empty files. */ zip->total_number_empty_entry++; if (file->dir) zip->total_number_dir_entry++; else file_register_empty(zip, file); return (r); } /* * Init compression. */ if ((zip->total_number_entry - zip->total_number_empty_entry) == 1) { r = _7z_compression_init_encoder(a, zip->opt_compression, zip->opt_compression_level); if (r < 0) { file_free(file); return (ARCHIVE_FATAL); } } /* Register a non-empty file. */ file_register(zip, file); /* * Set the current file to cur_file to read its contents. */ zip->cur_file = file; /* Save a offset of current file in temporary file. */ zip->entry_bytes_remaining = file->size; zip->entry_crc32 = 0; /* * Store a symbolic link name as file contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { ssize_t bytes; const void *p = (const void *)archive_entry_symlink(entry); bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN); if (bytes < 0) return ((int)bytes); zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes); zip->entry_bytes_remaining -= bytes; } return (r); } /* * Write data to a temporary file. */ static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct _7zip *zip; const unsigned char *p; ssize_t ws; zip = (struct _7zip *)a->format_data; /* * Open a temporary file. */ if (zip->temp_fd == -1) { zip->temp_offset = 0; zip->temp_fd = __archive_mktemp(NULL); if (zip->temp_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } } p = (const unsigned char *)buff; while (s) { ws = write(zip->temp_fd, p, s); if (ws < 0) { archive_set_error(&(a->archive), errno, "fwrite function failed"); return (ARCHIVE_FATAL); } s -= ws; p += ws; zip->temp_offset += ws; } return (ARCHIVE_OK); } static ssize_t compress_out(struct archive_write *a, const void *buff, size_t s, enum la_zaction run) { struct _7zip *zip = (struct _7zip *)a->format_data; int r; if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0) return (0); if ((zip->crc32flg & PRECODE_CRC32) && s) zip->precode_crc32 = crc32(zip->precode_crc32, buff, (unsigned)s); zip->stream.next_in = (const unsigned char *)buff; zip->stream.avail_in = s; for (;;) { /* Compress file data. */ r = compression_code(&(a->archive), &(zip->stream), run); if (r != ARCHIVE_OK && r != ARCHIVE_EOF) return (ARCHIVE_FATAL); if (zip->stream.avail_out == 0) { if (write_to_temp(a, zip->wbuff, sizeof(zip->wbuff)) != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->stream.next_out = zip->wbuff; zip->stream.avail_out = sizeof(zip->wbuff); if (zip->crc32flg & ENCODED_CRC32) zip->encoded_crc32 = crc32(zip->encoded_crc32, zip->wbuff, sizeof(zip->wbuff)); if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF) continue; } if (zip->stream.avail_in == 0) break; } if (run == ARCHIVE_Z_FINISH) { uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out; if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); if ((zip->crc32flg & ENCODED_CRC32) && bytes) zip->encoded_crc32 = crc32(zip->encoded_crc32, zip->wbuff, (unsigned)bytes); } return (s); } static ssize_t _7z_write_data(struct archive_write *a, const void *buff, size_t s) { struct _7zip *zip; ssize_t bytes; zip = (struct _7zip *)a->format_data; if (s > zip->entry_bytes_remaining) s = (size_t)zip->entry_bytes_remaining; if (s == 0 || zip->cur_file == NULL) return (0); bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN); if (bytes < 0) return (bytes); zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes); zip->entry_bytes_remaining -= bytes; return (bytes); } static int _7z_finish_entry(struct archive_write *a) { struct _7zip *zip; size_t s; ssize_t r; zip = (struct _7zip *)a->format_data; if (zip->cur_file == NULL) return (ARCHIVE_OK); while (zip->entry_bytes_remaining > 0) { s = (size_t)zip->entry_bytes_remaining; if (s > a->null_length) s = a->null_length; r = _7z_write_data(a, a->nulls, s); if (r < 0) return ((int)r); } zip->total_bytes_compressed += zip->stream.total_in; zip->total_bytes_uncompressed += zip->stream.total_out; zip->cur_file->crc32 = zip->entry_crc32; zip->cur_file = NULL; return (ARCHIVE_OK); } static int flush_wbuff(struct archive_write *a) { struct _7zip *zip; int r; size_t s; zip = (struct _7zip *)a->format_data; s = sizeof(zip->wbuff) - zip->wbuff_remaining; r = __archive_write_output(a, zip->wbuff, s); if (r != ARCHIVE_OK) return (r); zip->wbuff_remaining = sizeof(zip->wbuff); return (r); } static int copy_out(struct archive_write *a, uint64_t offset, uint64_t length) { struct _7zip *zip; int r; zip = (struct _7zip *)a->format_data; if (zip->temp_offset > 0 && lseek(zip->temp_fd, offset, SEEK_SET) < 0) { archive_set_error(&(a->archive), errno, "lseek failed"); return (ARCHIVE_FATAL); } while (length) { size_t rsize; ssize_t rs; unsigned char *wb; if (length > zip->wbuff_remaining) rsize = zip->wbuff_remaining; else rsize = (size_t)length; wb = zip->wbuff + (sizeof(zip->wbuff) - zip->wbuff_remaining); rs = read(zip->temp_fd, wb, rsize); if (rs < 0) { archive_set_error(&(a->archive), errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } if (rs == 0) { archive_set_error(&(a->archive), 0, "Truncated 7-Zip archive"); return (ARCHIVE_FATAL); } zip->wbuff_remaining -= rs; length -= rs; if (zip->wbuff_remaining == 0) { r = flush_wbuff(a); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } static int _7z_close(struct archive_write *a) { struct _7zip *zip; unsigned char *wb; uint64_t header_offset, header_size, header_unpacksize; uint64_t length; uint32_t header_crc32; int r; zip = (struct _7zip *)a->format_data; if (zip->total_number_entry > 0) { struct archive_rb_node *n; uint64_t data_offset, data_size, data_unpacksize; unsigned header_compression; r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH); if (r < 0) return (r); data_offset = 0; data_size = zip->stream.total_out; data_unpacksize = zip->stream.total_in; zip->coder.codec = zip->opt_compression; zip->coder.prop_size = zip->stream.prop_size; zip->coder.props = zip->stream.props; zip->stream.prop_size = 0; zip->stream.props = NULL; zip->total_number_nonempty_entry = zip->total_number_entry - zip->total_number_empty_entry; /* Connect an empty file list. */ if (zip->empty_list.first != NULL) { *zip->file_list.last = zip->empty_list.first; zip->file_list.last = zip->empty_list.last; } /* Connect a directory file list. */ ARCHIVE_RB_TREE_FOREACH(n, &(zip->rbtree)) { file_register(zip, (struct file *)n); } /* * NOTE: 7z command supports just LZMA1, LZMA2 and COPY for * the compression type for encoding the header. */ #if HAVE_LZMA_H header_compression = _7Z_LZMA1; /* If the stored file is only one, do not encode the header. * This is the same way 7z command does. */ if (zip->total_number_entry == 1) header_compression = _7Z_COPY; #else header_compression = _7Z_COPY; #endif r = _7z_compression_init_encoder(a, header_compression, 6); if (r < 0) return (r); zip->crc32flg = PRECODE_CRC32; zip->precode_crc32 = 0; r = make_header(a, data_offset, data_size, data_unpacksize, 1, &(zip->coder)); if (r < 0) return (r); r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH); if (r < 0) return (r); header_offset = data_offset + data_size; header_size = zip->stream.total_out; header_crc32 = zip->precode_crc32; header_unpacksize = zip->stream.total_in; if (header_compression != _7Z_COPY) { /* * Encode the header in order to reduce the size * of the archive. */ free(zip->coder.props); zip->coder.codec = header_compression; zip->coder.prop_size = zip->stream.prop_size; zip->coder.props = zip->stream.props; zip->stream.prop_size = 0; zip->stream.props = NULL; r = _7z_compression_init_encoder(a, _7Z_COPY, 0); if (r < 0) return (r); zip->crc32flg = ENCODED_CRC32; zip->encoded_crc32 = 0; /* * Make EncodedHeader. */ r = enc_uint64(a, kEncodedHeader); if (r < 0) return (r); r = make_streamsInfo(a, header_offset, header_size, header_unpacksize, 1, &(zip->coder), 0, header_crc32); if (r < 0) return (r); r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH); if (r < 0) return (r); header_offset = header_offset + header_size; header_size = zip->stream.total_out; header_crc32 = zip->encoded_crc32; } zip->crc32flg = 0; } else { header_offset = header_size = 0; header_crc32 = 0; } length = zip->temp_offset; /* * Make the zip header on wbuff(write buffer). */ wb = zip->wbuff; zip->wbuff_remaining = sizeof(zip->wbuff); memcpy(&wb[0], "7z\xBC\xAF\x27\x1C", 6); wb[6] = 0;/* Major version. */ wb[7] = 3;/* Minor version. */ archive_le64enc(&wb[12], header_offset);/* Next Header Offset */ archive_le64enc(&wb[20], header_size);/* Next Header Size */ archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */ archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */ zip->wbuff_remaining -= 32; /* * Read all file contents and an encoded header from the temporary * file and write out it. */ r = copy_out(a, 0, length); if (r != ARCHIVE_OK) return (r); r = flush_wbuff(a); return (r); } /* * Encode 64 bits value into 7-Zip's encoded UINT64 value. */ static int enc_uint64(struct archive_write *a, uint64_t val) { unsigned mask = 0x80; uint8_t numdata[9]; int i; numdata[0] = 0; for (i = 1; i < (int)sizeof(numdata); i++) { if (val < mask) { numdata[0] |= (uint8_t)val; break; } numdata[i] = (uint8_t)val; val >>= 8; numdata[0] |= mask; mask >>= 1; } return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN)); } static int make_substreamsInfo(struct archive_write *a, struct coder *coders) { struct _7zip *zip = (struct _7zip *)a->format_data; struct file *file; int r; /* * Make SubStreamsInfo. */ r = enc_uint64(a, kSubStreamsInfo); if (r < 0) return (r); if (zip->total_number_nonempty_entry > 1 && coders->codec != _7Z_COPY) { /* * Make NumUnPackStream. */ r = enc_uint64(a, kNumUnPackStream); if (r < 0) return (r); /* Write numUnpackStreams */ r = enc_uint64(a, zip->total_number_nonempty_entry); if (r < 0) return (r); /* * Make kSize. */ r = enc_uint64(a, kSize); if (r < 0) return (r); file = zip->file_list.first; for (;file != NULL; file = file->next) { if (file->next == NULL || file->next->size == 0) break; r = enc_uint64(a, file->size); if (r < 0) return (r); } } /* * Make CRC. */ r = enc_uint64(a, kCRC); if (r < 0) return (r); /* All are defined */ r = enc_uint64(a, 1); if (r < 0) return (r); file = zip->file_list.first; for (;file != NULL; file = file->next) { uint8_t crc[4]; if (file->size == 0) break; archive_le32enc(crc, file->crc32); r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN); if (r < 0) return (r); } /* Write End. */ r = enc_uint64(a, kEnd); if (r < 0) return (r); return (ARCHIVE_OK); } static int make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, uint64_t unpack_size, int num_coder, struct coder *coders, int substrm, uint32_t header_crc) { struct _7zip *zip = (struct _7zip *)a->format_data; uint8_t codec_buff[8]; int numFolders, fi; int codec_size; int i, r; if (coders->codec == _7Z_COPY) numFolders = (int)zip->total_number_nonempty_entry; else numFolders = 1; /* * Make PackInfo. */ r = enc_uint64(a, kPackInfo); if (r < 0) return (r); /* Write PackPos. */ r = enc_uint64(a, offset); if (r < 0) return (r); /* Write NumPackStreams. */ r = enc_uint64(a, numFolders); if (r < 0) return (r); /* Make Size. */ r = enc_uint64(a, kSize); if (r < 0) return (r); if (numFolders > 1) { struct file *file = zip->file_list.first; for (;file != NULL; file = file->next) { if (file->size == 0) break; r = enc_uint64(a, file->size); if (r < 0) return (r); } } else { /* Write size. */ r = enc_uint64(a, pack_size); if (r < 0) return (r); } r = enc_uint64(a, kEnd); if (r < 0) return (r); /* * Make UnPackInfo. */ r = enc_uint64(a, kUnPackInfo); if (r < 0) return (r); /* * Make Folder. */ r = enc_uint64(a, kFolder); if (r < 0) return (r); /* Write NumFolders. */ r = enc_uint64(a, numFolders); if (r < 0) return (r); /* Write External. */ r = enc_uint64(a, 0); if (r < 0) return (r); for (fi = 0; fi < numFolders; fi++) { /* Write NumCoders. */ r = enc_uint64(a, num_coder); if (r < 0) return (r); for (i = 0; i < num_coder; i++) { unsigned codec_id = coders[i].codec; /* Write Codec flag. */ archive_be64enc(codec_buff, codec_id); for (codec_size = 8; codec_size > 0; codec_size--) { if (codec_buff[8 - codec_size]) break; } if (codec_size == 0) codec_size = 1; if (coders[i].prop_size) r = enc_uint64(a, codec_size | 0x20); else r = enc_uint64(a, codec_size); if (r < 0) return (r); /* Write Codec ID. */ codec_size &= 0x0f; r = (int)compress_out(a, &codec_buff[8-codec_size], codec_size, ARCHIVE_Z_RUN); if (r < 0) return (r); if (coders[i].prop_size) { /* Write Codec property size. */ r = enc_uint64(a, coders[i].prop_size); if (r < 0) return (r); /* Write Codec properties. */ r = (int)compress_out(a, coders[i].props, coders[i].prop_size, ARCHIVE_Z_RUN); if (r < 0) return (r); } } } /* * Make CodersUnPackSize. */ r = enc_uint64(a, kCodersUnPackSize); if (r < 0) return (r); if (numFolders > 1) { struct file *file = zip->file_list.first; for (;file != NULL; file = file->next) { if (file->size == 0) break; r = enc_uint64(a, file->size); if (r < 0) return (r); } } else { /* Write UnPackSize. */ r = enc_uint64(a, unpack_size); if (r < 0) return (r); } if (!substrm) { uint8_t crc[4]; /* * Make CRC. */ r = enc_uint64(a, kCRC); if (r < 0) return (r); /* All are defined */ r = enc_uint64(a, 1); if (r < 0) return (r); archive_le32enc(crc, header_crc); r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN); if (r < 0) return (r); } /* Write End. */ r = enc_uint64(a, kEnd); if (r < 0) return (r); if (substrm) { /* * Make SubStreamsInfo. */ r = make_substreamsInfo(a, coders); if (r < 0) return (r); } /* Write End. */ r = enc_uint64(a, kEnd); if (r < 0) return (r); return (ARCHIVE_OK); } #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) static uint64_t utcToFiletime(time_t t, long ns) { uint64_t fileTime; fileTime = t; fileTime *= 10000000; fileTime += ns / 100; fileTime += EPOC_TIME; return (fileTime); } static int make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti) { uint8_t filetime[8]; struct _7zip *zip = (struct _7zip *)a->format_data; struct file *file; int r; uint8_t b, mask; /* * Make Time Bools. */ if (zip->total_number_time_defined[ti] == zip->total_number_entry) { /* Write Time Type. */ r = enc_uint64(a, type); if (r < 0) return (r); /* Write EmptyStream Size. */ r = enc_uint64(a, 2 + zip->total_number_entry * 8); if (r < 0) return (r); /* All are defined. */ r = enc_uint64(a, 1); if (r < 0) return (r); } else { if (zip->total_number_time_defined[ti] == 0) return (ARCHIVE_OK); /* Write Time Type. */ r = enc_uint64(a, type); if (r < 0) return (r); /* Write EmptyStream Size. */ r = enc_uint64(a, 2 + ((zip->total_number_entry + 7) >> 3) + zip->total_number_time_defined[ti] * 8); if (r < 0) return (r); /* All are not defined. */ r = enc_uint64(a, 0); if (r < 0) return (r); b = 0; mask = 0x80; file = zip->file_list.first; for (;file != NULL; file = file->next) { if (file->flg & flg) b |= mask; mask >>= 1; if (mask == 0) { r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); mask = 0x80; b = 0; } } if (mask != 0x80) { r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); } } /* External. */ r = enc_uint64(a, 0); if (r < 0) return (r); /* * Make Times. */ file = zip->file_list.first; for (;file != NULL; file = file->next) { if ((file->flg & flg) == 0) continue; archive_le64enc(filetime, utcToFiletime(file->times[ti].time, file->times[ti].time_ns)); r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN); if (r < 0) return (r); } return (ARCHIVE_OK); } static int make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, uint64_t unpack_size, int codernum, struct coder *coders) { struct _7zip *zip = (struct _7zip *)a->format_data; struct file *file; int r; uint8_t b, mask; /* * Make FilesInfo. */ r = enc_uint64(a, kHeader); if (r < 0) return (r); /* * If there are empty files only, do not write MainStreamInfo. */ if (zip->total_number_nonempty_entry) { /* * Make MainStreamInfo. */ r = enc_uint64(a, kMainStreamsInfo); if (r < 0) return (r); r = make_streamsInfo(a, offset, pack_size, unpack_size, codernum, coders, 1, 0); if (r < 0) return (r); } /* * Make FilesInfo. */ r = enc_uint64(a, kFilesInfo); if (r < 0) return (r); /* Write numFiles. */ r = enc_uint64(a, zip->total_number_entry); if (r < 0) return (r); if (zip->total_number_empty_entry > 0) { /* Make EmptyStream. */ r = enc_uint64(a, kEmptyStream); if (r < 0) return (r); /* Write EmptyStream Size. */ r = enc_uint64(a, (zip->total_number_entry+7)>>3); if (r < 0) return (r); b = 0; mask = 0x80; file = zip->file_list.first; for (;file != NULL; file = file->next) { if (file->size == 0) b |= mask; mask >>= 1; if (mask == 0) { r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); mask = 0x80; b = 0; } } if (mask != 0x80) { r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); } } if (zip->total_number_empty_entry > zip->total_number_dir_entry) { /* Make EmptyFile. */ r = enc_uint64(a, kEmptyFile); if (r < 0) return (r); /* Write EmptyFile Size. */ r = enc_uint64(a, (zip->total_number_empty_entry + 7) >> 3); if (r < 0) return (r); b = 0; mask = 0x80; file = zip->file_list.first; for (;file != NULL; file = file->next) { if (file->size) continue; if (!file->dir) b |= mask; mask >>= 1; if (mask == 0) { r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); mask = 0x80; b = 0; } } if (mask != 0x80) { r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); } } /* Make Name. */ r = enc_uint64(a, kName); if (r < 0) return (r); /* Write Name size. */ r = enc_uint64(a, zip->total_bytes_entry_name+1); if (r < 0) return (r); /* Write dmy byte. */ r = enc_uint64(a, 0); if (r < 0) return (r); file = zip->file_list.first; for (;file != NULL; file = file->next) { r = (int)compress_out(a, file->utf16name, file->name_len+2, ARCHIVE_Z_RUN); if (r < 0) return (r); } /* Make MTime. */ r = make_time(a, kMTime, MTIME_IS_SET, MTIME); if (r < 0) return (r); /* Make CTime. */ r = make_time(a, kCTime, CTIME_IS_SET, CTIME); if (r < 0) return (r); /* Make ATime. */ r = make_time(a, kATime, ATIME_IS_SET, ATIME); if (r < 0) return (r); /* Make Attributes. */ r = enc_uint64(a, kAttributes); if (r < 0) return (r); /* Write Attributes size. */ r = enc_uint64(a, 2 + zip->total_number_entry * 4); if (r < 0) return (r); /* Write "All Are Defined". */ r = enc_uint64(a, 1); if (r < 0) return (r); /* Write dmy byte. */ r = enc_uint64(a, 0); if (r < 0) return (r); file = zip->file_list.first; for (;file != NULL; file = file->next) { /* * High 16bits is unix mode. * Low 16bits is Windows attributes. */ uint32_t encattr, attr; if (file->dir) attr = 0x8010; else attr = 0x8020; if ((file->mode & 0222) == 0) attr |= 1;/* Read Only. */ attr |= ((uint32_t)file->mode) << 16; archive_le32enc(&encattr, attr); r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN); if (r < 0) return (r); } /* Write End. */ r = enc_uint64(a, kEnd); if (r < 0) return (r); /* Write End. */ r = enc_uint64(a, kEnd); if (r < 0) return (r); return (ARCHIVE_OK); } static int _7z_free(struct archive_write *a) { struct _7zip *zip = (struct _7zip *)a->format_data; /* Close the temporary file. */ if (zip->temp_fd >= 0) close(zip->temp_fd); file_free_register(zip); compression_end(&(a->archive), &(zip->stream)); free(zip->coder.props); free(zip); return (ARCHIVE_OK); } static int file_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct file *f1 = (const struct file *)n1; const struct file *f2 = (const struct file *)n2; if (f1->name_len == f2->name_len) return (memcmp(f1->utf16name, f2->utf16name, f1->name_len)); return (f1->name_len > f2->name_len)?1:-1; } static int file_cmp_key(const struct archive_rb_node *n, const void *key) { const struct file *f = (const struct file *)n; return (f->name_len - *(const char *)key); } static int file_new(struct archive_write *a, struct archive_entry *entry, struct file **newfile) { struct _7zip *zip; struct file *file; const char *u16; size_t u16len; int ret = ARCHIVE_OK; zip = (struct _7zip *)a->format_data; *newfile = NULL; file = calloc(1, sizeof(*file)); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } if (0 > archive_entry_pathname_l(entry, &u16, &u16len, zip->sconv)) { if (errno == ENOMEM) { free(file); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16LE"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A filename cannot be converted to UTF-16LE;" "You should disable making Joliet extension"); ret = ARCHIVE_WARN; } file->utf16name = malloc(u16len + 2); if (file->utf16name == NULL) { free(file); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Name"); return (ARCHIVE_FATAL); } memcpy(file->utf16name, u16, u16len); file->utf16name[u16len+0] = 0; file->utf16name[u16len+1] = 0; file->name_len = (unsigned)u16len; file->mode = archive_entry_mode(entry); if (archive_entry_filetype(entry) == AE_IFREG) file->size = archive_entry_size(entry); else archive_entry_set_size(entry, 0); if (archive_entry_filetype(entry) == AE_IFDIR) file->dir = 1; else if (archive_entry_filetype(entry) == AE_IFLNK) file->size = strlen(archive_entry_symlink(entry)); if (archive_entry_mtime_is_set(entry)) { file->flg |= MTIME_IS_SET; file->times[MTIME].time = archive_entry_mtime(entry); file->times[MTIME].time_ns = archive_entry_mtime_nsec(entry); } if (archive_entry_atime_is_set(entry)) { file->flg |= ATIME_IS_SET; file->times[ATIME].time = archive_entry_atime(entry); file->times[ATIME].time_ns = archive_entry_atime_nsec(entry); } if (archive_entry_ctime_is_set(entry)) { file->flg |= CTIME_IS_SET; file->times[CTIME].time = archive_entry_ctime(entry); file->times[CTIME].time_ns = archive_entry_ctime_nsec(entry); } *newfile = file; return (ret); } static void file_free(struct file *file) { free(file->utf16name); free(file); } static void file_register(struct _7zip *zip, struct file *file) { file->next = NULL; *zip->file_list.last = file; zip->file_list.last = &(file->next); } static void file_init_register(struct _7zip *zip) { zip->file_list.first = NULL; zip->file_list.last = &(zip->file_list.first); } static void file_free_register(struct _7zip *zip) { struct file *file, *file_next; file = zip->file_list.first; while (file != NULL) { file_next = file->next; file_free(file); file = file_next; } } static void file_register_empty(struct _7zip *zip, struct file *file) { file->next = NULL; *zip->empty_list.last = file; zip->empty_list.last = &(file->next); } static void file_init_register_empty(struct _7zip *zip) { zip->empty_list.first = NULL; zip->empty_list.last = &(zip->empty_list.first); } #if !defined(HAVE_ZLIB_H) || !defined(HAVE_BZLIB_H) ||\ !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H) static int compression_unsupported_encoder(struct archive *a, struct la_zstream *lastrm, const char *name) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "%s compression not supported on this platform", name); lastrm->valid = 0; lastrm->real_stream = NULL; return (ARCHIVE_FAILED); } #endif /* * _7_COPY compressor. */ static int compression_init_encoder_copy(struct archive *a, struct la_zstream *lastrm) { if (lastrm->valid) compression_end(a, lastrm); lastrm->valid = 1; lastrm->code = compression_code_copy; lastrm->end = compression_end_copy; return (ARCHIVE_OK); } static int compression_code_copy(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { size_t bytes; (void)a; /* UNUSED */ if (lastrm->avail_out > lastrm->avail_in) bytes = lastrm->avail_in; else bytes = lastrm->avail_out; if (bytes) { memcpy(lastrm->next_out, lastrm->next_in, bytes); lastrm->next_in += bytes; lastrm->avail_in -= bytes; lastrm->total_in += bytes; lastrm->next_out += bytes; lastrm->avail_out -= bytes; lastrm->total_out += bytes; } if (action == ARCHIVE_Z_FINISH && lastrm->avail_in == 0) return (ARCHIVE_EOF); return (ARCHIVE_OK); } static int compression_end_copy(struct archive *a, struct la_zstream *lastrm) { (void)a; /* UNUSED */ lastrm->valid = 0; return (ARCHIVE_OK); } /* * _7_DEFLATE compressor. */ #ifdef HAVE_ZLIB_H static int compression_init_encoder_deflate(struct archive *a, struct la_zstream *lastrm, int level, int withheader) { z_stream *strm; if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm)); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for gzip stream"); return (ARCHIVE_FATAL); } /* zlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = (uInt)lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; strm->avail_out = (uInt)lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; if (deflateInit2(strm, level, Z_DEFLATED, (withheader)?15:-15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_deflate; lastrm->end = compression_end_deflate; return (ARCHIVE_OK); } static int compression_code_deflate(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { z_stream *strm; int r; strm = (z_stream *)lastrm->real_stream; /* zlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = (uInt)lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; strm->avail_out = (uInt)lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; r = deflate(strm, (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH); lastrm->next_in = strm->next_in; lastrm->avail_in = strm->avail_in; lastrm->total_in = strm->total_in; lastrm->next_out = strm->next_out; lastrm->avail_out = strm->avail_out; lastrm->total_out = strm->total_out; switch (r) { case Z_OK: return (ARCHIVE_OK); case Z_STREAM_END: return (ARCHIVE_EOF); default: archive_set_error(a, ARCHIVE_ERRNO_MISC, "GZip compression failed:" " deflate() call returned status %d", r); return (ARCHIVE_FATAL); } } static int compression_end_deflate(struct archive *a, struct la_zstream *lastrm) { z_stream *strm; int r; strm = (z_stream *)lastrm->real_stream; r = deflateEnd(strm); free(strm); lastrm->real_stream = NULL; lastrm->valid = 0; if (r != Z_OK) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #else static int compression_init_encoder_deflate(struct archive *a, struct la_zstream *lastrm, int level, int withheader) { (void) level; /* UNUSED */ (void) withheader; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "deflate")); } #endif /* * _7_BZIP2 compressor. */ #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) static int compression_init_encoder_bzip2(struct archive *a, struct la_zstream *lastrm, int level) { bz_stream *strm; if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm)); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for bzip2 stream"); return (ARCHIVE_FATAL); } /* bzlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_bzip2; lastrm->end = compression_end_bzip2; return (ARCHIVE_OK); } static int compression_code_bzip2(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { bz_stream *strm; int r; strm = (bz_stream *)lastrm->real_stream; /* bzlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); r = BZ2_bzCompress(strm, (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN); lastrm->next_in = (const unsigned char *)strm->next_in; lastrm->avail_in = strm->avail_in; lastrm->total_in = (((uint64_t)(uint32_t)strm->total_in_hi32) << 32) + (uint64_t)(uint32_t)strm->total_in_lo32; lastrm->next_out = (unsigned char *)strm->next_out; lastrm->avail_out = strm->avail_out; lastrm->total_out = (((uint64_t)(uint32_t)strm->total_out_hi32) << 32) + (uint64_t)(uint32_t)strm->total_out_lo32; switch (r) { case BZ_RUN_OK: /* Non-finishing */ case BZ_FINISH_OK: /* Finishing: There's more work to do */ return (ARCHIVE_OK); case BZ_STREAM_END: /* Finishing: all done */ /* Only occurs in finishing case */ return (ARCHIVE_EOF); default: /* Any other return value indicates an error */ archive_set_error(a, ARCHIVE_ERRNO_MISC, "Bzip2 compression failed:" " BZ2_bzCompress() call returned status %d", r); return (ARCHIVE_FATAL); } } static int compression_end_bzip2(struct archive *a, struct la_zstream *lastrm) { bz_stream *strm; int r; strm = (bz_stream *)lastrm->real_stream; r = BZ2_bzCompressEnd(strm); free(strm); lastrm->real_stream = NULL; lastrm->valid = 0; if (r != BZ_OK) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #else static int compression_init_encoder_bzip2(struct archive *a, struct la_zstream *lastrm, int level) { (void) level; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "bzip2")); } #endif /* * _7_LZMA1, _7_LZMA2 compressor. */ #if defined(HAVE_LZMA_H) static int compression_init_encoder_lzma(struct archive *a, struct la_zstream *lastrm, int level, uint64_t filter_id) { static const lzma_stream lzma_init_data = LZMA_STREAM_INIT; lzma_stream *strm; lzma_filter *lzmafilters; lzma_options_lzma lzma_opt; int r; if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for lzma stream"); return (ARCHIVE_FATAL); } lzmafilters = (lzma_filter *)(strm+1); - if (level > 6) - level = 6; + if (level > 9) + level = 9; if (lzma_lzma_preset(&lzma_opt, level)) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } lzmafilters[0].id = filter_id; lzmafilters[0].options = &lzma_opt; lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ r = lzma_properties_size(&(lastrm->prop_size), lzmafilters); if (r != LZMA_OK) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzma_properties_size failed"); return (ARCHIVE_FATAL); } if (lastrm->prop_size) { lastrm->props = malloc(lastrm->prop_size); if (lastrm->props == NULL) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Cannot allocate memory"); return (ARCHIVE_FATAL); } r = lzma_properties_encode(lzmafilters, lastrm->props); if (r != LZMA_OK) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzma_properties_encode failed"); return (ARCHIVE_FATAL); } } *strm = lzma_init_data; r = lzma_raw_encoder(strm, lzmafilters); switch (r) { case LZMA_OK: lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_lzma; lastrm->end = compression_end_lzma; r = ARCHIVE_OK; break; case LZMA_MEM_ERROR: free(strm); lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Internal error initializing compression library: " "Cannot allocate memory"); r = ARCHIVE_FATAL; break; default: free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "It's a bug in liblzma"); r = ARCHIVE_FATAL; break; } return (r); } static int compression_init_encoder_lzma1(struct archive *a, struct la_zstream *lastrm, int level) { return compression_init_encoder_lzma(a, lastrm, level, LZMA_FILTER_LZMA1); } static int compression_init_encoder_lzma2(struct archive *a, struct la_zstream *lastrm, int level) { return compression_init_encoder_lzma(a, lastrm, level, LZMA_FILTER_LZMA2); } static int compression_code_lzma(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { lzma_stream *strm; int r; strm = (lzma_stream *)lastrm->real_stream; strm->next_in = lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in = lastrm->total_in; strm->next_out = lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out = lastrm->total_out; r = lzma_code(strm, (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN); lastrm->next_in = strm->next_in; lastrm->avail_in = strm->avail_in; lastrm->total_in = strm->total_in; lastrm->next_out = strm->next_out; lastrm->avail_out = strm->avail_out; lastrm->total_out = strm->total_out; switch (r) { case LZMA_OK: /* Non-finishing case */ return (ARCHIVE_OK); case LZMA_STREAM_END: /* This return can only occur in finishing case. */ return (ARCHIVE_EOF); case LZMA_MEMLIMIT_ERROR: archive_set_error(a, ENOMEM, "lzma compression error:" " %ju MiB would have been needed", (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1) / (1024 * 1024))); return (ARCHIVE_FATAL); default: /* Any other return value indicates an error */ archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzma compression failed:" " lzma_code() call returned status %d", r); return (ARCHIVE_FATAL); } } static int compression_end_lzma(struct archive *a, struct la_zstream *lastrm) { lzma_stream *strm; (void)a; /* UNUSED */ strm = (lzma_stream *)lastrm->real_stream; lzma_end(strm); free(strm); lastrm->valid = 0; lastrm->real_stream = NULL; return (ARCHIVE_OK); } #else static int compression_init_encoder_lzma1(struct archive *a, struct la_zstream *lastrm, int level) { (void) level; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "lzma")); } static int compression_init_encoder_lzma2(struct archive *a, struct la_zstream *lastrm, int level) { (void) level; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "lzma")); } #endif /* * _7_PPMD compressor. */ static void ppmd_write(void *p, Byte b) { struct archive_write *a = ((IByteOut *)p)->a; struct _7zip *zip = (struct _7zip *)(a->format_data); struct la_zstream *lastrm = &(zip->stream); struct ppmd_stream *strm; if (lastrm->avail_out) { *lastrm->next_out++ = b; lastrm->avail_out--; lastrm->total_out++; return; } strm = (struct ppmd_stream *)lastrm->real_stream; if (strm->buff_ptr < strm->buff_end) { *strm->buff_ptr++ = b; strm->buff_bytes++; } } static int compression_init_encoder_ppmd(struct archive *a, struct la_zstream *lastrm, unsigned maxOrder, uint32_t msize) { struct ppmd_stream *strm; uint8_t *props; int r; if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm)); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for PPMd"); return (ARCHIVE_FATAL); } strm->buff = malloc(32); if (strm->buff == NULL) { free(strm); archive_set_error(a, ENOMEM, "Can't allocate memory for PPMd"); return (ARCHIVE_FATAL); } strm->buff_ptr = strm->buff; strm->buff_end = strm->buff + 32; props = malloc(1+4); if (props == NULL) { free(strm->buff); free(strm); archive_set_error(a, ENOMEM, "Coludn't allocate memory for PPMd"); return (ARCHIVE_FATAL); } props[0] = maxOrder; archive_le32enc(props+1, msize); __archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context); r = __archive_ppmd7_functions.Ppmd7_Alloc( &strm->ppmd7_context, msize); if (r == 0) { free(strm->buff); free(strm); free(props); archive_set_error(a, ENOMEM, "Coludn't allocate memory for PPMd"); return (ARCHIVE_FATAL); } __archive_ppmd7_functions.Ppmd7_Init(&(strm->ppmd7_context), maxOrder); strm->byteout.a = (struct archive_write *)a; strm->byteout.Write = ppmd_write; strm->range_enc.Stream = &(strm->byteout); __archive_ppmd7_functions.Ppmd7z_RangeEnc_Init(&(strm->range_enc)); strm->stat = 0; lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_ppmd; lastrm->end = compression_end_ppmd; lastrm->prop_size = 5; lastrm->props = props; return (ARCHIVE_OK); } static int compression_code_ppmd(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { struct ppmd_stream *strm; (void)a; /* UNUSED */ strm = (struct ppmd_stream *)lastrm->real_stream; /* Copy encoded data if there are remaining bytes from previous call. */ if (strm->buff_bytes) { uint8_t *p = strm->buff_ptr - strm->buff_bytes; while (lastrm->avail_out && strm->buff_bytes) { *lastrm->next_out++ = *p++; lastrm->avail_out--; lastrm->total_out++; strm->buff_bytes--; } if (strm->buff_bytes) return (ARCHIVE_OK); if (strm->stat == 1) return (ARCHIVE_EOF); strm->buff_ptr = strm->buff; } while (lastrm->avail_in && lastrm->avail_out) { __archive_ppmd7_functions.Ppmd7_EncodeSymbol( &(strm->ppmd7_context), &(strm->range_enc), *lastrm->next_in++); lastrm->avail_in--; lastrm->total_in++; } if (lastrm->avail_in == 0 && action == ARCHIVE_Z_FINISH) { __archive_ppmd7_functions.Ppmd7z_RangeEnc_FlushData( &(strm->range_enc)); strm->stat = 1; /* Return EOF if there are no remaining bytes. */ if (strm->buff_bytes == 0) return (ARCHIVE_EOF); } return (ARCHIVE_OK); } static int compression_end_ppmd(struct archive *a, struct la_zstream *lastrm) { struct ppmd_stream *strm; (void)a; /* UNUSED */ strm = (struct ppmd_stream *)lastrm->real_stream; __archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context); free(strm->buff); free(strm); lastrm->real_stream = NULL; lastrm->valid = 0; return (ARCHIVE_OK); } /* * Universal compressor initializer. */ static int _7z_compression_init_encoder(struct archive_write *a, unsigned compression, int compression_level) { struct _7zip *zip; int r; zip = (struct _7zip *)a->format_data; switch (compression) { case _7Z_DEFLATE: r = compression_init_encoder_deflate( &(a->archive), &(zip->stream), compression_level, 0); break; case _7Z_BZIP2: r = compression_init_encoder_bzip2( &(a->archive), &(zip->stream), compression_level); break; case _7Z_LZMA1: r = compression_init_encoder_lzma1( &(a->archive), &(zip->stream), compression_level); break; case _7Z_LZMA2: r = compression_init_encoder_lzma2( &(a->archive), &(zip->stream), compression_level); break; case _7Z_PPMD: r = compression_init_encoder_ppmd( &(a->archive), &(zip->stream), PPMD7_DEFAULT_ORDER, PPMD7_DEFAULT_MEM_SIZE); break; case _7Z_COPY: default: r = compression_init_encoder_copy( &(a->archive), &(zip->stream)); break; } if (r == ARCHIVE_OK) { zip->stream.total_in = 0; zip->stream.next_out = zip->wbuff; zip->stream.avail_out = sizeof(zip->wbuff); zip->stream.total_out = 0; } return (r); } static int compression_code(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { if (lastrm->valid) return (lastrm->code(a, lastrm, action)); return (ARCHIVE_OK); } static int compression_end(struct archive *a, struct la_zstream *lastrm) { if (lastrm->valid) { lastrm->prop_size = 0; free(lastrm->props); lastrm->props = NULL; return (lastrm->end(a, lastrm)); } return (ARCHIVE_OK); } Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_cpio.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_cpio.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_cpio.c (revision 368707) @@ -1,500 +1,500 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" static ssize_t archive_write_cpio_data(struct archive_write *, const void *buff, size_t s); static int archive_write_cpio_close(struct archive_write *); static int archive_write_cpio_free(struct archive_write *); static int archive_write_cpio_finish_entry(struct archive_write *); static int archive_write_cpio_header(struct archive_write *, struct archive_entry *); static int archive_write_cpio_options(struct archive_write *, const char *, const char *); static int format_octal(int64_t, void *, int); static int64_t format_octal_recursive(int64_t, char *, int); static int write_header(struct archive_write *, struct archive_entry *); struct cpio { uint64_t entry_bytes_remaining; int64_t ino_next; struct { int64_t old; int new;} *ino_list; size_t ino_list_size; size_t ino_list_next; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; #define c_magic_offset 0 #define c_magic_size 6 #define c_dev_offset 6 #define c_dev_size 6 #define c_ino_offset 12 #define c_ino_size 6 #define c_mode_offset 18 #define c_mode_size 6 #define c_uid_offset 24 #define c_uid_size 6 #define c_gid_offset 30 #define c_gid_size 6 #define c_nlink_offset 36 #define c_nlink_size 6 #define c_rdev_offset 42 #define c_rdev_size 6 #define c_mtime_offset 48 #define c_mtime_size 11 #define c_namesize_offset 59 #define c_namesize_size 6 #define c_filesize_offset 65 #define c_filesize_size 11 /* * Set output format to 'cpio' format. */ int archive_write_set_format_cpio(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct cpio *cpio; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_cpio"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } a->format_data = cpio; a->format_name = "cpio"; a->format_options = archive_write_cpio_options; a->format_write_header = archive_write_cpio_header; a->format_write_data = archive_write_cpio_data; a->format_finish_entry = archive_write_cpio_finish_entry; a->format_close = archive_write_cpio_close; a->format_free = archive_write_cpio_free; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX cpio"; return (ARCHIVE_OK); } static int archive_write_cpio_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = (struct cpio *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { cpio->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } /* * Ino values are as long as 64 bits on some systems; cpio format * only allows 18 bits and relies on the ino values to identify hardlinked * files. So, we can't merely "hash" the ino numbers since collisions * would corrupt the archive. Instead, we generate synthetic ino values * to store in the archive and maintain a map of original ino values to * synthetic ones so we can preserve hardlink information. * * TODO: Make this more efficient. It's not as bad as it looks (most * files don't have any hardlinks and we don't do any work here for those), * but it wouldn't be hard to do better. * * TODO: Work with dev/ino pairs here instead of just ino values. */ static int synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry) { int64_t ino = archive_entry_ino64(entry); int ino_new; size_t i; /* * If no index number was given, don't assign one. In * particular, this handles the end-of-archive marker * correctly by giving it a zero index value. (This is also * why we start our synthetic index numbers with one below.) */ if (ino == 0) return (0); /* Don't store a mapping if we don't need to. */ if (archive_entry_nlink(entry) < 2) { return (int)(++cpio->ino_next); } /* Look up old ino; if we have it, this is a hardlink * and we reuse the same value. */ for (i = 0; i < cpio->ino_list_next; ++i) { if (cpio->ino_list[i].old == ino) return (cpio->ino_list[i].new); } /* Assign a new index number. */ ino_new = (int)(++cpio->ino_next); /* Ensure space for the new mapping. */ if (cpio->ino_list_size <= cpio->ino_list_next) { size_t newsize = cpio->ino_list_size < 512 ? 512 : cpio->ino_list_size * 2; void *newlist = realloc(cpio->ino_list, sizeof(cpio->ino_list[0]) * newsize); if (newlist == NULL) return (-1); cpio->ino_list_size = newsize; cpio->ino_list = newlist; } /* Record and return the new value. */ cpio->ino_list[cpio->ino_list_next].old = ino; cpio->ino_list[cpio->ino_list_next].new = ino_new; ++cpio->ino_list_next; return (ino_new); } static struct archive_string_conv * get_sconv(struct archive_write *a) { struct cpio *cpio; struct archive_string_conv *sconv; cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } return (sconv); } static int archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry) { const char *path; size_t len; - if (archive_entry_filetype(entry) == 0) { + if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) { archive_set_error(&a->archive, -1, "Filetype required"); return (ARCHIVE_FAILED); } if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } if (len == 0 || path == NULL || path[0] == '\0') { archive_set_error(&a->archive, -1, "Pathname required"); return (ARCHIVE_FAILED); } if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) { archive_set_error(&a->archive, -1, "Size required"); return (ARCHIVE_FAILED); } return write_header(a, entry); } static int write_header(struct archive_write *a, struct archive_entry *entry) { struct cpio *cpio; const char *p, *path; int pathlength, ret, ret_final; int64_t ino; char h[76]; struct archive_string_conv *sconv; struct archive_entry *entry_main; size_t len; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = archive_entry_pathname_l(entry, &path, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } /* Include trailing null. */ pathlength = (int)len + 1; memset(h, 0, sizeof(h)); format_octal(070707, h + c_magic_offset, c_magic_size); format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size); ino = synthesize_ino_value(cpio, entry); if (ino < 0) { archive_set_error(&a->archive, ENOMEM, "No memory for ino translation table"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } else if (ino > 0777777) { archive_set_error(&a->archive, ERANGE, "Too many files for this cpio format"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } format_octal(ino & 0777777, h + c_ino_offset, c_ino_size); /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) - format_octal(archive_entry_dev(entry), h + c_rdev_offset, c_rdev_size); + format_octal(archive_entry_rdev(entry), h + c_rdev_offset, c_rdev_size); else format_octal(0, h + c_rdev_offset, c_rdev_size); format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); format_octal(pathlength, h + c_namesize_offset, c_namesize_size); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); /* Symlinks get the link written as the body of the entry. */ ret = archive_entry_symlink_l(entry, &p, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_symlink(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } if (len > 0 && p != NULL && *p != '\0') ret = format_octal(strlen(p), h + c_filesize_offset, c_filesize_size); else ret = format_octal(archive_entry_size(entry), h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, "File is too large for cpio format."); ret_final = ARCHIVE_FAILED; goto exit_write_header; } ret = __archive_write_output(a, h, sizeof(h)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } cpio->entry_bytes_remaining = archive_entry_size(entry); /* Write the symlink now. */ if (p != NULL && *p != '\0') { ret = __archive_write_output(a, p, strlen(p)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } exit_write_header: archive_entry_free(entry_main); return (ret_final); } static ssize_t archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s) { struct cpio *cpio; int ret; cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); cpio->entry_bytes_remaining -= s; if (ret >= 0) return (s); else return (ret); } /* * Format a number into the specified field. */ static int format_octal(int64_t v, void *p, int digits) { int64_t max; int ret; max = (((int64_t)1) << (digits * 3)) - 1; if (v >= 0 && v <= max) { format_octal_recursive(v, (char *)p, digits); ret = 0; } else { format_octal_recursive(max, (char *)p, digits); ret = -1; } return (ret); } static int64_t format_octal_recursive(int64_t v, char *p, int s) { if (s == 0) return (v); v = format_octal_recursive(v, p+1, s-1); *p = '0' + ((char)v & 7); return (v >> 3); } static int archive_write_cpio_close(struct archive_write *a) { int er; struct archive_entry *trailer; trailer = archive_entry_new2(NULL); /* nlink = 1 here for GNU cpio compat. */ archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); archive_entry_set_pathname(trailer, "TRAILER!!!"); er = write_header(a, trailer); archive_entry_free(trailer); return (er); } static int archive_write_cpio_free(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; free(cpio->ino_list); free(cpio); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_cpio_finish_entry(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; return (__archive_write_nulls(a, (size_t)cpio->entry_bytes_remaining)); } Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c (revision 368707) @@ -1,457 +1,457 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o. * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" static ssize_t archive_write_newc_data(struct archive_write *, const void *buff, size_t s); static int archive_write_newc_close(struct archive_write *); static int archive_write_newc_free(struct archive_write *); static int archive_write_newc_finish_entry(struct archive_write *); static int archive_write_newc_header(struct archive_write *, struct archive_entry *); static int archive_write_newc_options(struct archive_write *, const char *, const char *); static int format_hex(int64_t, void *, int); static int64_t format_hex_recursive(int64_t, char *, int); static int write_header(struct archive_write *, struct archive_entry *); struct cpio { uint64_t entry_bytes_remaining; int padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; #define c_magic_offset 0 #define c_magic_size 6 #define c_ino_offset 6 #define c_ino_size 8 #define c_mode_offset 14 #define c_mode_size 8 #define c_uid_offset 22 #define c_uid_size 8 #define c_gid_offset 30 #define c_gid_size 8 #define c_nlink_offset 38 #define c_nlink_size 8 #define c_mtime_offset 46 #define c_mtime_size 8 #define c_filesize_offset 54 #define c_filesize_size 8 #define c_devmajor_offset 62 #define c_devmajor_size 8 #define c_devminor_offset 70 #define c_devminor_size 8 #define c_rdevmajor_offset 78 #define c_rdevmajor_size 8 #define c_rdevminor_offset 86 #define c_rdevminor_size 8 #define c_namesize_offset 94 #define c_namesize_size 8 #define c_checksum_offset 102 #define c_checksum_size 8 #define c_header_size 110 /* Logic trick: difference between 'n' and next multiple of 4 */ #define PAD4(n) (3 & (1 + ~(n))) /* * Set output format to 'cpio' format. */ int archive_write_set_format_cpio_newc(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct cpio *cpio; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } a->format_data = cpio; a->format_name = "cpio"; a->format_options = archive_write_newc_options; a->format_write_header = archive_write_newc_header; a->format_write_data = archive_write_newc_data; a->format_finish_entry = archive_write_newc_finish_entry; a->format_close = archive_write_newc_close; a->format_free = archive_write_newc_free; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "SVR4 cpio nocrc"; return (ARCHIVE_OK); } static int archive_write_newc_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = (struct cpio *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { cpio->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static struct archive_string_conv * get_sconv(struct archive_write *a) { struct cpio *cpio; struct archive_string_conv *sconv; cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } return (sconv); } static int archive_write_newc_header(struct archive_write *a, struct archive_entry *entry) { const char *path; size_t len; - if (archive_entry_filetype(entry) == 0) { + if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) { archive_set_error(&a->archive, -1, "Filetype required"); return (ARCHIVE_FAILED); } if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } if (len == 0 || path == NULL || path[0] == '\0') { archive_set_error(&a->archive, -1, "Pathname required"); return (ARCHIVE_FAILED); } if (archive_entry_hardlink(entry) == NULL && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) { archive_set_error(&a->archive, -1, "Size required"); return (ARCHIVE_FAILED); } return write_header(a, entry); } static int write_header(struct archive_write *a, struct archive_entry *entry) { int64_t ino; struct cpio *cpio; const char *p, *path; int pathlength, ret, ret_final; char h[c_header_size]; struct archive_string_conv *sconv; struct archive_entry *entry_main; size_t len; int pad; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = archive_entry_pathname_l(entry, &path, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } pathlength = (int)len + 1; /* Include trailing null. */ memset(h, 0, c_header_size); format_hex(0x070701, h + c_magic_offset, c_magic_size); format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset, c_devmajor_size); format_hex(archive_entry_devminor(entry), h + c_devminor_offset, c_devminor_size); ino = archive_entry_ino64(entry); if (ino > 0xffffffff) { archive_set_error(&a->archive, ERANGE, "large inode number truncated"); ret_final = ARCHIVE_WARN; } /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size); format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size); format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size); } else { format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size); format_hex(0, h + c_rdevminor_offset, c_rdevminor_size); } format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); format_hex(pathlength, h + c_namesize_offset, c_namesize_size); format_hex(0, h + c_checksum_offset, c_checksum_size); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); /* Symlinks get the link written as the body of the entry. */ ret = archive_entry_symlink_l(entry, &p, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Likname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_symlink(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } if (len > 0 && p != NULL && *p != '\0') ret = format_hex(strlen(p), h + c_filesize_offset, c_filesize_size); else ret = format_hex(archive_entry_size(entry), h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, "File is too large for this format."); ret_final = ARCHIVE_FAILED; goto exit_write_header; } ret = __archive_write_output(a, h, c_header_size); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } /* Pad pathname to even length. */ ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } pad = PAD4(pathlength + c_header_size); if (pad) { ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } cpio->entry_bytes_remaining = archive_entry_size(entry); cpio->padding = (int)PAD4(cpio->entry_bytes_remaining); /* Write the symlink now. */ if (p != NULL && *p != '\0') { ret = __archive_write_output(a, p, strlen(p)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } pad = PAD4(strlen(p)); ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } exit_write_header: archive_entry_free(entry_main); return (ret_final); } static ssize_t archive_write_newc_data(struct archive_write *a, const void *buff, size_t s) { struct cpio *cpio; int ret; cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); cpio->entry_bytes_remaining -= s; if (ret >= 0) return (s); else return (ret); } /* * Format a number into the specified field. */ static int format_hex(int64_t v, void *p, int digits) { int64_t max; int ret; max = (((int64_t)1) << (digits * 4)) - 1; if (v >= 0 && v <= max) { format_hex_recursive(v, (char *)p, digits); ret = 0; } else { format_hex_recursive(max, (char *)p, digits); ret = -1; } return (ret); } static int64_t format_hex_recursive(int64_t v, char *p, int s) { if (s == 0) return (v); v = format_hex_recursive(v, p+1, s-1); *p = "0123456789abcdef"[v & 0xf]; return (v >> 4); } static int archive_write_newc_close(struct archive_write *a) { int er; struct archive_entry *trailer; trailer = archive_entry_new(); archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); archive_entry_set_pathname(trailer, "TRAILER!!!"); /* Bypass the required data checks. */ er = write_header(a, trailer); archive_entry_free(trailer); return (er); } static int archive_write_newc_free(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; free(cpio); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_newc_finish_entry(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; return (__archive_write_nulls(a, (size_t)cpio->entry_bytes_remaining + cpio->padding)); } Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c (revision 368707) @@ -1,8162 +1,8163 @@ /*- * Copyright (c) 2009-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_write_private.h" #if defined(_WIN32) && !defined(__CYGWIN__) #define getuid() 0 #define getgid() 0 #endif /*#define DEBUG 1*/ #ifdef DEBUG /* To compare to the ISO image file made by mkisofs. */ #define COMPAT_MKISOFS 1 #endif #define LOGICAL_BLOCK_BITS 11 #define LOGICAL_BLOCK_SIZE 2048 #define PATH_TABLE_BLOCK_SIZE 4096 #define SYSTEM_AREA_BLOCK 16 #define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 1 #define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 1 #define BOOT_RECORD_DESCRIPTOR_BLOCK 1 #define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK 1 #define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK 1 #define RRIP_ER_BLOCK 1 #define PADDING_BLOCK 150 #define FD_1_2M_SIZE (1024 * 1200) #define FD_1_44M_SIZE (1024 * 1440) #define FD_2_88M_SIZE (1024 * 2880) #define MULTI_EXTENT_SIZE (ARCHIVE_LITERAL_LL(1) << 32) /* 4Gi bytes. */ #define MAX_DEPTH 8 #define RR_CE_SIZE 28 /* SUSP "CE" extension size */ #define FILE_FLAG_EXISTENCE 0x01 #define FILE_FLAG_DIRECTORY 0x02 #define FILE_FLAG_ASSOCIATED 0x04 #define FILE_FLAG_RECORD 0x08 #define FILE_FLAG_PROTECTION 0x10 #define FILE_FLAG_MULTI_EXTENT 0x80 static const char rrip_identifier[] = "RRIP_1991A"; static const char rrip_descriptor[] = "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR " "POSIX FILE SYSTEM SEMANTICS"; static const char rrip_source[] = "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. " "SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR " "CONTACT INFORMATION."; #define RRIP_ER_ID_SIZE (sizeof(rrip_identifier)-1) #define RRIP_ER_DSC_SIZE (sizeof(rrip_descriptor)-1) #define RRIP_ER_SRC_SIZE (sizeof(rrip_source)-1) #define RRIP_ER_SIZE (8 + RRIP_ER_ID_SIZE + \ RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE) static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; #define ZF_HEADER_SIZE 16 /* zisofs header size. */ #define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */ #define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS) /* * Manage extra records. */ struct extr_rec { int location; int offset; unsigned char buf[LOGICAL_BLOCK_SIZE]; struct extr_rec *next; }; struct ctl_extr_rec { int use_extr; unsigned char *bp; struct isoent *isoent; unsigned char *ce_ptr; int cur_len; int dr_len; int limit; int extr_off; int extr_loc; }; #define DR_SAFETY RR_CE_SIZE #define DR_LIMIT (254 - DR_SAFETY) /* * The relation of struct isofile and isoent and archive_entry. * * Primary volume tree --> struct isoent * | * v * struct isofile --> archive_entry * ^ * | * Joliet volume tree --> struct isoent * * struct isoent has specific information for volume. */ struct isofile { /* Used for managing struct isofile list. */ struct isofile *allnext; struct isofile *datanext; /* Used for managing a hardlinked struct isofile list. */ struct isofile *hlnext; struct isofile *hardlink_target; struct archive_entry *entry; /* * Used for making a directory tree. */ struct archive_string parentdir; struct archive_string basename; struct archive_string basename_utf16; struct archive_string symlink; int dircnt; /* The number of elements of * its parent directory */ /* * Used for a Directory Record. */ struct content { int64_t offset_of_temp; int64_t size; int blocks; uint32_t location; /* * One extent equals one content. * If this entry has multi extent, `next' variable points * next content data. */ struct content *next; /* next content */ } content, *cur_content; int write_content; enum { NO = 0, BOOT_CATALOG, BOOT_IMAGE } boot; /* * Used for a zisofs. */ struct { unsigned char header_size; unsigned char log2_bs; uint32_t uncompressed_size; } zisofs; }; struct isoent { /* Keep `rbnode' at the first member of struct isoent. */ struct archive_rb_node rbnode; struct isofile *file; struct isoent *parent; /* A list of children.(use chnext) */ struct { struct isoent *first; struct isoent **last; int cnt; } children; struct archive_rb_tree rbtree; /* A list of sub directories.(use drnext) */ struct { struct isoent *first; struct isoent **last; int cnt; } subdirs; /* A sorted list of sub directories. */ struct isoent **children_sorted; /* Used for managing struct isoent list. */ struct isoent *chnext; struct isoent *drnext; struct isoent *ptnext; /* * Used for making a Directory Record. */ int dir_number; struct { int vd; int self; int parent; int normal; } dr_len; uint32_t dir_location; int dir_block; /* * Identifier: * on primary, ISO9660 file/directory name. * on joliet, UCS2 file/directory name. * ext_off : offset of identifier extension. * ext_len : length of identifier extension. * id_len : byte size of identifier. * on primary, this is ext_off + ext_len + version length. * on joliet, this is ext_off + ext_len. * mb_len : length of multibyte-character of identifier. * on primary, mb_len and id_len are always the same. * on joliet, mb_len and id_len are different. */ char *identifier; int ext_off; int ext_len; int id_len; int mb_len; /* * Used for making a Rockridge extension. * This is a part of Directory Records. */ struct isoent *rr_parent; struct isoent *rr_child; /* Extra Record.(which we call in this source file) * A maximum size of the Directory Record is 254. * so, if generated RRIP data of a file cannot into a Directory * Record because of its size, that surplus data relocate this * Extra Record. */ struct { struct extr_rec *first; struct extr_rec **last; struct extr_rec *current; } extr_rec_list; signed int virtual:1; /* If set to one, this file type is a directory. * A convenience flag to be used as * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". */ signed int dir:1; }; struct hardlink { struct archive_rb_node rbnode; int nlink; struct { struct isofile *first; struct isofile **last; } file_list; }; /* * ISO writer options */ struct iso_option { /* * Usage : abstract-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -abstract * * Specifies Abstract Filename. * This file shall be described in the Root Directory * and containing a abstract statement. */ unsigned int abstract_file:1; #define OPT_ABSTRACT_FILE_DEFAULT 0 /* Not specified */ #define ABSTRACT_FILE_SIZE 37 /* * Usage : application-id= * Type : string, max 128 bytes * Default: Not specified * COMPAT : mkisofs -A/-appid . * * Specifies Application Identifier. * If the first byte is set to '_'(5F), the remaining * bytes of this option shall specify an identifier * for a file containing the identification of the * application. * This file shall be described in the Root Directory. */ unsigned int application_id:1; #define OPT_APPLICATION_ID_DEFAULT 0 /* Use default identifier */ #define APPLICATION_IDENTIFIER_SIZE 128 /* * Usage : !allow-vernum * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT: mkisofs -N * * Allow filenames to use version numbers. */ unsigned int allow_vernum:1; #define OPT_ALLOW_VERNUM_DEFAULT 1 /* Enabled */ /* * Usage : biblio-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -biblio * * Specifies Bibliographic Filename. * This file shall be described in the Root Directory * and containing bibliographic records. */ unsigned int biblio_file:1; #define OPT_BIBLIO_FILE_DEFAULT 0 /* Not specified */ #define BIBLIO_FILE_SIZE 37 /* * Usage : boot= * Type : string * Default: Not specified * COMPAT : mkisofs -b/-eltorito-boot * * Specifies "El Torito" boot image file to make * a bootable CD. */ unsigned int boot:1; #define OPT_BOOT_DEFAULT 0 /* Not specified */ /* * Usage : boot-catalog= * Type : string * Default: "boot.catalog" * COMPAT : mkisofs -c/-eltorito-catalog * * Specifies a fullpath of El Torito boot catalog. */ unsigned int boot_catalog:1; #define OPT_BOOT_CATALOG_DEFAULT 0 /* Not specified */ /* * Usage : boot-info-table * Type : boolean * Default: Disabled * COMPAT : mkisofs -boot-info-table * * Modify the boot image file specified by `boot' * option; ISO writer stores boot file information * into the boot file in ISO image at offset 8 * through offset 64. */ unsigned int boot_info_table:1; #define OPT_BOOT_INFO_TABLE_DEFAULT 0 /* Disabled */ /* * Usage : boot-load-seg= * Type : hexadecimal * Default: Not specified * COMPAT : mkisofs -boot-load-seg * * Specifies a load segment for boot image. * This is used with no-emulation mode. */ unsigned int boot_load_seg:1; #define OPT_BOOT_LOAD_SEG_DEFAULT 0 /* Not specified */ /* * Usage : boot-load-size= * Type : decimal * Default: Not specified * COMPAT : mkisofs -boot-load-size * * Specifies a sector count for boot image. * This is used with no-emulation mode. */ unsigned int boot_load_size:1; #define OPT_BOOT_LOAD_SIZE_DEFAULT 0 /* Not specified */ /* * Usage : boot-type= * : 'no-emulation' : 'no emulation' image * : 'fd' : floppy disk image * : 'hard-disk' : hard disk image * Type : string * Default: Auto detect * : We check a size of boot image; * : If the size is just 1.22M/1.44M/2.88M, * : we assume boot_type is 'fd'; * : otherwise boot_type is 'no-emulation'. * COMPAT : * boot=no-emulation * mkisofs -no-emul-boot * boot=fd * This is a default on the mkisofs. * boot=hard-disk * mkisofs -hard-disk-boot * * Specifies a type of "El Torito" boot image. */ unsigned int boot_type:2; #define OPT_BOOT_TYPE_AUTO 0 /* auto detect */ #define OPT_BOOT_TYPE_NO_EMU 1 /* ``no emulation'' image */ #define OPT_BOOT_TYPE_FD 2 /* floppy disk image */ #define OPT_BOOT_TYPE_HARD_DISK 3 /* hard disk image */ #define OPT_BOOT_TYPE_DEFAULT OPT_BOOT_TYPE_AUTO /* * Usage : compression-level= * Type : decimal * Default: Not specified * COMPAT : NONE * * Specifies compression level for option zisofs=direct. */ unsigned int compression_level:1; #define OPT_COMPRESSION_LEVEL_DEFAULT 0 /* Not specified */ /* * Usage : copyright-file= * Type : string, max 37 bytes * Default: Not specified * COMPAT : mkisofs -copyright * * Specifies Copyright Filename. * This file shall be described in the Root Directory * and containing a copyright statement. */ unsigned int copyright_file:1; #define OPT_COPYRIGHT_FILE_DEFAULT 0 /* Not specified */ #define COPYRIGHT_FILE_SIZE 37 /* * Usage : gid= * Type : decimal * Default: Not specified * COMPAT : mkisofs -gid * * Specifies a group id to rewrite the group id of all files. */ unsigned int gid:1; #define OPT_GID_DEFAULT 0 /* Not specified */ /* * Usage : iso-level=[1234] * Type : decimal * Default: 1 * COMPAT : mkisofs -iso-level * * Specifies ISO9600 Level. * Level 1: [DEFAULT] * - limits each file size less than 4Gi bytes; * - a File Name shall not contain more than eight * d-characters or eight d1-characters; * - a File Name Extension shall not contain more than * three d-characters or three d1-characters; * - a Directory Identifier shall not contain more * than eight d-characters or eight d1-characters. * Level 2: * - limits each file size less than 4Giga bytes; * - a File Name shall not contain more than thirty * d-characters or thirty d1-characters; * - a File Name Extension shall not contain more than * thirty d-characters or thirty d1-characters; * - a Directory Identifier shall not contain more * than thirty-one d-characters or thirty-one * d1-characters. * Level 3: * - no limit of file size; use multi extent. * Level 4: * - this level 4 simulates mkisofs option * '-iso-level 4'; * - crate a enhanced volume as mkisofs doing; * - allow a File Name to have leading dot; * - allow a File Name to have all ASCII letters; * - allow a File Name to have multiple dots; * - allow more then 8 depths of directory trees; * - disable a version number to a File Name; * - disable a forced period to the tail of a File Name; * - the maximum length of files and directories is raised to 193. * if rockridge option is disabled, raised to 207. */ unsigned int iso_level:3; #define OPT_ISO_LEVEL_DEFAULT 1 /* ISO Level 1 */ /* * Usage : joliet[=long] * : !joliet * : Do not generate Joliet Volume and Records. * : joliet [DEFAULT] * : Generates Joliet Volume and Directory Records. * : [COMPAT: mkisofs -J/-joliet] * : joliet=long * : The joliet filenames are up to 103 Unicode * : characters. * : This option breaks the Joliet specification. * : [COMPAT: mkisofs -J -joliet-long] * Type : boolean/string * Default: Enabled * COMPAT : mkisofs -J / -joliet-long * * Generates Joliet Volume and Directory Records. */ unsigned int joliet:2; #define OPT_JOLIET_DISABLE 0 /* Not generate Joliet Records. */ #define OPT_JOLIET_ENABLE 1 /* Generate Joliet Records. */ #define OPT_JOLIET_LONGNAME 2 /* Use long joliet filenames.*/ #define OPT_JOLIET_DEFAULT OPT_JOLIET_ENABLE /* * Usage : !limit-depth * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT : mkisofs -D/-disable-deep-relocation * * The number of levels in hierarchy cannot exceed eight. */ unsigned int limit_depth:1; #define OPT_LIMIT_DEPTH_DEFAULT 1 /* Enabled */ /* * Usage : !limit-dirs * Type : boolean * Default: Enabled * : Violates the ISO9660 standard if disable. * COMPAT : mkisofs -no-limit-pathtables * * Limits the number of directories less than 65536 due * to the size of the Parent Directory Number of Path * Table. */ unsigned int limit_dirs:1; #define OPT_LIMIT_DIRS_DEFAULT 1 /* Enabled */ /* * Usage : !pad * Type : boolean * Default: Enabled * COMPAT : -pad/-no-pad * * Pads the end of the ISO image by null of 300Ki bytes. */ unsigned int pad:1; #define OPT_PAD_DEFAULT 1 /* Enabled */ /* * Usage : publisher= * Type : string, max 128 bytes * Default: Not specified * COMPAT : mkisofs -publisher * * Specifies Publisher Identifier. * If the first byte is set to '_'(5F), the remaining * bytes of this option shall specify an identifier * for a file containing the identification of the user. * This file shall be described in the Root Directory. */ unsigned int publisher:1; #define OPT_PUBLISHER_DEFAULT 0 /* Not specified */ #define PUBLISHER_IDENTIFIER_SIZE 128 /* * Usage : rockridge * : !rockridge * : disable to generate SUSP and RR records. * : rockridge * : the same as 'rockridge=useful'. * : rockridge=strict * : generate SUSP and RR records. * : [COMPAT: mkisofs -R] * : rockridge=useful [DEFAULT] * : generate SUSP and RR records. * : [COMPAT: mkisofs -r] * : NOTE Our rockridge=useful option does not set a zero * : to uid and gid, you should use application * : option such as --gid,--gname,--uid and --uname * : bsdtar options instead. * Type : boolean/string * Default: Enabled as rockridge=useful * COMPAT : mkisofs -r / -R * * Generates SUSP and RR records. */ unsigned int rr:2; #define OPT_RR_DISABLED 0 #define OPT_RR_STRICT 1 #define OPT_RR_USEFUL 2 #define OPT_RR_DEFAULT OPT_RR_USEFUL /* * Usage : volume-id= * Type : string, max 32 bytes * Default: Not specified * COMPAT : mkisofs -V * * Specifies Volume Identifier. */ unsigned int volume_id:1; #define OPT_VOLUME_ID_DEFAULT 0 /* Use default identifier */ #define VOLUME_IDENTIFIER_SIZE 32 /* * Usage : !zisofs [DEFAULT] * : Disable to generate RRIP 'ZF' extension. * : zisofs * : Make files zisofs file and generate RRIP 'ZF' * : extension. So you do not need mkzftree utility * : for making zisofs. * : When the file size is less than one Logical Block * : size, that file will not zisofs'ed since it does * : reduce an ISO-image size. * : * : When you specify option 'boot=', that * : 'boot-image' file won't be converted to zisofs file. * Type : boolean * Default: Disabled * * Generates RRIP 'ZF' System Use Entry. */ unsigned int zisofs:1; #define OPT_ZISOFS_DISABLED 0 #define OPT_ZISOFS_DIRECT 1 #define OPT_ZISOFS_DEFAULT OPT_ZISOFS_DISABLED }; struct iso9660 { /* The creation time of ISO image. */ time_t birth_time; /* A file stream of a temporary file, which file contents * save to until ISO image can be created. */ int temp_fd; struct isofile *cur_file; struct isoent *cur_dirent; struct archive_string cur_dirstr; uint64_t bytes_remaining; int need_multi_extent; /* Temporary string buffer for Joliet extension. */ struct archive_string utf16be; struct archive_string mbs; struct archive_string_conv *sconv_to_utf16be; struct archive_string_conv *sconv_from_utf16be; /* A list of all of struct isofile entries. */ struct { struct isofile *first; struct isofile **last; } all_file_list; /* A list of struct isofile entries which have its * contents and are not a directory, a hardlinked file * and a symlink file. */ struct { struct isofile *first; struct isofile **last; } data_file_list; /* Used for managing to find hardlinking files. */ struct archive_rb_tree hardlink_rbtree; /* Used for making the Path Table Record. */ struct vdd { /* the root of entry tree. */ struct isoent *rootent; enum vdd_type { VDD_PRIMARY, VDD_JOLIET, VDD_ENHANCED } vdd_type; struct path_table { struct isoent *first; struct isoent **last; struct isoent **sorted; int cnt; } *pathtbl; int max_depth; int path_table_block; int path_table_size; int location_type_L_path_table; int location_type_M_path_table; int total_dir_block; } primary, joliet; /* Used for making a Volume Descriptor. */ int volume_space_size; int volume_sequence_number; int total_file_block; struct archive_string volume_identifier; struct archive_string publisher_identifier; struct archive_string data_preparer_identifier; struct archive_string application_identifier; struct archive_string copyright_file_identifier; struct archive_string abstract_file_identifier; struct archive_string bibliographic_file_identifier; /* Used for making rockridge extensions. */ int location_rrip_er; /* Used for making zisofs. */ struct { signed int detect_magic:1; signed int making:1; signed int allzero:1; unsigned char magic_buffer[64]; int magic_cnt; #ifdef HAVE_ZLIB_H /* * Copy a compressed file to iso9660.zisofs.temp_fd * and also copy a uncompressed file(original file) to * iso9660.temp_fd . If the number of logical block * of the compressed file is less than the number of * logical block of the uncompressed file, use it and * remove the copy of the uncompressed file. * but if not, we use uncompressed file and remove * the copy of the compressed file. */ uint32_t *block_pointers; size_t block_pointers_allocated; int block_pointers_cnt; int block_pointers_idx; int64_t total_size; int64_t block_offset; z_stream stream; int stream_valid; int64_t remaining; int compression_level; #endif } zisofs; struct isoent *directories_too_deep; int dircnt_max; /* Write buffer. */ #define wb_buffmax() (LOGICAL_BLOCK_SIZE * 32) #define wb_remaining(a) (((struct iso9660 *)(a)->format_data)->wbuff_remaining) #define wb_offset(a) (((struct iso9660 *)(a)->format_data)->wbuff_offset \ + wb_buffmax() - wb_remaining(a)) unsigned char wbuff[LOGICAL_BLOCK_SIZE * 32]; size_t wbuff_remaining; enum { WB_TO_STREAM, WB_TO_TEMP } wbuff_type; int64_t wbuff_offset; int64_t wbuff_written; int64_t wbuff_tail; /* 'El Torito' boot data. */ struct { /* boot catalog file */ struct archive_string catalog_filename; struct isoent *catalog; /* boot image file */ struct archive_string boot_filename; struct isoent *boot; unsigned char platform_id; #define BOOT_PLATFORM_X86 0 #define BOOT_PLATFORM_PPC 1 #define BOOT_PLATFORM_MAC 2 struct archive_string id; unsigned char media_type; #define BOOT_MEDIA_NO_EMULATION 0 #define BOOT_MEDIA_1_2M_DISKETTE 1 #define BOOT_MEDIA_1_44M_DISKETTE 2 #define BOOT_MEDIA_2_88M_DISKETTE 3 #define BOOT_MEDIA_HARD_DISK 4 unsigned char system_type; uint16_t boot_load_seg; uint16_t boot_load_size; #define BOOT_LOAD_SIZE 4 } el_torito; struct iso_option opt; }; /* * Types of Volume Descriptor */ enum VD_type { VDT_BOOT_RECORD=0, /* Boot Record Volume Descriptor */ VDT_PRIMARY=1, /* Primary Volume Descriptor */ VDT_SUPPLEMENTARY=2, /* Supplementary Volume Descriptor */ VDT_TERMINATOR=255 /* Volume Descriptor Set Terminator */ }; /* * Types of Directory Record */ enum dir_rec_type { DIR_REC_VD, /* Stored in Volume Descriptor. */ DIR_REC_SELF, /* Stored as Current Directory. */ DIR_REC_PARENT, /* Stored as Parent Directory. */ DIR_REC_NORMAL /* Stored as Child. */ }; /* * Kinds of Volume Descriptor Character */ enum vdc { VDC_STD, VDC_LOWERCASE, VDC_UCS2, VDC_UCS2_DIRECT }; /* * IDentifier Resolver. * Used for resolving duplicated filenames. */ struct idr { struct idrent { struct archive_rb_node rbnode; /* Used in wait_list. */ struct idrent *wnext; struct idrent *avail; struct isoent *isoent; int weight; int noff; int rename_num; } *idrent_pool; struct archive_rb_tree rbtree; struct { struct idrent *first; struct idrent **last; } wait_list; int pool_size; int pool_idx; int num_size; int null_size; char char_map[0x80]; }; enum char_type { A_CHAR, D_CHAR }; static int iso9660_options(struct archive_write *, const char *, const char *); static int iso9660_write_header(struct archive_write *, struct archive_entry *); static ssize_t iso9660_write_data(struct archive_write *, const void *, size_t); static int iso9660_finish_entry(struct archive_write *); static int iso9660_close(struct archive_write *); static int iso9660_free(struct archive_write *); static void get_system_identitier(char *, size_t); static void set_str(unsigned char *, const char *, size_t, char, const char *); static inline int joliet_allowed_char(unsigned char, unsigned char); static int set_str_utf16be(struct archive_write *, unsigned char *, const char *, size_t, uint16_t, enum vdc); static int set_str_a_characters_bp(struct archive_write *, unsigned char *, int, int, const char *, enum vdc); static int set_str_d_characters_bp(struct archive_write *, unsigned char *, int, int, const char *, enum vdc); static void set_VD_bp(unsigned char *, enum VD_type, unsigned char); static inline void set_unused_field_bp(unsigned char *, int, int); static unsigned char *extra_open_record(unsigned char *, int, struct isoent *, struct ctl_extr_rec *); static void extra_close_record(struct ctl_extr_rec *, int); static unsigned char * extra_next_record(struct ctl_extr_rec *, int); static unsigned char *extra_get_record(struct isoent *, int *, int *, int *); static void extra_tell_used_size(struct ctl_extr_rec *, int); static int extra_setup_location(struct isoent *, int); static int set_directory_record_rr(unsigned char *, int, struct isoent *, struct iso9660 *, enum dir_rec_type); static int set_directory_record(unsigned char *, size_t, struct isoent *, struct iso9660 *, enum dir_rec_type, enum vdd_type); static inline int get_dir_rec_size(struct iso9660 *, struct isoent *, enum dir_rec_type, enum vdd_type); static inline unsigned char *wb_buffptr(struct archive_write *); static int wb_write_out(struct archive_write *); static int wb_consume(struct archive_write *, size_t); #ifdef HAVE_ZLIB_H static int wb_set_offset(struct archive_write *, int64_t); #endif static int write_null(struct archive_write *, size_t); static int write_VD_terminator(struct archive_write *); static int set_file_identifier(unsigned char *, int, int, enum vdc, struct archive_write *, struct vdd *, struct archive_string *, const char *, int, enum char_type); static int write_VD(struct archive_write *, struct vdd *); static int write_VD_boot_record(struct archive_write *); static int write_information_block(struct archive_write *); static int write_path_table(struct archive_write *, int, struct vdd *); static int write_directory_descriptors(struct archive_write *, struct vdd *); static int write_file_descriptors(struct archive_write *); static int write_rr_ER(struct archive_write *); static void calculate_path_table_size(struct vdd *); static void isofile_init_entry_list(struct iso9660 *); static void isofile_add_entry(struct iso9660 *, struct isofile *); static void isofile_free_all_entries(struct iso9660 *); static void isofile_init_entry_data_file_list(struct iso9660 *); static void isofile_add_data_file(struct iso9660 *, struct isofile *); static struct isofile * isofile_new(struct archive_write *, struct archive_entry *); static void isofile_free(struct isofile *); static int isofile_gen_utility_names(struct archive_write *, struct isofile *); static int isofile_register_hardlink(struct archive_write *, struct isofile *); static void isofile_connect_hardlink_files(struct iso9660 *); static void isofile_init_hardlinks(struct iso9660 *); static void isofile_free_hardlinks(struct iso9660 *); static struct isoent *isoent_new(struct isofile *); static int isoent_clone_tree(struct archive_write *, struct isoent **, struct isoent *); static void _isoent_free(struct isoent *isoent); static void isoent_free_all(struct isoent *); static struct isoent * isoent_create_virtual_dir(struct archive_write *, struct iso9660 *, const char *); static int isoent_cmp_node(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key(const struct archive_rb_node *, const void *); static int isoent_add_child_head(struct isoent *, struct isoent *); static int isoent_add_child_tail(struct isoent *, struct isoent *); static void isoent_remove_child(struct isoent *, struct isoent *); static void isoent_setup_directory_location(struct iso9660 *, int, struct vdd *); static void isoent_setup_file_location(struct iso9660 *, int); static int get_path_component(char *, size_t, const char *); static int isoent_tree(struct archive_write *, struct isoent **); static struct isoent *isoent_find_child(struct isoent *, const char *); static struct isoent *isoent_find_entry(struct isoent *, const char *); static void idr_relaxed_filenames(char *); static void idr_init(struct iso9660 *, struct vdd *, struct idr *); static void idr_cleanup(struct idr *); static int idr_ensure_poolsize(struct archive_write *, struct idr *, int); static int idr_start(struct archive_write *, struct idr *, int, int, int, int, const struct archive_rb_tree_ops *); static void idr_register(struct idr *, struct isoent *, int, int); static void idr_extend_identifier(struct idrent *, int, int); static void idr_resolve(struct idr *, void (*)(unsigned char *, int)); static void idr_set_num(unsigned char *, int); static void idr_set_num_beutf16(unsigned char *, int); static int isoent_gen_iso9660_identifier(struct archive_write *, struct isoent *, struct idr *); static int isoent_gen_joliet_identifier(struct archive_write *, struct isoent *, struct idr *); static int isoent_cmp_iso9660_identifier(const struct isoent *, const struct isoent *); static int isoent_cmp_node_iso9660(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key_iso9660(const struct archive_rb_node *, const void *); static int isoent_cmp_joliet_identifier(const struct isoent *, const struct isoent *); static int isoent_cmp_node_joliet(const struct archive_rb_node *, const struct archive_rb_node *); static int isoent_cmp_key_joliet(const struct archive_rb_node *, const void *); static inline void path_table_add_entry(struct path_table *, struct isoent *); static inline struct isoent * path_table_last_entry(struct path_table *); static int isoent_make_path_table(struct archive_write *); static int isoent_find_out_boot_file(struct archive_write *, struct isoent *); static int isoent_create_boot_catalog(struct archive_write *, struct isoent *); static size_t fd_boot_image_size(int); static int make_boot_catalog(struct archive_write *); static int setup_boot_information(struct archive_write *); static int zisofs_init(struct archive_write *, struct isofile *); static void zisofs_detect_magic(struct archive_write *, const void *, size_t); static int zisofs_write_to_temp(struct archive_write *, const void *, size_t); static int zisofs_finish_entry(struct archive_write *); static int zisofs_rewind_boot_file(struct archive_write *); static int zisofs_free(struct archive_write *); int archive_write_set_format_iso9660(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct iso9660 *iso9660; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); iso9660 = calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } iso9660->birth_time = 0; iso9660->temp_fd = -1; iso9660->cur_file = NULL; iso9660->primary.max_depth = 0; iso9660->primary.vdd_type = VDD_PRIMARY; iso9660->primary.pathtbl = NULL; iso9660->joliet.rootent = NULL; iso9660->joliet.max_depth = 0; iso9660->joliet.vdd_type = VDD_JOLIET; iso9660->joliet.pathtbl = NULL; isofile_init_entry_list(iso9660); isofile_init_entry_data_file_list(iso9660); isofile_init_hardlinks(iso9660); iso9660->directories_too_deep = NULL; iso9660->dircnt_max = 1; iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_TEMP; iso9660->wbuff_offset = 0; iso9660->wbuff_written = 0; iso9660->wbuff_tail = 0; archive_string_init(&(iso9660->utf16be)); archive_string_init(&(iso9660->mbs)); /* * Init Identifiers used for PVD and SVD. */ archive_string_init(&(iso9660->volume_identifier)); archive_strcpy(&(iso9660->volume_identifier), "CDROM"); archive_string_init(&(iso9660->publisher_identifier)); archive_string_init(&(iso9660->data_preparer_identifier)); archive_string_init(&(iso9660->application_identifier)); archive_strcpy(&(iso9660->application_identifier), archive_version_string()); archive_string_init(&(iso9660->copyright_file_identifier)); archive_string_init(&(iso9660->abstract_file_identifier)); archive_string_init(&(iso9660->bibliographic_file_identifier)); /* * Init El Torito bootable CD variables. */ archive_string_init(&(iso9660->el_torito.catalog_filename)); iso9660->el_torito.catalog = NULL; /* Set default file name of boot catalog */ archive_strcpy(&(iso9660->el_torito.catalog_filename), "boot.catalog"); archive_string_init(&(iso9660->el_torito.boot_filename)); iso9660->el_torito.boot = NULL; iso9660->el_torito.platform_id = BOOT_PLATFORM_X86; archive_string_init(&(iso9660->el_torito.id)); iso9660->el_torito.boot_load_seg = 0; iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE; /* * Init zisofs variables. */ #ifdef HAVE_ZLIB_H iso9660->zisofs.block_pointers = NULL; iso9660->zisofs.block_pointers_allocated = 0; iso9660->zisofs.stream_valid = 0; iso9660->zisofs.compression_level = 9; memset(&(iso9660->zisofs.stream), 0, sizeof(iso9660->zisofs.stream)); #endif /* * Set default value of iso9660 options. */ iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT; iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT; iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT; iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT; iso9660->opt.boot = OPT_BOOT_DEFAULT; iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT; iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT; iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT; iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT; iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT; iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT; iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT; iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT; iso9660->opt.joliet = OPT_JOLIET_DEFAULT; iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT; iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT; iso9660->opt.pad = OPT_PAD_DEFAULT; iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT; iso9660->opt.rr = OPT_RR_DEFAULT; iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT; iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT; /* Create the root directory. */ iso9660->primary.rootent = isoent_create_virtual_dir(a, iso9660, ""); if (iso9660->primary.rootent == NULL) { free(iso9660); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } iso9660->primary.rootent->parent = iso9660->primary.rootent; iso9660->cur_dirent = iso9660->primary.rootent; archive_string_init(&(iso9660->cur_dirstr)); archive_string_ensure(&(iso9660->cur_dirstr), 1); iso9660->cur_dirstr.s[0] = 0; iso9660->sconv_to_utf16be = NULL; iso9660->sconv_from_utf16be = NULL; a->format_data = iso9660; a->format_name = "iso9660"; a->format_options = iso9660_options; a->format_write_header = iso9660_write_header; a->format_write_data = iso9660_write_data; a->format_finish_entry = iso9660_finish_entry; a->format_close = iso9660_close; a->format_free = iso9660_free; a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; return (ARCHIVE_OK); } static int get_str_opt(struct archive_write *a, struct archive_string *s, size_t maxsize, const char *key, const char *value) { if (strlen(value) > maxsize) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Value is longer than %zu characters " "for option ``%s''", maxsize, key); return (ARCHIVE_FATAL); } archive_strcpy(s, value); return (ARCHIVE_OK); } static int get_num_opt(struct archive_write *a, int *num, int high, int low, const char *key, const char *value) { const char *p = value; int data = 0; int neg = 0; if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(empty) for option ``%s''", key); return (ARCHIVE_FATAL); } if (*p == '-') { neg = 1; p++; } while (*p) { if (*p >= '0' && *p <= '9') data = data * 10 + *p - '0'; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value for option ``%s''", key); return (ARCHIVE_FATAL); } if (data > high) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(over %d) for " "option ``%s''", high, key); return (ARCHIVE_FATAL); } if (data < low) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(under %d) for " "option ``%s''", low, key); return (ARCHIVE_FATAL); } p++; } if (neg) data *= -1; *num = data; return (ARCHIVE_OK); } static int iso9660_options(struct archive_write *a, const char *key, const char *value) { struct iso9660 *iso9660 = a->format_data; const char *p; int r; switch (key[0]) { case 'a': if (strcmp(key, "abstract-file") == 0) { r = get_str_opt(a, &(iso9660->abstract_file_identifier), ABSTRACT_FILE_SIZE, key, value); iso9660->opt.abstract_file = r == ARCHIVE_OK; return (r); } if (strcmp(key, "application-id") == 0) { r = get_str_opt(a, &(iso9660->application_identifier), APPLICATION_IDENTIFIER_SIZE, key, value); iso9660->opt.application_id = r == ARCHIVE_OK; return (r); } if (strcmp(key, "allow-vernum") == 0) { iso9660->opt.allow_vernum = value != NULL; return (ARCHIVE_OK); } break; case 'b': if (strcmp(key, "biblio-file") == 0) { r = get_str_opt(a, &(iso9660->bibliographic_file_identifier), BIBLIO_FILE_SIZE, key, value); iso9660->opt.biblio_file = r == ARCHIVE_OK; return (r); } if (strcmp(key, "boot") == 0) { if (value == NULL) iso9660->opt.boot = 0; else { iso9660->opt.boot = 1; archive_strcpy( &(iso9660->el_torito.boot_filename), value); } return (ARCHIVE_OK); } if (strcmp(key, "boot-catalog") == 0) { r = get_str_opt(a, &(iso9660->el_torito.catalog_filename), 1024, key, value); iso9660->opt.boot_catalog = r == ARCHIVE_OK; return (r); } if (strcmp(key, "boot-info-table") == 0) { iso9660->opt.boot_info_table = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "boot-load-seg") == 0) { uint32_t seg; iso9660->opt.boot_load_seg = 0; if (value == NULL) goto invalid_value; seg = 0; p = value; if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) p += 2; while (*p) { if (seg) seg <<= 4; if (*p >= 'A' && *p <= 'F') seg += *p - 'A' + 0x0a; else if (*p >= 'a' && *p <= 'f') seg += *p - 'a' + 0x0a; else if (*p >= '0' && *p <= '9') seg += *p - '0'; else goto invalid_value; if (seg > 0xffff) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value(over 0xffff) for " "option ``%s''", key); return (ARCHIVE_FATAL); } p++; } iso9660->el_torito.boot_load_seg = (uint16_t)seg; iso9660->opt.boot_load_seg = 1; return (ARCHIVE_OK); } if (strcmp(key, "boot-load-size") == 0) { int num = 0; r = get_num_opt(a, &num, 0xffff, 1, key, value); iso9660->opt.boot_load_size = r == ARCHIVE_OK; if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->el_torito.boot_load_size = (uint16_t)num; return (ARCHIVE_OK); } if (strcmp(key, "boot-type") == 0) { if (value == NULL) goto invalid_value; if (strcmp(value, "no-emulation") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU; else if (strcmp(value, "fd") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_FD; else if (strcmp(value, "hard-disk") == 0) iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK; else goto invalid_value; return (ARCHIVE_OK); } break; case 'c': if (strcmp(key, "compression-level") == 0) { #ifdef HAVE_ZLIB_H if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || value[1] != '\0') goto invalid_value; iso9660->zisofs.compression_level = value[0] - '0'; iso9660->opt.compression_level = 1; return (ARCHIVE_OK); #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Option ``%s'' " "is not supported on this platform.", key); return (ARCHIVE_FATAL); #endif } if (strcmp(key, "copyright-file") == 0) { r = get_str_opt(a, &(iso9660->copyright_file_identifier), COPYRIGHT_FILE_SIZE, key, value); iso9660->opt.copyright_file = r == ARCHIVE_OK; return (r); } #ifdef DEBUG /* Specifies Volume creation date and time; * year(4),month(2),day(2),hour(2),minute(2),second(2). * e.g. "20090929033757" */ if (strcmp(key, "creation") == 0) { struct tm tm; char buf[5]; p = value; if (p == NULL || strlen(p) < 14) goto invalid_value; memset(&tm, 0, sizeof(tm)); memcpy(buf, p, 4); buf[4] = '\0'; p += 4; tm.tm_year = strtol(buf, NULL, 10) - 1900; memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_mon = strtol(buf, NULL, 10) - 1; memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_mday = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_hour = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; p += 2; tm.tm_min = strtol(buf, NULL, 10); memcpy(buf, p, 2); buf[2] = '\0'; tm.tm_sec = strtol(buf, NULL, 10); iso9660->birth_time = mktime(&tm); return (ARCHIVE_OK); } #endif break; case 'i': if (strcmp(key, "iso-level") == 0) { if (value != NULL && value[1] == '\0' && (value[0] >= '1' && value[0] <= '4')) { iso9660->opt.iso_level = value[0]-'0'; return (ARCHIVE_OK); } goto invalid_value; } break; case 'j': if (strcmp(key, "joliet") == 0) { if (value == NULL) iso9660->opt.joliet = OPT_JOLIET_DISABLE; else if (strcmp(value, "1") == 0) iso9660->opt.joliet = OPT_JOLIET_ENABLE; else if (strcmp(value, "long") == 0) iso9660->opt.joliet = OPT_JOLIET_LONGNAME; else goto invalid_value; return (ARCHIVE_OK); } break; case 'l': if (strcmp(key, "limit-depth") == 0) { iso9660->opt.limit_depth = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "limit-dirs") == 0) { iso9660->opt.limit_dirs = value != NULL; return (ARCHIVE_OK); } break; case 'p': if (strcmp(key, "pad") == 0) { iso9660->opt.pad = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "publisher") == 0) { r = get_str_opt(a, &(iso9660->publisher_identifier), PUBLISHER_IDENTIFIER_SIZE, key, value); iso9660->opt.publisher = r == ARCHIVE_OK; return (r); } break; case 'r': if (strcmp(key, "rockridge") == 0 || strcmp(key, "Rockridge") == 0) { if (value == NULL) iso9660->opt.rr = OPT_RR_DISABLED; else if (strcmp(value, "1") == 0) iso9660->opt.rr = OPT_RR_USEFUL; else if (strcmp(value, "strict") == 0) iso9660->opt.rr = OPT_RR_STRICT; else if (strcmp(value, "useful") == 0) iso9660->opt.rr = OPT_RR_USEFUL; else goto invalid_value; return (ARCHIVE_OK); } break; case 'v': if (strcmp(key, "volume-id") == 0) { r = get_str_opt(a, &(iso9660->volume_identifier), VOLUME_IDENTIFIER_SIZE, key, value); iso9660->opt.volume_id = r == ARCHIVE_OK; return (r); } break; case 'z': if (strcmp(key, "zisofs") == 0) { if (value == NULL) iso9660->opt.zisofs = OPT_ZISOFS_DISABLED; else { #ifdef HAVE_ZLIB_H iso9660->opt.zisofs = OPT_ZISOFS_DIRECT; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "``zisofs'' " "is not supported on this platform."); return (ARCHIVE_FATAL); #endif } return (ARCHIVE_OK); } break; } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); invalid_value: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid value for option ``%s''", key); return (ARCHIVE_FAILED); } static int iso9660_write_header(struct archive_write *a, struct archive_entry *entry) { struct iso9660 *iso9660; struct isofile *file; struct isoent *isoent; int r, ret = ARCHIVE_OK; iso9660 = a->format_data; iso9660->cur_file = NULL; iso9660->bytes_remaining = 0; iso9660->need_multi_extent = 0; if (archive_entry_filetype(entry) == AE_IFLNK && iso9660->opt.rr == OPT_RR_DISABLED) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore symlink file."); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) >= MULTI_EXTENT_SIZE) { if (iso9660->opt.iso_level < 3) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore over %lld bytes file. " "This file too large.", MULTI_EXTENT_SIZE); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } iso9660->need_multi_extent = 1; } file = isofile_new(a, entry); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } r = isofile_gen_utility_names(a, file); if (r < ARCHIVE_WARN) { isofile_free(file); return (r); } else if (r < ret) ret = r; /* * Ignore a path which looks like the top of directory name * since we have already made the root directory of an ISO image. */ if (archive_strlen(&(file->parentdir)) == 0 && archive_strlen(&(file->basename)) == 0) { isofile_free(file); return (r); } isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } if (isoent->file->dircnt > iso9660->dircnt_max) iso9660->dircnt_max = isoent->file->dircnt; /* Add the current file into tree */ r = isoent_tree(a, &isoent); if (r != ARCHIVE_OK) return (r); /* If there is the same file in tree and * the current file is older than the file in tree. * So we don't need the current file data anymore. */ if (isoent->file != file) return (ARCHIVE_OK); /* Non regular files contents are unneeded to be saved to * temporary files. */ if (archive_entry_filetype(file->entry) != AE_IFREG) return (ret); /* * Set the current file to cur_file to read its contents. */ iso9660->cur_file = file; if (archive_entry_nlink(file->entry) > 1) { r = isofile_register_hardlink(a, file); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* * Prepare to save the contents of the file. */ if (iso9660->temp_fd < 0) { iso9660->temp_fd = __archive_mktemp(NULL); if (iso9660->temp_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } } /* Save an offset of current file in temporary file. */ file->content.offset_of_temp = wb_offset(a); file->cur_content = &(file->content); r = zisofs_init(a, file); if (r < ret) ret = r; iso9660->bytes_remaining = archive_entry_size(file->entry); return (ret); } static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; ssize_t written; const unsigned char *b; b = (const unsigned char *)buff; while (s) { written = write(iso9660->temp_fd, b, s); if (written < 0) { archive_set_error(&a->archive, errno, "Can't write to temporary file"); return (ARCHIVE_FATAL); } s -= written; b += written; } return (ARCHIVE_OK); } static int wb_write_to_temp(struct archive_write *a, const void *buff, size_t s) { const char *xp = buff; size_t xs = s; /* * If a written data size is big enough to use system-call * and there is no waiting data, this calls write_to_temp() in * order to reduce a extra memory copy. */ if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; xs = s % LOGICAL_BLOCK_SIZE; iso9660->wbuff_offset += s - xs; if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (xs == 0) return (ARCHIVE_OK); xp += s - xs; } while (xs) { size_t size = xs; if (size > wb_remaining(a)) size = wb_remaining(a); memcpy(wb_buffptr(a), xp, size); if (wb_consume(a, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); xs -= size; xp += size; } return (ARCHIVE_OK); } static int wb_write_padding_to_temp(struct archive_write *a, int64_t csize) { size_t ns; int ret; ns = (size_t)(csize % LOGICAL_BLOCK_SIZE); if (ns != 0) ret = write_null(a, LOGICAL_BLOCK_SIZE - ns); else ret = ARCHIVE_OK; return (ret); } static ssize_t write_iso9660_data(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; size_t ws; if (iso9660->temp_fd < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } ws = s; if (iso9660->need_multi_extent && (iso9660->cur_file->cur_content->size + ws) >= (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) { struct content *con; size_t ts; ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE - iso9660->cur_file->cur_content->size); if (iso9660->zisofs.detect_magic) zisofs_detect_magic(a, buff, ts); if (iso9660->zisofs.making) { if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->cur_file->cur_content->size += ts; } /* Write padding. */ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Compute the logical block number. */ iso9660->cur_file->cur_content->blocks = (int) ((iso9660->cur_file->cur_content->size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); /* * Make next extent. */ ws -= ts; buff = (const void *)(((const unsigned char *)buff) + ts); /* Make a content for next extent. */ con = calloc(1, sizeof(*con)); if (con == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate content data"); return (ARCHIVE_FATAL); } con->offset_of_temp = wb_offset(a); iso9660->cur_file->cur_content->next = con; iso9660->cur_file->cur_content = con; #ifdef HAVE_ZLIB_H iso9660->zisofs.block_offset = 0; #endif } if (iso9660->zisofs.detect_magic) zisofs_detect_magic(a, buff, ws); if (iso9660->zisofs.making) { if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->cur_file->cur_content->size += ws; } return (s); } static ssize_t iso9660_write_data(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; ssize_t r; if (iso9660->cur_file == NULL) return (0); if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) return (0); if (s > iso9660->bytes_remaining) s = (size_t)iso9660->bytes_remaining; if (s == 0) return (0); r = write_iso9660_data(a, buff, s); if (r > 0) iso9660->bytes_remaining -= r; return (r); } static int iso9660_finish_entry(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; if (iso9660->cur_file == NULL) return (ARCHIVE_OK); if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) return (ARCHIVE_OK); if (iso9660->cur_file->content.size == 0) return (ARCHIVE_OK); /* If there are unwritten data, write null data instead. */ while (iso9660->bytes_remaining > 0) { size_t s; s = (iso9660->bytes_remaining > a->null_length)? a->null_length: (size_t)iso9660->bytes_remaining; if (write_iso9660_data(a, a->nulls, s) < 0) return (ARCHIVE_FATAL); iso9660->bytes_remaining -= s; } if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write padding. */ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Compute the logical block number. */ iso9660->cur_file->cur_content->blocks = (int) ((iso9660->cur_file->cur_content->size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); /* Add the current file to data file list. */ isofile_add_data_file(iso9660, iso9660->cur_file); return (ARCHIVE_OK); } static int iso9660_close(struct archive_write *a) { struct iso9660 *iso9660; int ret, blocks; iso9660 = a->format_data; /* * Write remaining data out to the temporary file. */ if (wb_remaining(a) > 0) { ret = wb_write_out(a); if (ret < 0) return (ret); } /* * Preparations... */ #ifdef DEBUG if (iso9660->birth_time == 0) #endif time(&(iso9660->birth_time)); /* * Prepare a bootable ISO image. */ if (iso9660->opt.boot) { /* Find out the boot file entry. */ ret = isoent_find_out_boot_file(a, iso9660->primary.rootent); if (ret < 0) return (ret); /* Reconvert the boot file from zisofs'ed form to * plain form. */ ret = zisofs_rewind_boot_file(a); if (ret < 0) return (ret); /* Write remaining data out to the temporary file. */ if (wb_remaining(a) > 0) { ret = wb_write_out(a); if (ret < 0) return (ret); } /* Create the boot catalog. */ ret = isoent_create_boot_catalog(a, iso9660->primary.rootent); if (ret < 0) return (ret); } /* * Prepare joliet extensions. */ if (iso9660->opt.joliet) { /* Make a new tree for joliet. */ ret = isoent_clone_tree(a, &(iso9660->joliet.rootent), iso9660->primary.rootent); if (ret < 0) return (ret); /* Make sure we have UTF-16BE converters. * if there is no file entry, converters are still * uninitialized. */ if (iso9660->sconv_to_utf16be == NULL) { iso9660->sconv_to_utf16be = archive_string_conversion_to_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_to_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); iso9660->sconv_from_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_from_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } } /* * Make Path Tables. */ ret = isoent_make_path_table(a); if (ret < 0) return (ret); /* * Calculate a total volume size and setup all locations of * contents of an iso9660 image. */ blocks = SYSTEM_AREA_BLOCK + PRIMARY_VOLUME_DESCRIPTOR_BLOCK + VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK + NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; if (iso9660->opt.boot) blocks += BOOT_RECORD_DESCRIPTOR_BLOCK; if (iso9660->opt.joliet) blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; if (iso9660->opt.iso_level == 4) blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; /* Setup the locations of Path Table. */ iso9660->primary.location_type_L_path_table = blocks; blocks += iso9660->primary.path_table_block; iso9660->primary.location_type_M_path_table = blocks; blocks += iso9660->primary.path_table_block; if (iso9660->opt.joliet) { iso9660->joliet.location_type_L_path_table = blocks; blocks += iso9660->joliet.path_table_block; iso9660->joliet.location_type_M_path_table = blocks; blocks += iso9660->joliet.path_table_block; } /* Setup the locations of directories. */ isoent_setup_directory_location(iso9660, blocks, &(iso9660->primary)); blocks += iso9660->primary.total_dir_block; if (iso9660->opt.joliet) { isoent_setup_directory_location(iso9660, blocks, &(iso9660->joliet)); blocks += iso9660->joliet.total_dir_block; } if (iso9660->opt.rr) { iso9660->location_rrip_er = blocks; blocks += RRIP_ER_BLOCK; } /* Setup the locations of all file contents. */ isoent_setup_file_location(iso9660, blocks); blocks += iso9660->total_file_block; if (iso9660->opt.boot && iso9660->opt.boot_info_table) { ret = setup_boot_information(a); if (ret < 0) return (ret); } /* Now we have a total volume size. */ iso9660->volume_space_size = blocks; if (iso9660->opt.pad) iso9660->volume_space_size += PADDING_BLOCK; iso9660->volume_sequence_number = 1; /* * Write an ISO 9660 image. */ /* Switch to start using wbuff as file buffer. */ iso9660->wbuff_remaining = wb_buffmax(); iso9660->wbuff_type = WB_TO_STREAM; iso9660->wbuff_offset = 0; iso9660->wbuff_written = 0; iso9660->wbuff_tail = 0; /* Write The System Area */ ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Primary Volume Descriptor */ ret = write_VD(a, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.boot) { /* Write Boot Record Volume Descriptor */ ret = write_VD_boot_record(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.iso_level == 4) { /* Write Enhanced Volume Descriptor */ iso9660->primary.vdd_type = VDD_ENHANCED; ret = write_VD(a, &(iso9660->primary)); iso9660->primary.vdd_type = VDD_PRIMARY; if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.joliet) { ret = write_VD(a, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write Volume Descriptor Set Terminator */ ret = write_VD_terminator(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Non-ISO File System Information */ ret = write_information_block(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type L Path Table */ ret = write_path_table(a, 0, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type M Path Table */ ret = write_path_table(a, 1, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.joliet) { /* Write Type L Path Table */ ret = write_path_table(a, 0, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Type M Path Table */ ret = write_path_table(a, 1, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write Directory Descriptors */ ret = write_directory_descriptors(a, &(iso9660->primary)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->opt.joliet) { ret = write_directory_descriptors(a, &(iso9660->joliet)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->opt.rr) { /* Write Rockridge ER(Extensions Reference) */ ret = write_rr_ER(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Write File Descriptors */ ret = write_file_descriptors(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Write Padding */ if (iso9660->opt.pad) { ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } if (iso9660->directories_too_deep != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Directories too deep.", archive_entry_pathname( iso9660->directories_too_deep->file->entry)); return (ARCHIVE_WARN); } /* Write remaining data out. */ ret = wb_write_out(a); return (ret); } static int iso9660_free(struct archive_write *a) { struct iso9660 *iso9660; int i, ret; iso9660 = a->format_data; /* Close the temporary file. */ if (iso9660->temp_fd >= 0) close(iso9660->temp_fd); /* Free some stuff for zisofs operations. */ ret = zisofs_free(a); /* Remove directory entries in tree which includes file entries. */ isoent_free_all(iso9660->primary.rootent); for (i = 0; i < iso9660->primary.max_depth; i++) free(iso9660->primary.pathtbl[i].sorted); free(iso9660->primary.pathtbl); if (iso9660->opt.joliet) { isoent_free_all(iso9660->joliet.rootent); for (i = 0; i < iso9660->joliet.max_depth; i++) free(iso9660->joliet.pathtbl[i].sorted); free(iso9660->joliet.pathtbl); } /* Remove isofile entries. */ isofile_free_all_entries(iso9660); isofile_free_hardlinks(iso9660); archive_string_free(&(iso9660->cur_dirstr)); archive_string_free(&(iso9660->volume_identifier)); archive_string_free(&(iso9660->publisher_identifier)); archive_string_free(&(iso9660->data_preparer_identifier)); archive_string_free(&(iso9660->application_identifier)); archive_string_free(&(iso9660->copyright_file_identifier)); archive_string_free(&(iso9660->abstract_file_identifier)); archive_string_free(&(iso9660->bibliographic_file_identifier)); archive_string_free(&(iso9660->el_torito.catalog_filename)); archive_string_free(&(iso9660->el_torito.boot_filename)); archive_string_free(&(iso9660->el_torito.id)); archive_string_free(&(iso9660->utf16be)); archive_string_free(&(iso9660->mbs)); free(iso9660); a->format_data = NULL; return (ret); } /* * Get the System Identifier */ static void get_system_identitier(char *system_id, size_t size) { #if defined(HAVE_SYS_UTSNAME_H) struct utsname u; uname(&u); strncpy(system_id, u.sysname, size-1); system_id[size-1] = '\0'; #elif defined(_WIN32) && !defined(__CYGWIN__) strncpy(system_id, "Windows", size-1); system_id[size-1] = '\0'; #else -#error no way to get the system identifier on your platform. + strncpy(system_id, "Unknown", size-1); + system_id[size-1] = '\0'; #endif } static void set_str(unsigned char *p, const char *s, size_t l, char f, const char *map) { unsigned char c; if (s == NULL) s = ""; while ((c = *s++) != 0 && l > 0) { if (c >= 0x80 || map[c] == 0) { /* illegal character */ if (c >= 'a' && c <= 'z') { /* convert c from a-z to A-Z */ c -= 0x20; } else c = 0x5f; } *p++ = c; l--; } /* If l isn't zero, fill p buffer by the character * which indicated by f. */ if (l > 0) memset(p , f, l); } static inline int joliet_allowed_char(unsigned char high, unsigned char low) { int utf16 = (high << 8) | low; if (utf16 <= 0x001F) return (0); switch (utf16) { case 0x002A: /* '*' */ case 0x002F: /* '/' */ case 0x003A: /* ':' */ case 0x003B: /* ';' */ case 0x003F: /* '?' */ case 0x005C: /* '\' */ return (0);/* Not allowed. */ } return (1); } static int set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s, size_t l, uint16_t uf, enum vdc vdc) { size_t size, i; int onepad; if (s == NULL) s = ""; if (l & 0x01) { onepad = 1; l &= ~1; } else onepad = 0; if (vdc == VDC_UCS2) { struct iso9660 *iso9660 = a->format_data; if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s), iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } size = iso9660->utf16be.length; if (size > l) size = l; memcpy(p, iso9660->utf16be.s, size); } else { const uint16_t *u16 = (const uint16_t *)s; size = 0; while (*u16++) size += 2; if (size > l) size = l; memcpy(p, s, size); } for (i = 0; i < size; i += 2, p += 2) { if (!joliet_allowed_char(p[0], p[1])) archive_be16enc(p, 0x005F);/* '_' */ } l -= size; while (l > 0) { archive_be16enc(p, uf); p += 2; l -= 2; } if (onepad) *p = 0; return (ARCHIVE_OK); } static const char a_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char a1_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char d_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ }; static const char d1_characters_map[0x80] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ }; static int set_str_a_characters_bp(struct archive_write *a, unsigned char *bp, int from, int to, const char *s, enum vdc vdc) { int r; switch (vdc) { case VDC_STD: set_str(bp+from, s, to - from + 1, 0x20, a_characters_map); r = ARCHIVE_OK; break; case VDC_LOWERCASE: set_str(bp+from, s, to - from + 1, 0x20, a1_characters_map); r = ARCHIVE_OK; break; case VDC_UCS2: case VDC_UCS2_DIRECT: r = set_str_utf16be(a, bp+from, s, to - from + 1, 0x0020, vdc); break; default: r = ARCHIVE_FATAL; } return (r); } static int set_str_d_characters_bp(struct archive_write *a, unsigned char *bp, int from, int to, const char *s, enum vdc vdc) { int r; switch (vdc) { case VDC_STD: set_str(bp+from, s, to - from + 1, 0x20, d_characters_map); r = ARCHIVE_OK; break; case VDC_LOWERCASE: set_str(bp+from, s, to - from + 1, 0x20, d1_characters_map); r = ARCHIVE_OK; break; case VDC_UCS2: case VDC_UCS2_DIRECT: r = set_str_utf16be(a, bp+from, s, to - from + 1, 0x0020, vdc); break; default: r = ARCHIVE_FATAL; } return (r); } static void set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver) { /* Volume Descriptor Type */ bp[1] = (unsigned char)type; /* Standard Identifier */ memcpy(bp + 2, "CD001", 5); /* Volume Descriptor Version */ bp[7] = ver; } static inline void set_unused_field_bp(unsigned char *bp, int from, int to) { memset(bp + from, 0, to - from + 1); } /* * 8-bit unsigned numerical values. * ISO9660 Standard 7.1.1 */ static inline void set_num_711(unsigned char *p, unsigned char value) { *p = value; } /* * 8-bit signed numerical values. * ISO9660 Standard 7.1.2 */ static inline void set_num_712(unsigned char *p, char value) { *((char *)p) = value; } /* * Least significant byte first. * ISO9660 Standard 7.2.1 */ static inline void set_num_721(unsigned char *p, uint16_t value) { archive_le16enc(p, value); } /* * Most significant byte first. * ISO9660 Standard 7.2.2 */ static inline void set_num_722(unsigned char *p, uint16_t value) { archive_be16enc(p, value); } /* * Both-byte orders. * ISO9660 Standard 7.2.3 */ static void set_num_723(unsigned char *p, uint16_t value) { archive_le16enc(p, value); archive_be16enc(p+2, value); } /* * Least significant byte first. * ISO9660 Standard 7.3.1 */ static inline void set_num_731(unsigned char *p, uint32_t value) { archive_le32enc(p, value); } /* * Most significant byte first. * ISO9660 Standard 7.3.2 */ static inline void set_num_732(unsigned char *p, uint32_t value) { archive_be32enc(p, value); } /* * Both-byte orders. * ISO9660 Standard 7.3.3 */ static inline void set_num_733(unsigned char *p, uint32_t value) { archive_le32enc(p, value); archive_be32enc(p+4, value); } static void set_digit(unsigned char *p, size_t s, int value) { while (s--) { p[s] = '0' + (value % 10); value /= 10; } } #if defined(HAVE_STRUCT_TM_TM_GMTOFF) #define get_gmoffset(tm) ((tm)->tm_gmtoff) #elif defined(HAVE_STRUCT_TM___TM_GMTOFF) #define get_gmoffset(tm) ((tm)->__tm_gmtoff) #else static long get_gmoffset(struct tm *tm) { long offset; #if defined(HAVE__GET_TIMEZONE) _get_timezone(&offset); #elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) offset = _timezone; #else offset = timezone; #endif offset *= -1; if (tm->tm_isdst) offset += 3600; return (offset); } #endif static void get_tmfromtime(struct tm *tm, time_t *t) { #if HAVE_LOCALTIME_R tzset(); localtime_r(t, tm); #elif HAVE__LOCALTIME64_S __time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits _localtime64_s(tm, &tmp_t); #else memcpy(tm, localtime(t), sizeof(*tm)); #endif } /* * Date and Time Format. * ISO9660 Standard 8.4.26.1 */ static void set_date_time(unsigned char *p, time_t t) { struct tm tm; get_tmfromtime(&tm, &t); set_digit(p, 4, tm.tm_year + 1900); set_digit(p+4, 2, tm.tm_mon + 1); set_digit(p+6, 2, tm.tm_mday); set_digit(p+8, 2, tm.tm_hour); set_digit(p+10, 2, tm.tm_min); set_digit(p+12, 2, tm.tm_sec); set_digit(p+14, 2, 0); set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15))); } static void set_date_time_null(unsigned char *p) { memset(p, (int)'0', 16); p[16] = 0; } static void set_time_915(unsigned char *p, time_t t) { struct tm tm; get_tmfromtime(&tm, &t); set_num_711(p+0, tm.tm_year); set_num_711(p+1, tm.tm_mon+1); set_num_711(p+2, tm.tm_mday); set_num_711(p+3, tm.tm_hour); set_num_711(p+4, tm.tm_min); set_num_711(p+5, tm.tm_sec); set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15))); } /* * Write SUSP "CE" System Use Entry. */ static int set_SUSP_CE(unsigned char *p, int location, int offset, int size) { unsigned char *bp = p -1; /* Extend the System Use Area * "CE" Format: * len ver * +----+----+----+----+-----------+-----------+ * | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 | * +----+----+----+----+-----------+-----------+ * 0 1 2 3 4 12 20 * +-----------+ * | LOCATION3 | * +-----------+ * 20 28 * LOCATION1 : Location of Continuation of System Use Area. * LOCATION2 : Offset to Start of Continuation. * LOCATION3 : Length of the Continuation. */ bp[1] = 'C'; bp[2] = 'E'; bp[3] = RR_CE_SIZE; /* length */ bp[4] = 1; /* version */ set_num_733(bp+5, location); set_num_733(bp+13, offset); set_num_733(bp+21, size); return (RR_CE_SIZE); } /* * The functions, which names are beginning with extra_, are used to * control extra records. * The maximum size of a Directory Record is 254. When a filename is * very long, all of RRIP data of a file won't stored to the Directory * Record and so remaining RRIP data store to an extra record instead. */ static unsigned char * extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent, struct ctl_extr_rec *ctl) { ctl->bp = bp; if (bp != NULL) bp += dr_len; ctl->use_extr = 0; ctl->isoent = isoent; ctl->ce_ptr = NULL; ctl->cur_len = ctl->dr_len = dr_len; ctl->limit = DR_LIMIT; return (bp); } static void extra_close_record(struct ctl_extr_rec *ctl, int ce_size) { int padding = 0; if (ce_size > 0) extra_tell_used_size(ctl, ce_size); /* Padding. */ if (ctl->cur_len & 0x01) { ctl->cur_len++; if (ctl->bp != NULL) ctl->bp[ctl->cur_len] = 0; padding = 1; } if (ctl->use_extr) { if (ctl->ce_ptr != NULL) set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc, ctl->extr_off, ctl->cur_len - padding); } else ctl->dr_len = ctl->cur_len; } #define extra_space(ctl) ((ctl)->limit - (ctl)->cur_len) static unsigned char * extra_next_record(struct ctl_extr_rec *ctl, int length) { int cur_len = ctl->cur_len;/* save cur_len */ /* Close the current extra record or Directory Record. */ extra_close_record(ctl, RR_CE_SIZE); /* Get a next extra record. */ ctl->use_extr = 1; if (ctl->bp != NULL) { /* Storing data into an extra record. */ unsigned char *p; /* Save the pointer where a CE extension will be * stored to. */ ctl->ce_ptr = &ctl->bp[cur_len+1]; p = extra_get_record(ctl->isoent, &ctl->limit, &ctl->extr_off, &ctl->extr_loc); ctl->bp = p - 1;/* the base of bp offset is 1. */ } else /* Calculating the size of an extra record. */ (void)extra_get_record(ctl->isoent, &ctl->limit, NULL, NULL); ctl->cur_len = 0; /* Check if an extra record is almost full. * If so, get a next one. */ if (extra_space(ctl) < length) (void)extra_next_record(ctl, length); return (ctl->bp); } static inline struct extr_rec * extra_last_record(struct isoent *isoent) { if (isoent->extr_rec_list.first == NULL) return (NULL); return ((struct extr_rec *)(void *) ((char *)(isoent->extr_rec_list.last) - offsetof(struct extr_rec, next))); } static unsigned char * extra_get_record(struct isoent *isoent, int *space, int *off, int *loc) { struct extr_rec *rec; isoent = isoent->parent; if (off != NULL) { /* Storing data into an extra record. */ rec = isoent->extr_rec_list.current; if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) rec = rec->next; } else { /* Calculating the size of an extra record. */ rec = extra_last_record(isoent); if (rec == NULL || DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) { rec = malloc(sizeof(*rec)); if (rec == NULL) return (NULL); rec->location = 0; rec->offset = 0; /* Insert `rec` into the tail of isoent->extr_rec_list */ rec->next = NULL; /* * Note: testing isoent->extr_rec_list.last == NULL * here is really unneeded since it has been already * initialized at isoent_new function but Clang Static * Analyzer claims that it is dereference of null * pointer. */ if (isoent->extr_rec_list.last == NULL) isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); *isoent->extr_rec_list.last = rec; isoent->extr_rec_list.last = &(rec->next); } } *space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY; if (*space & 0x01) *space -= 1;/* Keep padding space. */ if (off != NULL) *off = rec->offset; if (loc != NULL) *loc = rec->location; isoent->extr_rec_list.current = rec; return (&rec->buf[rec->offset]); } static void extra_tell_used_size(struct ctl_extr_rec *ctl, int size) { struct isoent *isoent; struct extr_rec *rec; if (ctl->use_extr) { isoent = ctl->isoent->parent; rec = isoent->extr_rec_list.current; if (rec != NULL) rec->offset += size; } ctl->cur_len += size; } static int extra_setup_location(struct isoent *isoent, int location) { struct extr_rec *rec; int cnt; cnt = 0; rec = isoent->extr_rec_list.first; isoent->extr_rec_list.current = rec; while (rec) { cnt++; rec->location = location++; rec->offset = 0; rec = rec->next; } return (cnt); } /* * Create the RRIP entries. */ static int set_directory_record_rr(unsigned char *bp, int dr_len, struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t) { /* Flags(BP 5) of the Rockridge "RR" System Use Field */ unsigned char rr_flag; #define RR_USE_PX 0x01 #define RR_USE_PN 0x02 #define RR_USE_SL 0x04 #define RR_USE_NM 0x08 #define RR_USE_CL 0x10 #define RR_USE_PL 0x20 #define RR_USE_RE 0x40 #define RR_USE_TF 0x80 int length; struct ctl_extr_rec ctl; struct isoent *rr_parent, *pxent; struct isofile *file; bp = extra_open_record(bp, dr_len, isoent, &ctl); if (t == DIR_REC_PARENT) { rr_parent = isoent->rr_parent; pxent = isoent->parent; if (rr_parent != NULL) isoent = rr_parent; else isoent = isoent->parent; } else { rr_parent = NULL; pxent = isoent; } file = isoent->file; if (t != DIR_REC_NORMAL) { rr_flag = RR_USE_PX | RR_USE_TF; if (rr_parent != NULL) rr_flag |= RR_USE_PL; } else { rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF; if (archive_entry_filetype(file->entry) == AE_IFLNK) rr_flag |= RR_USE_SL; if (isoent->rr_parent != NULL) rr_flag |= RR_USE_RE; if (isoent->rr_child != NULL) rr_flag |= RR_USE_CL; if (archive_entry_filetype(file->entry) == AE_IFCHR || archive_entry_filetype(file->entry) == AE_IFBLK) rr_flag |= RR_USE_PN; #ifdef COMPAT_MKISOFS /* * mkisofs 2.01.01a63 records "RE" extension to * the entry of "rr_moved" directory. * I don't understand this behavior. */ if (isoent->virtual && isoent->parent == iso9660->primary.rootent && strcmp(isoent->file->basename.s, "rr_moved") == 0) rr_flag |= RR_USE_RE; #endif } /* Write "SP" System Use Entry. */ if (t == DIR_REC_SELF && isoent == isoent->parent) { length = 7; if (bp != NULL) { bp[1] = 'S'; bp[2] = 'P'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = 0xBE; /* Check Byte */ bp[6] = 0xEF; /* Check Byte */ bp[7] = 0; bp += length; } extra_tell_used_size(&ctl, length); } /* Write "RR" System Use Entry. */ length = 5; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'R'; bp[2] = 'R'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = rr_flag; bp += length; } extra_tell_used_size(&ctl, length); /* Write "NM" System Use Entry. */ if (rr_flag & RR_USE_NM) { /* * "NM" Format: * e.g. a basename is 'foo' * len ver flg * +----+----+----+----+----+----+----+----+ * | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'| * +----+----+----+----+----+----+----+----+ * <----------------- len -----------------> */ size_t nmlen = file->basename.length; const char *nm = file->basename.s; size_t nmmax; if (extra_space(&ctl) < 6) bp = extra_next_record(&ctl, 6); if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; bp[4] = 1; /* version */ } nmmax = extra_space(&ctl); if (nmmax > 0xff) nmmax = 0xff; while (nmlen + 5 > nmmax) { length = (int)nmmax; if (bp != NULL) { bp[3] = length; bp[5] = 0x01;/* Alternate Name continues * in next "NM" field */ memcpy(bp+6, nm, length - 5); bp += length; } nmlen -= length - 5; nm += length - 5; extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 6) { bp = extra_next_record(&ctl, 6); nmmax = extra_space(&ctl); if (nmmax > 0xff) nmmax = 0xff; } if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; bp[4] = 1; /* version */ } } length = 5 + (int)nmlen; if (bp != NULL) { bp[3] = length; bp[5] = 0; memcpy(bp+6, nm, nmlen); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PX" System Use Entry. */ if (rr_flag & RR_USE_PX) { /* * "PX" Format: * len ver * +----+----+----+----+-----------+-----------+ * | 'P'| 'X'| 2C | 01 | FILE MODE | LINKS | * +----+----+----+----+-----------+-----------+ * 0 1 2 3 4 12 20 * +-----------+-----------+------------------+ * | USER ID | GROUP ID |FILE SERIAL NUMBER| * +-----------+-----------+------------------+ * 20 28 36 44 */ length = 44; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { mode_t mode; int64_t uid; int64_t gid; mode = archive_entry_mode(file->entry); uid = archive_entry_uid(file->entry); gid = archive_entry_gid(file->entry); if (iso9660->opt.rr == OPT_RR_USEFUL) { /* * This action is similar to mkisofs -r option * but our rockridge=useful option does not * set a zero to uid and gid. */ /* set all read bit ON */ mode |= 0444; #if !defined(_WIN32) && !defined(__CYGWIN__) if (mode & 0111) #endif /* set all exec bit ON */ mode |= 0111; /* clear all write bits. */ mode &= ~0222; /* clear setuid,setgid,sticky bits. */ mode &= ~07000; } bp[1] = 'P'; bp[2] = 'X'; bp[3] = length; bp[4] = 1; /* version */ /* file mode */ set_num_733(bp+5, mode); /* file links (stat.st_nlink) */ set_num_733(bp+13, archive_entry_nlink(file->entry)); set_num_733(bp+21, (uint32_t)uid); set_num_733(bp+29, (uint32_t)gid); /* File Serial Number */ if (pxent->dir) set_num_733(bp+37, pxent->dir_location); else if (file->hardlink_target != NULL) set_num_733(bp+37, file->hardlink_target->cur_content->location); else set_num_733(bp+37, file->cur_content->location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "SL" System Use Entry. */ if (rr_flag & RR_USE_SL) { /* * "SL" Format: * e.g. a symbolic name is 'foo/bar' * len ver flg * +----+----+----+----+----+------------+ * | 'S'| 'L'| 0F | 01 | 00 | components | * +----+----+----+----+----+-----+------+ * 0 1 2 3 4 5 ...|... 15 * <----------------- len --------+------> * components : | * cflg clen | * +----+----+----+----+----+ | * | 00 | 03 | 'f'| 'o'| 'o'| <---+ * +----+----+----+----+----+ | * 5 6 7 8 9 10 | * cflg clen | * +----+----+----+----+----+ | * | 00 | 03 | 'b'| 'a'| 'r'| <---+ * +----+----+----+----+----+ * 10 11 12 13 14 15 * * - cflg : flag of component * - clen : length of component */ const char *sl; char sl_last; if (extra_space(&ctl) < 7) bp = extra_next_record(&ctl, 7); sl = file->symlink.s; sl_last = '\0'; if (bp != NULL) { bp[1] = 'S'; bp[2] = 'L'; bp[4] = 1; /* version */ } for (;;) { unsigned char *nc, *cf, *cl, cldmy = 0; int sllen, slmax; slmax = extra_space(&ctl); if (slmax > 0xff) slmax = 0xff; if (bp != NULL) nc = &bp[6]; else nc = NULL; cf = cl = NULL; sllen = 0; while (*sl && sllen + 11 < slmax) { if (sl_last == '\0' && sl[0] == '/') { /* * flg len * +----+----+ * | 08 | 00 | ROOT component. * +----+----+ ("/") * * Root component has to appear * at the first component only. */ if (nc != NULL) { cf = nc++; *cf = 0x08; /* ROOT */ *nc++ = 0; } sllen += 2; sl++; sl_last = '/'; cl = NULL; continue; } if (((sl_last == '\0' || sl_last == '/') && sl[0] == '.' && sl[1] == '.' && (sl[2] == '/' || sl[2] == '\0')) || (sl[0] == '/' && sl[1] == '.' && sl[2] == '.' && (sl[3] == '/' || sl[3] == '\0'))) { /* * flg len * +----+----+ * | 04 | 00 | PARENT component. * +----+----+ ("..") */ if (nc != NULL) { cf = nc++; *cf = 0x04; /* PARENT */ *nc++ = 0; } sllen += 2; if (sl[0] == '/') sl += 3;/* skip "/.." */ else sl += 2;/* skip ".." */ sl_last = '.'; cl = NULL; continue; } if (((sl_last == '\0' || sl_last == '/') && sl[0] == '.' && (sl[1] == '/' || sl[1] == '\0')) || (sl[0] == '/' && sl[1] == '.' && (sl[2] == '/' || sl[2] == '\0'))) { /* * flg len * +----+----+ * | 02 | 00 | CURRENT component. * +----+----+ (".") */ if (nc != NULL) { cf = nc++; *cf = 0x02; /* CURRENT */ *nc++ = 0; } sllen += 2; if (sl[0] == '/') sl += 2;/* skip "/." */ else sl ++; /* skip "." */ sl_last = '.'; cl = NULL; continue; } if (sl[0] == '/' || cl == NULL) { if (nc != NULL) { cf = nc++; *cf = 0; cl = nc++; *cl = 0; } else cl = &cldmy; sllen += 2; if (sl[0] == '/') { sl_last = *sl++; continue; } } sl_last = *sl++; if (nc != NULL) { *nc++ = sl_last; (*cl) ++; } sllen++; } if (*sl) { length = 5 + sllen; if (bp != NULL) { /* * Mark flg as CONTINUE component. */ *cf |= 0x01; /* * len ver flg * +----+----+----+----+----+- * | 'S'| 'L'| XX | 01 | 01 | * +----+----+----+----+----+- * ^ * continues in next "SL" */ bp[3] = length; bp[5] = 0x01;/* This Symbolic Link * continues in next * "SL" field */ bp += length; } extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 11) bp = extra_next_record(&ctl, 11); if (bp != NULL) { /* Next 'SL' */ bp[1] = 'S'; bp[2] = 'L'; bp[4] = 1; /* version */ } } else { length = 5 + sllen; if (bp != NULL) { bp[3] = length; bp[5] = 0; bp += length; } extra_tell_used_size(&ctl, length); break; } } } /* Write "TF" System Use Entry. */ if (rr_flag & RR_USE_TF) { /* * "TF" Format: * len ver * +----+----+----+----+-----+-------------+ * | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS | * +----+----+----+----+-----+-------------+ * 0 1 2 3 4 5 XX * TIME STAMPS : ISO 9660 Standard 9.1.5. * If TF_LONG_FORM FLAGS is set, * use ISO9660 Standard 8.4.26.1. */ #define TF_CREATION 0x01 /* Creation time recorded */ #define TF_MODIFY 0x02 /* Modification time recorded */ #define TF_ACCESS 0x04 /* Last Access time recorded */ #define TF_ATTRIBUTES 0x08 /* Last Attribute Change time recorded */ #define TF_BACKUP 0x10 /* Last Backup time recorded */ #define TF_EXPIRATION 0x20 /* Expiration time recorded */ #define TF_EFFECTIVE 0x40 /* Effective time recorded */ #define TF_LONG_FORM 0x80 /* ISO 9660 17-byte time format used */ unsigned char tf_flags; length = 5; tf_flags = 0; #ifndef COMPAT_MKISOFS if (archive_entry_birthtime_is_set(file->entry) && archive_entry_birthtime(file->entry) <= archive_entry_mtime(file->entry)) { length += 7; tf_flags |= TF_CREATION; } #endif if (archive_entry_mtime_is_set(file->entry)) { length += 7; tf_flags |= TF_MODIFY; } if (archive_entry_atime_is_set(file->entry)) { length += 7; tf_flags |= TF_ACCESS; } if (archive_entry_ctime_is_set(file->entry)) { length += 7; tf_flags |= TF_ATTRIBUTES; } if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'T'; bp[2] = 'F'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = tf_flags; bp += 5; /* Creation time */ if (tf_flags & TF_CREATION) { set_time_915(bp+1, archive_entry_birthtime(file->entry)); bp += 7; } /* Modification time */ if (tf_flags & TF_MODIFY) { set_time_915(bp+1, archive_entry_mtime(file->entry)); bp += 7; } /* Last Access time */ if (tf_flags & TF_ACCESS) { set_time_915(bp+1, archive_entry_atime(file->entry)); bp += 7; } /* Last Attribute Change time */ if (tf_flags & TF_ATTRIBUTES) { set_time_915(bp+1, archive_entry_ctime(file->entry)); bp += 7; } } extra_tell_used_size(&ctl, length); } /* Write "RE" System Use Entry. */ if (rr_flag & RR_USE_RE) { /* * "RE" Format: * len ver * +----+----+----+----+ * | 'R'| 'E'| 04 | 01 | * +----+----+----+----+ * 0 1 2 3 4 */ length = 4; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'R'; bp[2] = 'E'; bp[3] = length; bp[4] = 1; /* version */ bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PL" System Use Entry. */ if (rr_flag & RR_USE_PL) { /* * "PL" Format: * len ver * +----+----+----+----+------------+ * | 'P'| 'L'| 0C | 01 | *LOCATION | * +----+----+----+----+------------+ * 0 1 2 3 4 12 * *LOCATION: location of parent directory */ length = 12; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'P'; bp[2] = 'L'; bp[3] = length; bp[4] = 1; /* version */ set_num_733(bp + 5, rr_parent->dir_location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "CL" System Use Entry. */ if (rr_flag & RR_USE_CL) { /* * "CL" Format: * len ver * +----+----+----+----+------------+ * | 'C'| 'L'| 0C | 01 | *LOCATION | * +----+----+----+----+------------+ * 0 1 2 3 4 12 * *LOCATION: location of child directory */ length = 12; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'C'; bp[2] = 'L'; bp[3] = length; bp[4] = 1; /* version */ set_num_733(bp + 5, isoent->rr_child->dir_location); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "PN" System Use Entry. */ if (rr_flag & RR_USE_PN) { /* * "PN" Format: * len ver * +----+----+----+----+------------+------------+ * | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low | * +----+----+----+----+------------+------------+ * 0 1 2 3 4 12 20 */ length = 20; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { uint64_t dev; bp[1] = 'P'; bp[2] = 'N'; bp[3] = length; bp[4] = 1; /* version */ dev = (uint64_t)archive_entry_rdev(file->entry); set_num_733(bp + 5, (uint32_t)(dev >> 32)); set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF)); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "ZF" System Use Entry. */ if (file->zisofs.header_size) { /* * "ZF" Format: * len ver * +----+----+----+----+----+----+-------------+ * | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size | * +----+----+----+----+----+----+-------------+ * 0 1 2 3 4 5 6 7 * +--------------------+-------------------+ * | Log2 of block Size | Uncompressed Size | * +--------------------+-------------------+ * 7 8 16 */ length = 16; if (extra_space(&ctl) < length) bp = extra_next_record(&ctl, length); if (bp != NULL) { bp[1] = 'Z'; bp[2] = 'F'; bp[3] = length; bp[4] = 1; /* version */ bp[5] = 'p'; bp[6] = 'z'; bp[7] = file->zisofs.header_size; bp[8] = file->zisofs.log2_bs; set_num_733(bp + 9, file->zisofs.uncompressed_size); bp += length; } extra_tell_used_size(&ctl, length); } /* Write "CE" System Use Entry. */ if (t == DIR_REC_SELF && isoent == isoent->parent) { length = RR_CE_SIZE; if (bp != NULL) set_SUSP_CE(bp+1, iso9660->location_rrip_er, 0, RRIP_ER_SIZE); extra_tell_used_size(&ctl, length); } extra_close_record(&ctl, 0); return (ctl.dr_len); } /* * Write data of a Directory Record or calculate writing bytes itself. * If parameter `p' is NULL, calculates the size of writing data, which * a Directory Record needs to write, then it saved and return * the calculated size. * Parameter `n' is a remaining size of buffer. when parameter `p' is * not NULL, check whether that `n' is not less than the saved size. * if that `n' is small, return zero. * * This format of the Directory Record is according to * ISO9660 Standard 9.1 */ static int set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t, enum vdd_type vdd_type) { unsigned char *bp; size_t dr_len; size_t fi_len; if (p != NULL) { /* * Check whether a write buffer size is less than the * saved size which is needed to write this Directory * Record. */ switch (t) { case DIR_REC_VD: dr_len = isoent->dr_len.vd; break; case DIR_REC_SELF: dr_len = isoent->dr_len.self; break; case DIR_REC_PARENT: dr_len = isoent->dr_len.parent; break; case DIR_REC_NORMAL: default: dr_len = isoent->dr_len.normal; break; } if (dr_len > n) return (0);/* Needs more buffer size. */ } if (t == DIR_REC_NORMAL && isoent->identifier != NULL) fi_len = isoent->id_len; else fi_len = 1; if (p != NULL) { struct isoent *xisoent; struct isofile *file; unsigned char flag; if (t == DIR_REC_PARENT) xisoent = isoent->parent; else xisoent = isoent; file = isoent->file; if (file->hardlink_target != NULL) file = file->hardlink_target; /* Make a file flag. */ if (xisoent->dir) flag = FILE_FLAG_DIRECTORY; else { if (file->cur_content->next != NULL) flag = FILE_FLAG_MULTI_EXTENT; else flag = 0; } bp = p -1; /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ if (xisoent->dir) set_num_733(bp+3, xisoent->dir_location); else set_num_733(bp+3, file->cur_content->location); /* Data Length */ if (xisoent->dir) set_num_733(bp+11, xisoent->dir_block * LOGICAL_BLOCK_SIZE); else set_num_733(bp+11, (uint32_t)file->cur_content->size); /* Recording Date and Time */ /* NOTE: * If a file type is symbolic link, you are seeing this * field value is different from a value mkisofs makes. * libarchive uses lstat to get this one, but it * seems mkisofs uses stat to get. */ set_time_915(bp+19, archive_entry_mtime(xisoent->file->entry)); /* File Flags */ bp[26] = flag; /* File Unit Size */ set_num_711(bp+27, 0); /* Interleave Gap Size */ set_num_711(bp+28, 0); /* Volume Sequence Number */ set_num_723(bp+29, iso9660->volume_sequence_number); /* Length of File Identifier */ set_num_711(bp+33, (unsigned char)fi_len); /* File Identifier */ switch (t) { case DIR_REC_VD: case DIR_REC_SELF: set_num_711(bp+34, 0); break; case DIR_REC_PARENT: set_num_711(bp+34, 1); break; case DIR_REC_NORMAL: if (isoent->identifier != NULL) memcpy(bp+34, isoent->identifier, fi_len); else set_num_711(bp+34, 0); break; } } else bp = NULL; dr_len = 33 + fi_len; /* Padding Field */ if (dr_len & 0x01) { dr_len ++; if (p != NULL) bp[dr_len] = 0; } /* Volume Descriptor does not record extension. */ if (t == DIR_REC_VD) { if (p != NULL) /* Length of Directory Record */ set_num_711(p, (unsigned char)dr_len); else isoent->dr_len.vd = (int)dr_len; return ((int)dr_len); } /* Rockridge */ if (iso9660->opt.rr && vdd_type != VDD_JOLIET) dr_len = set_directory_record_rr(bp, (int)dr_len, isoent, iso9660, t); if (p != NULL) /* Length of Directory Record */ set_num_711(p, (unsigned char)dr_len); else { /* * Save the size which is needed to write this * Directory Record. */ switch (t) { case DIR_REC_VD: /* This case does not come, but compiler * complains that DIR_REC_VD not handled * in switch .... */ break; case DIR_REC_SELF: isoent->dr_len.self = (int)dr_len; break; case DIR_REC_PARENT: isoent->dr_len.parent = (int)dr_len; break; case DIR_REC_NORMAL: isoent->dr_len.normal = (int)dr_len; break; } } return ((int)dr_len); } /* * Calculate the size of a directory record. */ static inline int get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent, enum dir_rec_type t, enum vdd_type vdd_type) { return (set_directory_record(NULL, SIZE_MAX, isoent, iso9660, t, vdd_type)); } /* * Manage to write ISO-image data with wbuff to reduce calling * __archive_write_output() for performance. */ static inline unsigned char * wb_buffptr(struct archive_write *a) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; return (&(iso9660->wbuff[sizeof(iso9660->wbuff) - iso9660->wbuff_remaining])); } static int wb_write_out(struct archive_write *a) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; size_t wsize, nw; int r; wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; nw = wsize % LOGICAL_BLOCK_SIZE; if (iso9660->wbuff_type == WB_TO_STREAM) r = __archive_write_output(a, iso9660->wbuff, wsize - nw); else r = write_to_temp(a, iso9660->wbuff, wsize - nw); /* Increase the offset. */ iso9660->wbuff_offset += wsize - nw; if (iso9660->wbuff_offset > iso9660->wbuff_written) iso9660->wbuff_written = iso9660->wbuff_offset; iso9660->wbuff_remaining = sizeof(iso9660->wbuff); if (nw) { iso9660->wbuff_remaining -= nw; memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw); } return (r); } static int wb_consume(struct archive_write *a, size_t size) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; if (size > iso9660->wbuff_remaining || iso9660->wbuff_remaining == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal Programming error: iso9660:wb_consume()" " size=%jd, wbuff_remaining=%jd", (intmax_t)size, (intmax_t)iso9660->wbuff_remaining); return (ARCHIVE_FATAL); } iso9660->wbuff_remaining -= size; if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE) return (wb_write_out(a)); return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int wb_set_offset(struct archive_write *a, int64_t off) { struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; int64_t used, ext_bytes; if (iso9660->wbuff_type != WB_TO_TEMP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal Programming error: iso9660:wb_set_offset()"); return (ARCHIVE_FATAL); } used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; if (iso9660->wbuff_offset + used > iso9660->wbuff_tail) iso9660->wbuff_tail = iso9660->wbuff_offset + used; if (iso9660->wbuff_offset < iso9660->wbuff_written) { if (used > 0 && write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->wbuff_offset = iso9660->wbuff_written; lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET); iso9660->wbuff_remaining = sizeof(iso9660->wbuff); used = 0; } if (off < iso9660->wbuff_offset) { /* * Write out waiting data. */ if (used > 0) { if (wb_write_out(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); } lseek(iso9660->temp_fd, off, SEEK_SET); iso9660->wbuff_offset = off; iso9660->wbuff_remaining = sizeof(iso9660->wbuff); } else if (off <= iso9660->wbuff_tail) { iso9660->wbuff_remaining = (size_t) (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset)); } else { ext_bytes = off - iso9660->wbuff_tail; iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff) - (iso9660->wbuff_tail - iso9660->wbuff_offset)); while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) { if (write_null(a, (size_t)iso9660->wbuff_remaining) != ARCHIVE_OK) return (ARCHIVE_FATAL); ext_bytes -= iso9660->wbuff_remaining; } if (ext_bytes > 0) { if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ static int write_null(struct archive_write *a, size_t size) { size_t remaining; unsigned char *p, *old; int r; remaining = wb_remaining(a); p = wb_buffptr(a); if (size <= remaining) { memset(p, 0, size); return (wb_consume(a, size)); } memset(p, 0, remaining); r = wb_consume(a, remaining); if (r != ARCHIVE_OK) return (r); size -= remaining; old = p; p = wb_buffptr(a); memset(p, 0, old - p); remaining = wb_remaining(a); while (size) { size_t wsize = size; if (wsize > remaining) wsize = remaining; r = wb_consume(a, wsize); if (r != ARCHIVE_OK) return (r); size -= wsize; } return (ARCHIVE_OK); } /* * Write Volume Descriptor Set Terminator */ static int write_VD_terminator(struct archive_write *a) { unsigned char *bp; bp = wb_buffptr(a) -1; set_VD_bp(bp, VDT_TERMINATOR, 1); set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc, struct archive_write *a, struct vdd *vdd, struct archive_string *id, const char *label, int leading_under, enum char_type char_type) { char identifier[256]; struct isoent *isoent; const char *ids; size_t len; int r; if (id->length > 0 && leading_under && id->s[0] != '_') { if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc); else r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc); } else if (id->length > 0) { ids = id->s; if (leading_under) ids++; isoent = isoent_find_entry(vdd->rootent, ids); if (isoent == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Not Found %s `%s'.", label, ids); return (ARCHIVE_FATAL); } len = isoent->ext_off + isoent->ext_len; if (vdd->vdd_type == VDD_JOLIET) { if (len > sizeof(identifier)-2) len = sizeof(identifier)-2; } else { if (len > sizeof(identifier)-1) len = sizeof(identifier)-1; } memcpy(identifier, isoent->identifier, len); identifier[len] = '\0'; if (vdd->vdd_type == VDD_JOLIET) { identifier[len+1] = 0; vdc = VDC_UCS2_DIRECT; } if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, identifier, vdc); else r = set_str_d_characters_bp(a, bp, from, to, identifier, vdc); } else { if (char_type == A_CHAR) r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc); else r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc); } return (r); } /* * Write Primary/Supplementary Volume Descriptor */ static int write_VD(struct archive_write *a, struct vdd *vdd) { struct iso9660 *iso9660; unsigned char *bp; uint16_t volume_set_size = 1; char identifier[256]; enum VD_type vdt; enum vdc vdc; unsigned char vd_ver, fst_ver; int r; iso9660 = a->format_data; switch (vdd->vdd_type) { case VDD_JOLIET: vdt = VDT_SUPPLEMENTARY; vd_ver = fst_ver = 1; vdc = VDC_UCS2; break; case VDD_ENHANCED: vdt = VDT_SUPPLEMENTARY; vd_ver = fst_ver = 2; vdc = VDC_LOWERCASE; break; case VDD_PRIMARY: default: vdt = VDT_PRIMARY; vd_ver = fst_ver = 1; #ifdef COMPAT_MKISOFS vdc = VDC_LOWERCASE; #else vdc = VDC_STD; #endif break; } bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, vdt, vd_ver); /* Unused Field */ set_unused_field_bp(bp, 8, 8); /* System Identifier */ get_system_identitier(identifier, sizeof(identifier)); r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc); if (r != ARCHIVE_OK) return (r); /* Volume Identifier */ r = set_str_d_characters_bp(a, bp, 41, 72, iso9660->volume_identifier.s, vdc); if (r != ARCHIVE_OK) return (r); /* Unused Field */ set_unused_field_bp(bp, 73, 80); /* Volume Space Size */ set_num_733(bp+81, iso9660->volume_space_size); if (vdd->vdd_type == VDD_JOLIET) { /* Escape Sequences */ bp[89] = 0x25;/* UCS-2 Level 3 */ bp[90] = 0x2F; bp[91] = 0x45; memset(bp + 92, 0, 120 - 92 + 1); } else { /* Unused Field */ set_unused_field_bp(bp, 89, 120); } /* Volume Set Size */ set_num_723(bp+121, volume_set_size); /* Volume Sequence Number */ set_num_723(bp+125, iso9660->volume_sequence_number); /* Logical Block Size */ set_num_723(bp+129, LOGICAL_BLOCK_SIZE); /* Path Table Size */ set_num_733(bp+133, vdd->path_table_size); /* Location of Occurrence of Type L Path Table */ set_num_731(bp+141, vdd->location_type_L_path_table); /* Location of Optional Occurrence of Type L Path Table */ set_num_731(bp+145, 0); /* Location of Occurrence of Type M Path Table */ set_num_732(bp+149, vdd->location_type_M_path_table); /* Location of Optional Occurrence of Type M Path Table */ set_num_732(bp+153, 0); /* Directory Record for Root Directory(BP 157 to 190) */ set_directory_record(bp+157, 190-157+1, vdd->rootent, iso9660, DIR_REC_VD, vdd->vdd_type); /* Volume Set Identifier */ r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc); if (r != ARCHIVE_OK) return (r); /* Publisher Identifier */ r = set_file_identifier(bp, 319, 446, vdc, a, vdd, &(iso9660->publisher_identifier), "Publisher File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Data Preparer Identifier */ r = set_file_identifier(bp, 447, 574, vdc, a, vdd, &(iso9660->data_preparer_identifier), "Data Preparer File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Application Identifier */ r = set_file_identifier(bp, 575, 702, vdc, a, vdd, &(iso9660->application_identifier), "Application File", 1, A_CHAR); if (r != ARCHIVE_OK) return (r); /* Copyright File Identifier */ r = set_file_identifier(bp, 703, 739, vdc, a, vdd, &(iso9660->copyright_file_identifier), "Copyright File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Abstract File Identifier */ r = set_file_identifier(bp, 740, 776, vdc, a, vdd, &(iso9660->abstract_file_identifier), "Abstract File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Bibliographic File Identifier */ r = set_file_identifier(bp, 777, 813, vdc, a, vdd, &(iso9660->bibliographic_file_identifier), "Bibliongraphic File", 0, D_CHAR); if (r != ARCHIVE_OK) return (r); /* Volume Creation Date and Time */ set_date_time(bp+814, iso9660->birth_time); /* Volume Modification Date and Time */ set_date_time(bp+831, iso9660->birth_time); /* Volume Expiration Date and Time(obsolete) */ set_date_time_null(bp+848); /* Volume Effective Date and Time */ set_date_time(bp+865, iso9660->birth_time); /* File Structure Version */ bp[882] = fst_ver; /* Reserved */ bp[883] = 0; /* Application Use */ memset(bp + 884, 0x20, 1395 - 884 + 1); /* Reserved */ set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } /* * Write Boot Record Volume Descriptor */ static int write_VD_boot_record(struct archive_write *a) { struct iso9660 *iso9660; unsigned char *bp; iso9660 = a->format_data; bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, VDT_BOOT_RECORD, 1); /* Boot System Identifier */ memcpy(bp+8, "EL TORITO SPECIFICATION", 23); set_unused_field_bp(bp, 8+23, 39); /* Unused */ set_unused_field_bp(bp, 40, 71); /* Absolute pointer to first sector of Boot Catalog */ set_num_731(bp+72, iso9660->el_torito.catalog->file->content.location); /* Unused */ set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } enum keytype { KEY_FLG, KEY_STR, KEY_INT, KEY_HEX }; static void set_option_info(struct archive_string *info, int *opt, const char *key, enum keytype type, ...) { va_list ap; char prefix; const char *s; int d; prefix = (*opt==0)? ' ':','; va_start(ap, type); switch (type) { case KEY_FLG: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s%s", prefix, (d == 0)?"!":"", key); break; case KEY_STR: s = va_arg(ap, const char *); archive_string_sprintf(info, "%c%s=%s", prefix, key, s); break; case KEY_INT: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s=%d", prefix, key, d); break; case KEY_HEX: d = va_arg(ap, int); archive_string_sprintf(info, "%c%s=%x", prefix, key, d); break; } va_end(ap); *opt = 1; } /* * Make Non-ISO File System Information */ static int write_information_block(struct archive_write *a) { struct iso9660 *iso9660; char buf[128]; const char *v; int opt, r; struct archive_string info; size_t info_size = LOGICAL_BLOCK_SIZE * NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; iso9660 = (struct iso9660 *)a->format_data; if (info_size > wb_remaining(a)) { r = wb_write_out(a); if (r != ARCHIVE_OK) return (r); } archive_string_init(&info); if (archive_string_ensure(&info, info_size) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memset(info.s, 0, info_size); opt = 0; #if defined(HAVE__CTIME64_S) { __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp)); } #elif defined(HAVE_CTIME_R) ctime_r(&(iso9660->birth_time), buf); #else strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; #endif archive_string_sprintf(&info, "INFO %s%s", buf, archive_version_string()); if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT) set_option_info(&info, &opt, "abstract-file", KEY_STR, iso9660->abstract_file_identifier.s); if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT) set_option_info(&info, &opt, "application-id", KEY_STR, iso9660->application_identifier.s); if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT) set_option_info(&info, &opt, "allow-vernum", KEY_FLG, iso9660->opt.allow_vernum); if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT) set_option_info(&info, &opt, "biblio-file", KEY_STR, iso9660->bibliographic_file_identifier.s); if (iso9660->opt.boot != OPT_BOOT_DEFAULT) set_option_info(&info, &opt, "boot", KEY_STR, iso9660->el_torito.boot_filename.s); if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT) set_option_info(&info, &opt, "boot-catalog", KEY_STR, iso9660->el_torito.catalog_filename.s); if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT) set_option_info(&info, &opt, "boot-info-table", KEY_FLG, iso9660->opt.boot_info_table); if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT) set_option_info(&info, &opt, "boot-load-seg", KEY_HEX, iso9660->el_torito.boot_load_seg); if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT) set_option_info(&info, &opt, "boot-load-size", KEY_INT, iso9660->el_torito.boot_load_size); if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) { v = "no-emulation"; if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD) v = "fd"; if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK) v = "hard-disk"; set_option_info(&info, &opt, "boot-type", KEY_STR, v); } #ifdef HAVE_ZLIB_H if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT) set_option_info(&info, &opt, "compression-level", KEY_INT, iso9660->zisofs.compression_level); #endif if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT) set_option_info(&info, &opt, "copyright-file", KEY_STR, iso9660->copyright_file_identifier.s); if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT) set_option_info(&info, &opt, "iso-level", KEY_INT, iso9660->opt.iso_level); if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) { if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) set_option_info(&info, &opt, "joliet", KEY_STR, "long"); else set_option_info(&info, &opt, "joliet", KEY_FLG, iso9660->opt.joliet); } if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT) set_option_info(&info, &opt, "limit-depth", KEY_FLG, iso9660->opt.limit_depth); if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT) set_option_info(&info, &opt, "limit-dirs", KEY_FLG, iso9660->opt.limit_dirs); if (iso9660->opt.pad != OPT_PAD_DEFAULT) set_option_info(&info, &opt, "pad", KEY_FLG, iso9660->opt.pad); if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT) set_option_info(&info, &opt, "publisher", KEY_STR, iso9660->publisher_identifier.s); if (iso9660->opt.rr != OPT_RR_DEFAULT) { if (iso9660->opt.rr == OPT_RR_DISABLED) set_option_info(&info, &opt, "rockridge", KEY_FLG, iso9660->opt.rr); else if (iso9660->opt.rr == OPT_RR_STRICT) set_option_info(&info, &opt, "rockridge", KEY_STR, "strict"); else if (iso9660->opt.rr == OPT_RR_USEFUL) set_option_info(&info, &opt, "rockridge", KEY_STR, "useful"); } if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT) set_option_info(&info, &opt, "volume-id", KEY_STR, iso9660->volume_identifier.s); if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT) set_option_info(&info, &opt, "zisofs", KEY_FLG, iso9660->opt.zisofs); memcpy(wb_buffptr(a), info.s, info_size); archive_string_free(&info); return (wb_consume(a, info_size)); } static int write_rr_ER(struct archive_write *a) { unsigned char *p; p = wb_buffptr(a); memset(p, 0, LOGICAL_BLOCK_SIZE); p[0] = 'E'; p[1] = 'R'; p[3] = 0x01; p[2] = RRIP_ER_SIZE; p[4] = RRIP_ER_ID_SIZE; p[5] = RRIP_ER_DSC_SIZE; p[6] = RRIP_ER_SRC_SIZE; p[7] = 0x01; memcpy(&p[8], rrip_identifier, p[4]); memcpy(&p[8+p[4]], rrip_descriptor, p[5]); memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static void calculate_path_table_size(struct vdd *vdd) { int depth, size; struct path_table *pt; pt = vdd->pathtbl; size = 0; for (depth = 0; depth < vdd->max_depth; depth++) { struct isoent **ptbl; int i, cnt; if ((cnt = pt[depth].cnt) == 0) break; ptbl = pt[depth].sorted; for (i = 0; i < cnt; i++) { int len; if (ptbl[i]->identifier == NULL) len = 1; /* root directory */ else len = ptbl[i]->id_len; if (len & 0x01) len++; /* Padding Field */ size += 8 + len; } } vdd->path_table_size = size; vdd->path_table_block = ((size + PATH_TABLE_BLOCK_SIZE -1) / PATH_TABLE_BLOCK_SIZE) * (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE); } static int _write_path_table(struct archive_write *a, int type_m, int depth, struct vdd *vdd) { unsigned char *bp, *wb; struct isoent **ptbl; size_t wbremaining; int i, r, wsize; if (vdd->pathtbl[depth].cnt == 0) return (0); wsize = 0; wb = wb_buffptr(a); wbremaining = wb_remaining(a); bp = wb - 1; ptbl = vdd->pathtbl[depth].sorted; for (i = 0; i < vdd->pathtbl[depth].cnt; i++) { struct isoent *np; size_t len; np = ptbl[i]; if (np->identifier == NULL) len = 1; /* root directory */ else len = np->id_len; if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) { r = wb_consume(a, (bp+1) - wb); if (r < 0) return (r); wb = wb_buffptr(a); wbremaining = wb_remaining(a); bp = wb -1; } /* Length of Directory Identifier */ set_num_711(bp+1, (unsigned char)len); /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ if (type_m) set_num_732(bp+3, np->dir_location); else set_num_731(bp+3, np->dir_location); /* Parent Directory Number */ if (type_m) set_num_722(bp+7, np->parent->dir_number); else set_num_721(bp+7, np->parent->dir_number); /* Directory Identifier */ if (np->identifier == NULL) bp[9] = 0; else memcpy(&bp[9], np->identifier, len); if (len & 0x01) { /* Padding Field */ bp[9+len] = 0; len++; } wsize += 8 + (int)len; bp += 8 + len; } if ((bp + 1) > wb) { r = wb_consume(a, (bp+1)-wb); if (r < 0) return (r); } return (wsize); } static int write_path_table(struct archive_write *a, int type_m, struct vdd *vdd) { int depth, r; size_t path_table_size; r = ARCHIVE_OK; path_table_size = 0; for (depth = 0; depth < vdd->max_depth; depth++) { r = _write_path_table(a, type_m, depth, vdd); if (r < 0) return (r); path_table_size += r; } /* Write padding data. */ path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE; if (path_table_size > 0) r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size); return (r); } static int calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd, struct isoent *isoent, int depth) { struct isoent **enttbl; int bs, block, i; block = 1; bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type); bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type); if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) return (block); enttbl = isoent->children_sorted; for (i = 0; i < isoent->children.cnt; i++) { struct isoent *np = enttbl[i]; struct isofile *file; file = np->file; if (file->hardlink_target != NULL) file = file->hardlink_target; file->cur_content = &(file->content); do { int dr_l; dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL, vdd->vdd_type); if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) { block ++; bs = dr_l; } else bs += dr_l; file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } return (block); } static int _write_directory_descriptors(struct archive_write *a, struct vdd *vdd, struct isoent *isoent, int depth) { struct iso9660 *iso9660 = a->format_data; struct isoent **enttbl; unsigned char *p, *wb; int i, r; int dr_l; p = wb = wb_buffptr(a); #define WD_REMAINING (LOGICAL_BLOCK_SIZE - (p - wb)) p += set_directory_record(p, WD_REMAINING, isoent, iso9660, DIR_REC_SELF, vdd->vdd_type); p += set_directory_record(p, WD_REMAINING, isoent, iso9660, DIR_REC_PARENT, vdd->vdd_type); if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) { memset(p, 0, WD_REMAINING); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } enttbl = isoent->children_sorted; for (i = 0; i < isoent->children.cnt; i++) { struct isoent *np = enttbl[i]; struct isofile *file = np->file; if (file->hardlink_target != NULL) file = file->hardlink_target; file->cur_content = &(file->content); do { dr_l = set_directory_record(p, WD_REMAINING, np, iso9660, DIR_REC_NORMAL, vdd->vdd_type); if (dr_l == 0) { memset(p, 0, WD_REMAINING); r = wb_consume(a, LOGICAL_BLOCK_SIZE); if (r < 0) return (r); p = wb = wb_buffptr(a); dr_l = set_directory_record(p, WD_REMAINING, np, iso9660, DIR_REC_NORMAL, vdd->vdd_type); } p += dr_l; file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } memset(p, 0, WD_REMAINING); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int write_directory_descriptors(struct archive_write *a, struct vdd *vdd) { struct isoent *np; int depth, r; depth = 0; np = vdd->rootent; do { struct extr_rec *extr; r = _write_directory_descriptors(a, vdd, np, depth); if (r < 0) return (r); if (vdd->vdd_type != VDD_JOLIET) { /* * This extract record is used by SUSP,RRIP. * Not for joliet. */ for (extr = np->extr_rec_list.first; extr != NULL; extr = extr->next) { unsigned char *wb; wb = wb_buffptr(a); memcpy(wb, extr->buf, extr->offset); memset(wb + extr->offset, 0, LOGICAL_BLOCK_SIZE - extr->offset); r = wb_consume(a, LOGICAL_BLOCK_SIZE); if (r < 0) return (r); } } if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } /* * Read file contents from the temporary file, and write it. */ static int write_file_contents(struct archive_write *a, int64_t offset, int64_t size) { struct iso9660 *iso9660 = a->format_data; int r; lseek(iso9660->temp_fd, offset, SEEK_SET); while (size) { size_t rsize; ssize_t rs; unsigned char *wb; wb = wb_buffptr(a); rsize = wb_remaining(a); if (rsize > (size_t)size) rsize = (size_t)size; rs = read(iso9660->temp_fd, wb, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } size -= rs; r = wb_consume(a, rs); if (r < 0) return (r); } return (ARCHIVE_OK); } static int write_file_descriptors(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; int64_t blocks, offset; int r; blocks = 0; offset = 0; /* Make the boot catalog contents, and write it. */ if (iso9660->el_torito.catalog != NULL) { r = make_boot_catalog(a); if (r < 0) return (r); } /* Write the boot file contents. */ if (iso9660->el_torito.boot != NULL) { file = iso9660->el_torito.boot->file; blocks = file->content.blocks; offset = file->content.offset_of_temp; if (offset != 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); blocks = 0; offset = 0; } } /* Write out all file contents. */ for (file = iso9660->data_file_list.first; file != NULL; file = file->datanext) { if (!file->write_content) continue; if ((offset + (blocks << LOGICAL_BLOCK_BITS)) < file->content.offset_of_temp) { if (blocks > 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); } blocks = 0; offset = file->content.offset_of_temp; } file->cur_content = &(file->content); do { blocks += file->cur_content->blocks; /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } /* Flush out remaining blocks. */ if (blocks > 0) { r = write_file_contents(a, offset, blocks << LOGICAL_BLOCK_BITS); if (r < 0) return (r); } return (ARCHIVE_OK); } static void isofile_init_entry_list(struct iso9660 *iso9660) { iso9660->all_file_list.first = NULL; iso9660->all_file_list.last = &(iso9660->all_file_list.first); } static void isofile_add_entry(struct iso9660 *iso9660, struct isofile *file) { file->allnext = NULL; *iso9660->all_file_list.last = file; iso9660->all_file_list.last = &(file->allnext); } static void isofile_free_all_entries(struct iso9660 *iso9660) { struct isofile *file, *file_next; file = iso9660->all_file_list.first; while (file != NULL) { file_next = file->allnext; isofile_free(file); file = file_next; } } static void isofile_init_entry_data_file_list(struct iso9660 *iso9660) { iso9660->data_file_list.first = NULL; iso9660->data_file_list.last = &(iso9660->data_file_list.first); } static void isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file) { file->datanext = NULL; *iso9660->data_file_list.last = file; iso9660->data_file_list.last = &(file->datanext); } static struct isofile * isofile_new(struct archive_write *a, struct archive_entry *entry) { struct isofile *file; file = calloc(1, sizeof(*file)); if (file == NULL) return (NULL); if (entry != NULL) file->entry = archive_entry_clone(entry); else file->entry = archive_entry_new2(&a->archive); if (file->entry == NULL) { free(file); return (NULL); } archive_string_init(&(file->parentdir)); archive_string_init(&(file->basename)); archive_string_init(&(file->basename_utf16)); archive_string_init(&(file->symlink)); file->cur_content = &(file->content); return (file); } static void isofile_free(struct isofile *file) { struct content *con, *tmp; con = file->content.next; while (con != NULL) { tmp = con; con = con->next; free(tmp); } archive_entry_free(file->entry); archive_string_free(&(file->parentdir)); archive_string_free(&(file->basename)); archive_string_free(&(file->basename_utf16)); archive_string_free(&(file->symlink)); free(file); } #if defined(_WIN32) || defined(__CYGWIN__) static int cleanup_backslash_1(char *p) { int mb, dos; mb = dos = 0; while (*p) { if (*(unsigned char *)p > 127) mb = 1; if (*p == '\\') { /* If we have not met any multi-byte characters, * we can replace '\' with '/'. */ if (!mb) *p = '/'; dos = 1; } p++; } if (!mb || !dos) return (0); return (-1); } static void cleanup_backslash_2(wchar_t *p) { /* Convert a path-separator from '\' to '/' */ while (*p != L'\0') { if (*p == L'\\') *p = L'/'; p++; } } #endif /* * Generate a parent directory name and a base name from a pathname. */ static int isofile_gen_utility_names(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660; const char *pathname; char *p, *dirname, *slash; size_t len; int ret = ARCHIVE_OK; iso9660 = a->format_data; archive_string_empty(&(file->parentdir)); archive_string_empty(&(file->basename)); archive_string_empty(&(file->basename_utf16)); archive_string_empty(&(file->symlink)); pathname = archive_entry_pathname(file->entry); if (pathname == NULL || pathname[0] == '\0') {/* virtual root */ file->dircnt = 0; return (ret); } /* * Make a UTF-16BE basename if Joliet extension enabled. */ if (iso9660->opt.joliet) { const char *u16, *ulast; size_t u16len, ulen_last; if (iso9660->sconv_to_utf16be == NULL) { iso9660->sconv_to_utf16be = archive_string_conversion_to_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_to_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); iso9660->sconv_from_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_from_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } /* * Convert a filename to UTF-16BE. */ if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len, iso9660->sconv_to_utf16be)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A filename cannot be converted to UTF-16BE;" "You should disable making Joliet extension"); ret = ARCHIVE_WARN; } /* * Make sure a path separator is not in the last; * Remove trailing '/'. */ while (u16len >= 2) { #if defined(_WIN32) || defined(__CYGWIN__) if (u16[u16len-2] == 0 && (u16[u16len-1] == '/' || u16[u16len-1] == '\\')) #else if (u16[u16len-2] == 0 && u16[u16len-1] == '/') #endif { u16len -= 2; } else break; } /* * Find a basename in UTF-16BE. */ ulast = u16; u16len >>= 1; ulen_last = u16len; while (u16len > 0) { #if defined(_WIN32) || defined(__CYGWIN__) if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\')) #else if (u16[0] == 0 && u16[1] == '/') #endif { ulast = u16 + 2; ulen_last = u16len -1; } u16 += 2; u16len --; } ulen_last <<= 1; if (archive_string_ensure(&(file->basename_utf16), ulen_last) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for UTF-16BE"); return (ARCHIVE_FATAL); } /* * Set UTF-16BE basename. */ memcpy(file->basename_utf16.s, ulast, ulen_last); file->basename_utf16.length = ulen_last; } archive_strcpy(&(file->parentdir), pathname); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (cleanup_backslash_1(file->parentdir.s) != 0) { const wchar_t *wp = archive_entry_pathname_w(file->entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->parentdir)); r = archive_string_append_from_wcs(&(file->parentdir), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #endif len = file->parentdir.length; p = dirname = file->parentdir.s; /* * Remove leading '/', '../' and './' elements */ while (*p) { if (p[0] == '/') { p++; len--; } else if (p[0] != '.') break; else if (p[1] == '.' && p[2] == '/') { p += 3; len -= 3; } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) { p += 2; len -= 2; } else if (p[1] == '\0') { p++; len--; } else break; } if (p != dirname) { memmove(dirname, p, len+1); p = dirname; } /* * Remove "/","/." and "/.." elements from tail. */ while (len > 0) { size_t ll = len; if (len > 0 && p[len-1] == '/') { p[len-1] = '\0'; len--; } if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { p[len-2] = '\0'; len -= 2; } if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && p[len-1] == '.') { p[len-3] = '\0'; len -= 3; } if (ll == len) break; } while (*p) { if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ memmove(p, p+1, strlen(p+1) + 1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ memmove(p, p+2, strlen(p+2) + 1); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' */ char *rp = p -1; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { strcpy(rp, p+3); p = rp; } else { strcpy(dirname, p+4); p = dirname; } } else p++; } else p++; } p = dirname; len = strlen(p); if (archive_entry_filetype(file->entry) == AE_IFLNK) { /* Convert symlink name too. */ pathname = archive_entry_symlink(file->entry); archive_strcpy(&(file->symlink), pathname); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (archive_strlen(&(file->symlink)) > 0 && cleanup_backslash_1(file->symlink.s) != 0) { const wchar_t *wp = archive_entry_symlink_w(file->entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->symlink)); r = archive_string_append_from_wcs( &(file->symlink), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #endif } /* * - Count up directory elements. * - Find out the position which points the last position of * path separator('/'). */ slash = NULL; file->dircnt = 0; for (; *p != '\0'; p++) if (*p == '/') { slash = p; file->dircnt++; } if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; archive_string_copy(&(file->basename), &(file->parentdir)); archive_string_empty(&(file->parentdir)); *file->parentdir.s = '\0'; return (ret); } /* Make a basename from dirname and slash */ *slash = '\0'; file->parentdir.length = slash - dirname; archive_strcpy(&(file->basename), slash + 1); if (archive_entry_filetype(file->entry) == AE_IFDIR) file->dircnt ++; return (ret); } /* * Register a entry to get a hardlink target. */ static int isofile_register_hardlink(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660 = a->format_data; struct hardlink *hl; const char *pathname; archive_entry_set_nlink(file->entry, 1); pathname = archive_entry_hardlink(file->entry); if (pathname == NULL) { /* This `file` is a hardlink target. */ hl = malloc(sizeof(*hl)); if (hl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } hl->nlink = 1; /* A hardlink target must be the first position. */ file->hlnext = NULL; hl->file_list.first = file; hl->file_list.last = &(file->hlnext); __archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree), (struct archive_rb_node *)hl); } else { hl = (struct hardlink *)__archive_rb_tree_find_node( &(iso9660->hardlink_rbtree), pathname); if (hl != NULL) { /* Insert `file` entry into the tail. */ file->hlnext = NULL; *hl->file_list.last = file; hl->file_list.last = &(file->hlnext); hl->nlink++; } archive_entry_unset_size(file->entry); } return (ARCHIVE_OK); } /* * Hardlinked files have to have the same location of extent. * We have to find out hardlink target entries for the entries * which have a hardlink target name. */ static void isofile_connect_hardlink_files(struct iso9660 *iso9660) { struct archive_rb_node *n; struct hardlink *hl; struct isofile *target, *nf; ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) { hl = (struct hardlink *)n; /* The first entry must be a hardlink target. */ target = hl->file_list.first; archive_entry_set_nlink(target->entry, hl->nlink); /* Set a hardlink target to reference entries. */ for (nf = target->hlnext; nf != NULL; nf = nf->hlnext) { nf->hardlink_target = target; archive_entry_set_nlink(nf->entry, hl->nlink); } } } static int isofile_hd_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct hardlink *h1 = (const struct hardlink *)n1; const struct hardlink *h2 = (const struct hardlink *)n2; return (strcmp(archive_entry_pathname(h1->file_list.first->entry), archive_entry_pathname(h2->file_list.first->entry))); } static int isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key) { const struct hardlink *h = (const struct hardlink *)n; return (strcmp(archive_entry_pathname(h->file_list.first->entry), (const char *)key)); } static void isofile_init_hardlinks(struct iso9660 *iso9660) { static const struct archive_rb_tree_ops rb_ops = { isofile_hd_cmp_node, isofile_hd_cmp_key, }; __archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops); } static void isofile_free_hardlinks(struct iso9660 *iso9660) { struct archive_rb_node *n, *tmp; ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(iso9660->hardlink_rbtree), tmp) { __archive_rb_tree_remove_node(&(iso9660->hardlink_rbtree), n); free(n); } } static struct isoent * isoent_new(struct isofile *file) { struct isoent *isoent; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node, isoent_cmp_key, }; isoent = calloc(1, sizeof(*isoent)); if (isoent == NULL) return (NULL); isoent->file = file; isoent->children.first = NULL; isoent->children.last = &(isoent->children.first); __archive_rb_tree_init(&(isoent->rbtree), &rb_ops); isoent->subdirs.first = NULL; isoent->subdirs.last = &(isoent->subdirs.first); isoent->extr_rec_list.first = NULL; isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); isoent->extr_rec_list.current = NULL; if (archive_entry_filetype(file->entry) == AE_IFDIR) isoent->dir = 1; return (isoent); } static inline struct isoent * isoent_clone(struct isoent *src) { return (isoent_new(src->file)); } static void _isoent_free(struct isoent *isoent) { struct extr_rec *er, *er_next; free(isoent->children_sorted); free(isoent->identifier); er = isoent->extr_rec_list.first; while (er != NULL) { er_next = er->next; free(er); er = er_next; } free(isoent); } static void isoent_free_all(struct isoent *isoent) { struct isoent *np, *np_temp; if (isoent == NULL) return; np = isoent; for (;;) { if (np->dir) { if (np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; continue; } } for (;;) { np_temp = np; if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; _isoent_free(np_temp); if (np == np_temp) return; } else { np = np->chnext; _isoent_free(np_temp); break; } } } } static struct isoent * isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname) { struct isofile *file; struct isoent *isoent; file = isofile_new(a, NULL); if (file == NULL) return (NULL); archive_entry_set_pathname(file->entry, pathname); archive_entry_unset_mtime(file->entry); archive_entry_unset_atime(file->entry); archive_entry_unset_ctime(file->entry); archive_entry_set_uid(file->entry, getuid()); archive_entry_set_gid(file->entry, getgid()); archive_entry_set_mode(file->entry, 0555 | AE_IFDIR); archive_entry_set_nlink(file->entry, 2); if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { isofile_free(file); return (NULL); } isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) return (NULL); isoent->dir = 1; isoent->virtual = 1; return (isoent); } static int isoent_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct isoent *e1 = (const struct isoent *)n1; const struct isoent *e2 = (const struct isoent *)n2; return (strcmp(e1->file->basename.s, e2->file->basename.s)); } static int isoent_cmp_key(const struct archive_rb_node *n, const void *key) { const struct isoent *e = (const struct isoent *)n; return (strcmp(e->file->basename.s, (const char *)key)); } static int isoent_add_child_head(struct isoent *parent, struct isoent *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); if ((child->chnext = parent->children.first) == NULL) parent->children.last = &(child->chnext); parent->children.first = child; parent->children.cnt++; child->parent = parent; /* Add a child to a sub-directory chain */ if (child->dir) { if ((child->drnext = parent->subdirs.first) == NULL) parent->subdirs.last = &(child->drnext); parent->subdirs.first = child; parent->subdirs.cnt++; child->parent = parent; } else child->drnext = NULL; return (1); } static int isoent_add_child_tail(struct isoent *parent, struct isoent *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); child->chnext = NULL; *parent->children.last = child; parent->children.last = &(child->chnext); parent->children.cnt++; child->parent = parent; /* Add a child to a sub-directory chain */ child->drnext = NULL; if (child->dir) { *parent->subdirs.last = child; parent->subdirs.last = &(child->drnext); parent->subdirs.cnt++; child->parent = parent; } return (1); } static void isoent_remove_child(struct isoent *parent, struct isoent *child) { struct isoent *ent; /* Remove a child entry from children chain. */ ent = parent->children.first; while (ent->chnext != child) ent = ent->chnext; if ((ent->chnext = ent->chnext->chnext) == NULL) parent->children.last = &(ent->chnext); parent->children.cnt--; if (child->dir) { /* Remove a child entry from sub-directory chain. */ ent = parent->subdirs.first; while (ent->drnext != child) ent = ent->drnext; if ((ent->drnext = ent->drnext->drnext) == NULL) parent->subdirs.last = &(ent->drnext); parent->subdirs.cnt--; } __archive_rb_tree_remove_node(&(parent->rbtree), (struct archive_rb_node *)child); } static int isoent_clone_tree(struct archive_write *a, struct isoent **nroot, struct isoent *root) { struct isoent *np, *xroot, *newent; np = root; xroot = NULL; do { newent = isoent_clone(np); if (newent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } if (xroot == NULL) { *nroot = xroot = newent; newent->parent = xroot; } else isoent_add_child_tail(xroot, newent); if (np->dir && np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; xroot = newent; continue; } while (np != np->parent) { if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; xroot = xroot->parent; } else { np = np->chnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } /* * Setup directory locations. */ static void isoent_setup_directory_location(struct iso9660 *iso9660, int location, struct vdd *vdd) { struct isoent *np; int depth; vdd->total_dir_block = 0; depth = 0; np = vdd->rootent; do { int block; np->dir_block = calculate_directory_descriptors( iso9660, vdd, np, depth); vdd->total_dir_block += np->dir_block; np->dir_location = location; location += np->dir_block; block = extra_setup_location(np, location); vdd->total_dir_block += block; location += block; if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); } static void _isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent, int *symlocation) { struct isoent **children; int n; if (isoent->children.cnt == 0) return; children = isoent->children_sorted; for (n = 0; n < isoent->children.cnt; n++) { struct isoent *np; struct isofile *file; np = children[n]; if (np->dir) continue; if (np == iso9660->el_torito.boot) continue; file = np->file; if (file->boot || file->hardlink_target != NULL) continue; if (archive_entry_filetype(file->entry) == AE_IFLNK || file->content.size == 0) { /* * Do not point a valid location. * Make sure entry is not hardlink file. */ file->content.location = (*symlocation)--; continue; } file->write_content = 1; } } /* * Setup file locations. */ static void isoent_setup_file_location(struct iso9660 *iso9660, int location) { struct isoent *isoent; struct isoent *np; struct isofile *file; size_t size; int block; int depth; int joliet; int symlocation; int total_block; iso9660->total_file_block = 0; if ((isoent = iso9660->el_torito.catalog) != NULL) { isoent->file->content.location = location; block = (int)((archive_entry_size(isoent->file->entry) + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); location += block; iso9660->total_file_block += block; } if ((isoent = iso9660->el_torito.boot) != NULL) { isoent->file->content.location = location; size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = (size_t)archive_entry_size(isoent->file->entry); block = ((int)size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS; location += block; iso9660->total_file_block += block; isoent->file->content.blocks = block; } depth = 0; symlocation = -16; if (!iso9660->opt.rr && iso9660->opt.joliet) { joliet = 1; np = iso9660->joliet.rootent; } else { joliet = 0; np = iso9660->primary.rootent; } do { _isoent_file_location(iso9660, np, &symlocation); if (np->subdirs.first != NULL && (joliet || ((iso9660->opt.rr == OPT_RR_DISABLED && depth + 2 < iso9660->primary.max_depth) || (iso9660->opt.rr && depth + 1 < iso9660->primary.max_depth)))) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); total_block = 0; for (file = iso9660->data_file_list.first; file != NULL; file = file->datanext) { if (!file->write_content) continue; file->cur_content = &(file->content); do { file->cur_content->location = location; location += file->cur_content->blocks; total_block += file->cur_content->blocks; /* Next fragment */ file->cur_content = file->cur_content->next; } while (file->cur_content != NULL); } iso9660->total_file_block += total_block; } static int get_path_component(char *name, size_t n, const char *fn) { char *p; size_t l; p = strchr(fn, '/'); if (p == NULL) { if ((l = strlen(fn)) == 0) return (0); } else l = p - fn; if (l > n -1) return (-1); memcpy(name, fn, l); name[l] = '\0'; return ((int)l); } /* * Add a new entry into the tree. */ static int isoent_tree(struct archive_write *a, struct isoent **isoentpp) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct iso9660 *iso9660 = a->format_data; struct isoent *dent, *isoent, *np; struct isofile *f1, *f2; const char *fn, *p; int l; isoent = *isoentpp; dent = iso9660->primary.rootent; if (isoent->file->parentdir.length > 0) fn = p = isoent->file->parentdir.s; else fn = p = ""; /* * If the path of the parent directory of `isoent' entry is * the same as the path of `cur_dirent', add isoent to * `cur_dirent'. */ if (archive_strlen(&(iso9660->cur_dirstr)) == archive_strlen(&(isoent->file->parentdir)) && strcmp(iso9660->cur_dirstr.s, fn) == 0) { if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) { np = (struct isoent *)__archive_rb_tree_find_node( &(iso9660->cur_dirent->rbtree), isoent->file->basename.s); goto same_entry; } return (ARCHIVE_OK); } for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) { np = NULL; break; } if (l < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); _isoent_free(isoent); return (ARCHIVE_FATAL); } np = isoent_find_child(dent, name); if (np == NULL || fn[0] == '\0') break; /* Find next subdirectory. */ if (!np->dir) { /* NOT Directory! */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "`%s' is not directory, we cannot insert `%s' ", archive_entry_pathname(np->file->entry), archive_entry_pathname(isoent->file->entry)); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FAILED); } fn += l; if (fn[0] == '/') fn++; dent = np; } if (np == NULL) { /* * Create virtual parent directories. */ while (fn[0] != '\0') { struct isoent *vp; struct archive_string as; archive_string_init(&as); archive_strncat(&as, p, fn - p + l); if (as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } vp = isoent_create_virtual_dir(a, iso9660, as.s); if (vp == NULL) { archive_string_free(&as); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FATAL); } archive_string_free(&as); if (vp->file->dircnt > iso9660->dircnt_max) iso9660->dircnt_max = vp->file->dircnt; isoent_add_child_tail(dent, vp); np = vp; fn += l; if (fn[0] == '/') fn++; l = get_path_component(name, sizeof(name), fn); if (l < 0) { archive_string_free(&as); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FATAL); } dent = np; } /* Found out the parent directory where isoent can be * inserted. */ iso9660->cur_dirent = dent; archive_string_empty(&(iso9660->cur_dirstr)); archive_string_ensure(&(iso9660->cur_dirstr), archive_strlen(&(dent->file->parentdir)) + archive_strlen(&(dent->file->basename)) + 2); if (archive_strlen(&(dent->file->parentdir)) + archive_strlen(&(dent->file->basename)) == 0) iso9660->cur_dirstr.s[0] = 0; else { if (archive_strlen(&(dent->file->parentdir)) > 0) { archive_string_copy(&(iso9660->cur_dirstr), &(dent->file->parentdir)); archive_strappend_char(&(iso9660->cur_dirstr), '/'); } archive_string_concat(&(iso9660->cur_dirstr), &(dent->file->basename)); } if (!isoent_add_child_tail(dent, isoent)) { np = (struct isoent *)__archive_rb_tree_find_node( &(dent->rbtree), isoent->file->basename.s); goto same_entry; } return (ARCHIVE_OK); } same_entry: /* * We have already has the entry the filename of which is * the same. */ f1 = np->file; f2 = isoent->file; /* If the file type of entries is different, * we cannot handle it. */ if (archive_entry_filetype(f1->entry) != archive_entry_filetype(f2->entry)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found duplicate entries `%s' and its file type is " "different", archive_entry_pathname(f1->entry)); _isoent_free(isoent); *isoentpp = NULL; return (ARCHIVE_FAILED); } /* Swap file entries. */ np->file = f2; isoent->file = f1; np->virtual = 0; _isoent_free(isoent); *isoentpp = np; return (ARCHIVE_OK); } /* * Find a entry from `isoent' */ static struct isoent * isoent_find_child(struct isoent *isoent, const char *child_name) { struct isoent *np; np = (struct isoent *)__archive_rb_tree_find_node( &(isoent->rbtree), child_name); return (np); } /* * Find a entry full-path of which is specified by `fn' parameter, * in the tree. */ static struct isoent * isoent_find_entry(struct isoent *rootent, const char *fn) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct isoent *isoent, *np; int l; isoent = rootent; np = NULL; for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) break; fn += l; if (fn[0] == '/') fn++; np = isoent_find_child(isoent, name); if (np == NULL) break; if (fn[0] == '\0') break;/* We found out the entry */ /* Try sub directory. */ isoent = np; np = NULL; if (!isoent->dir) break;/* Not directory */ } return (np); } /* * Following idr_* functions are used for resolving duplicated filenames * and unreceivable filenames to generate ISO9660/Joliet Identifiers. */ static void idr_relaxed_filenames(char *map) { int i; for (i = 0x21; i <= 0x2F; i++) map[i] = 1; for (i = 0x3A; i <= 0x41; i++) map[i] = 1; for (i = 0x5B; i <= 0x5E; i++) map[i] = 1; map[0x60] = 1; for (i = 0x7B; i <= 0x7E; i++) map[i] = 1; } static void idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr) { idr->idrent_pool = NULL; idr->pool_size = 0; if (vdd->vdd_type != VDD_JOLIET) { if (iso9660->opt.iso_level <= 3) { memcpy(idr->char_map, d_characters_map, sizeof(idr->char_map)); } else { memcpy(idr->char_map, d1_characters_map, sizeof(idr->char_map)); idr_relaxed_filenames(idr->char_map); } } } static void idr_cleanup(struct idr *idr) { free(idr->idrent_pool); } static int idr_ensure_poolsize(struct archive_write *a, struct idr *idr, int cnt) { if (idr->pool_size < cnt) { void *p; const int bk = (1 << 7) - 1; int psize; psize = (cnt + bk) & ~bk; p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } idr->idrent_pool = (struct idrent *)p; idr->pool_size = psize; } return (ARCHIVE_OK); } static int idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax, int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops) { int r; (void)ffmax; /* UNUSED */ r = idr_ensure_poolsize(a, idr, cnt); if (r != ARCHIVE_OK) return (r); __archive_rb_tree_init(&(idr->rbtree), rbt_ops); idr->wait_list.first = NULL; idr->wait_list.last = &(idr->wait_list.first); idr->pool_idx = 0; idr->num_size = num_size; idr->null_size = null_size; return (ARCHIVE_OK); } static void idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff) { struct idrent *idrent, *n; idrent = &(idr->idrent_pool[idr->pool_idx++]); idrent->wnext = idrent->avail = NULL; idrent->isoent = isoent; idrent->weight = weight; idrent->noff = noff; idrent->rename_num = 0; if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) { n = (struct idrent *)__archive_rb_tree_find_node( &(idr->rbtree), idrent->isoent); if (n != NULL) { /* this `idrent' needs to rename. */ idrent->avail = n; *idr->wait_list.last = idrent; idr->wait_list.last = &(idrent->wnext); } } } static void idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize) { unsigned char *p; int wnp_ext_off; wnp_ext_off = wnp->isoent->ext_off; if (wnp->noff + numsize != wnp_ext_off) { p = (unsigned char *)wnp->isoent->identifier; /* Extend the filename; foo.c --> foo___.c */ memmove(p + wnp->noff + numsize, p + wnp_ext_off, wnp->isoent->ext_len + nullsize); wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize; wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len; } } static void idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) { struct idrent *n; unsigned char *p; for (n = idr->wait_list.first; n != NULL; n = n->wnext) { idr_extend_identifier(n, idr->num_size, idr->null_size); p = (unsigned char *)n->isoent->identifier + n->noff; do { fsetnum(p, n->avail->rename_num++); } while (!__archive_rb_tree_insert_node( &(idr->rbtree), &(n->rbnode))); } } static void idr_set_num(unsigned char *p, int num) { static const char xdig[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig); p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))]; num %= sizeof(xdig) * sizeof(xdig); p[1] = xdig[ (num / sizeof(xdig))]; num %= sizeof(xdig); p[2] = xdig[num]; } static void idr_set_num_beutf16(unsigned char *p, int num) { static const uint16_t xdig[] = { 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A }; #define XDIG_CNT (sizeof(xdig)/sizeof(xdig[0])) num %= XDIG_CNT * XDIG_CNT * XDIG_CNT; archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]); num %= XDIG_CNT * XDIG_CNT; archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]); num %= XDIG_CNT; archive_be16enc(p+4, xdig[num]); } /* * Generate ISO9660 Identifier. */ static int isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct iso9660 *iso9660; struct isoent *np; char *p; int l, r; const char *char_map; char allow_ldots, allow_multidot, allow_period, allow_vernum; int fnmax, ffmax, dnmax; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_iso9660, isoent_cmp_key_iso9660 }; if (isoent->children.cnt == 0) return (0); iso9660 = a->format_data; char_map = idr->char_map; if (iso9660->opt.iso_level <= 3) { allow_ldots = 0; allow_multidot = 0; allow_period = 1; allow_vernum = iso9660->opt.allow_vernum; if (iso9660->opt.iso_level == 1) { fnmax = 8; ffmax = 12;/* fnmax + '.' + 3 */ dnmax = 8; } else { fnmax = 30; ffmax = 31; dnmax = 31; } } else { allow_ldots = allow_multidot = 1; allow_period = allow_vernum = 0; if (iso9660->opt.rr) /* * MDR : The maximum size of Directory Record(254). * DRL : A Directory Record Length(33). * CE : A size of SUSP CE System Use Entry(28). * MDR - DRL - CE = 254 - 33 - 28 = 193. */ fnmax = ffmax = dnmax = 193; else /* * XA : CD-ROM XA System Use Extension * Information(14). * MDR - DRL - XA = 254 - 33 -14 = 207. */ fnmax = ffmax = dnmax = 207; } r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops); if (r < 0) return (r); for (np = isoent->children.first; np != NULL; np = np->chnext) { char *dot, *xdot; int ext_off, noff, weight; l = (int)np->file->basename.length; p = malloc(l+31+2+1); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(p, np->file->basename.s, l); p[l] = '\0'; np->identifier = p; dot = xdot = NULL; if (!allow_ldots) { /* * If there is a '.' character at the first byte, * it has to be replaced by '_' character. */ if (*p == '.') *p++ = '_'; } for (;*p; p++) { if (*p & 0x80) { *p = '_'; continue; } if (char_map[(unsigned char)*p]) { /* if iso-level is '4', a character '.' is * allowed by char_map. */ if (*p == '.') { xdot = dot; dot = p; } continue; } if (*p >= 'a' && *p <= 'z') { *p -= 'a' - 'A'; continue; } if (*p == '.') { xdot = dot; dot = p; if (allow_multidot) continue; } *p = '_'; } p = np->identifier; weight = -1; if (dot == NULL) { int nammax; if (np->dir) nammax = dnmax; else nammax = fnmax; if (l > nammax) { p[nammax] = '\0'; weight = nammax; ext_off = nammax; } else ext_off = l; } else { *dot = '.'; ext_off = (int)(dot - p); if (iso9660->opt.iso_level == 1) { if (dot - p <= 8) { if (strlen(dot) > 4) { /* A length of a file extension * must be less than 4 */ dot[4] = '\0'; weight = 0; } } else { p[8] = dot[0]; p[9] = dot[1]; p[10] = dot[2]; p[11] = dot[3]; p[12] = '\0'; weight = 8; ext_off = 8; } } else if (np->dir) { if (l > dnmax) { p[dnmax] = '\0'; weight = dnmax; if (ext_off > dnmax) ext_off = dnmax; } } else if (l > ffmax) { int extlen = (int)strlen(dot); int xdoff; if (xdot != NULL) xdoff = (int)(xdot - p); else xdoff = 0; if (extlen > 1 && xdoff < fnmax-1) { int off; if (extlen > ffmax) extlen = ffmax; off = ffmax - extlen; if (off == 0) { /* A dot('.') character * doesn't place to the first * byte of identifier. */ off ++; extlen --; } memmove(p+off, dot, extlen); p[ffmax] = '\0'; ext_off = off; weight = off; #ifdef COMPAT_MKISOFS } else if (xdoff >= fnmax-1) { /* Simulate a bug(?) of mkisofs. */ p[fnmax-1] = '\0'; ext_off = fnmax-1; weight = fnmax-1; #endif } else { p[fnmax] = '\0'; ext_off = fnmax; weight = fnmax; } } } /* Save an offset of a file name extension to sort files. */ np->ext_off = ext_off; np->ext_len = (int)strlen(&p[ext_off]); np->id_len = l = ext_off + np->ext_len; /* Make an offset of the number which is used to be set * hexadecimal number to avoid duplicate identifier. */ if (iso9660->opt.iso_level == 1) { if (ext_off >= 5) noff = 5; else noff = ext_off; } else { if (l == ffmax) noff = ext_off - 3; else if (l == ffmax-1) noff = ext_off - 2; else if (l == ffmax-2) noff = ext_off - 1; else noff = ext_off; } /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier. */ idr_resolve(idr, idr_set_num); /* Add a period and a version number to identifiers. */ for (np = isoent->children.first; np != NULL; np = np->chnext) { if (!np->dir && np->rr_child == NULL) { p = np->identifier + np->ext_off + np->ext_len; if (np->ext_len == 0 && allow_period) { *p++ = '.'; np->ext_len = 1; } if (np->ext_len == 1 && !allow_period) { *--p = '\0'; np->ext_len = 0; } np->id_len = np->ext_off + np->ext_len; if (allow_vernum) { *p++ = ';'; *p++ = '1'; np->id_len += 2; } *p = '\0'; } else np->id_len = np->ext_off + np->ext_len; np->mb_len = np->id_len; } return (ARCHIVE_OK); } /* * Generate Joliet Identifier. */ static int isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct iso9660 *iso9660; struct isoent *np; unsigned char *p; size_t l; int r; size_t ffmax, parent_len; static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_joliet, isoent_cmp_key_joliet }; if (isoent->children.cnt == 0) return (0); iso9660 = a->format_data; if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) ffmax = 206; else ffmax = 128; r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); if (r < 0) return (r); parent_len = 1; for (np = isoent; np->parent != np; np = np->parent) parent_len += np->mb_len + 1; for (np = isoent->children.first; np != NULL; np = np->chnext) { unsigned char *dot; int ext_off, noff, weight; size_t lt; if ((l = np->file->basename_utf16.length) > ffmax) l = ffmax; p = malloc((l+1)*2); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(p, np->file->basename_utf16.s, l); p[l] = 0; p[l+1] = 0; np->identifier = (char *)p; lt = l; dot = p + l; weight = 0; while (lt > 0) { if (!joliet_allowed_char(p[0], p[1])) archive_be16enc(p, 0x005F); /* '_' */ else if (p[0] == 0 && p[1] == 0x2E) /* '.' */ dot = p; p += 2; lt -= 2; } ext_off = (int)(dot - (unsigned char *)np->identifier); np->ext_off = ext_off; np->ext_len = (int)l - ext_off; np->id_len = (int)l; /* * Get a length of MBS of a full-pathname. */ if (np->file->basename_utf16.length > ffmax) { if (archive_strncpy_l(&iso9660->mbs, (const char *)np->identifier, l, iso9660->sconv_from_utf16be) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, errno, "No memory"); return (ARCHIVE_FATAL); } np->mb_len = (int)iso9660->mbs.length; if (np->mb_len != (int)np->file->basename.length) weight = np->mb_len; } else np->mb_len = (int)np->file->basename.length; /* If a length of full-pathname is longer than 240 bytes, * it violates Joliet extensions regulation. */ if (parent_len > 240 || np->mb_len > 240 || parent_len + np->mb_len > 240) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "The regulation of Joliet extensions;" " A length of a full-pathname of `%s' is " "longer than 240 bytes, (p=%d, b=%d)", archive_entry_pathname(np->file->entry), (int)parent_len, (int)np->mb_len); return (ARCHIVE_FATAL); } /* Make an offset of the number which is used to be set * hexadecimal number to avoid duplicate identifier. */ if (l == ffmax) noff = ext_off - 6; else if (l == ffmax-2) noff = ext_off - 4; else if (l == ffmax-4) noff = ext_off - 2; else noff = ext_off; /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier with Joliet Volume. */ idr_resolve(idr, idr_set_num_beutf16); return (ARCHIVE_OK); } /* * This comparing rule is according to ISO9660 Standard 9.3 */ static int isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2) { const char *s1, *s2; int cmp; int l; s1 = p1->identifier; s2 = p2->identifier; /* Compare File Name */ l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } /* Compare File Name Extension */ if (p1->ext_len == 0 && p2->ext_len == 0) return (0); if (p1->ext_len == 1 && p2->ext_len == 1) return (0); if (p1->ext_len <= 1) return (-1); if (p2->ext_len <= 1) return (1); l = p1->ext_len; if (l > p2->ext_len) l = p2->ext_len; s1 = p1->identifier + p1->ext_off; s2 = p2->identifier + p2->ext_off; if (l > 1) { cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); } if (p1->ext_len < p2->ext_len) { s2 += l; l = p2->ext_len - p1->ext_len; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_len > p2->ext_len) { s1 += l; l = p1->ext_len - p2->ext_len; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } /* Compare File Version Number */ /* No operation. The File Version Number is always one. */ return (cmp); } static int isoent_cmp_node_iso9660(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct idrent *e1 = (const struct idrent *)n1; const struct idrent *e2 = (const struct idrent *)n2; return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent)); } static int isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key) { const struct isoent *isoent = (const struct isoent *)key; const struct idrent *idrent = (const struct idrent *)node; return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent)); } static int isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2) { const unsigned char *s1, *s2; int cmp; int l; s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; /* Compare File Name */ l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } /* Compare File Name Extension */ if (p1->ext_len == 0 && p2->ext_len == 0) return (0); if (p1->ext_len == 2 && p2->ext_len == 2) return (0); if (p1->ext_len <= 2) return (-1); if (p2->ext_len <= 2) return (1); l = p1->ext_len; if (l > p2->ext_len) l = p2->ext_len; s1 = (unsigned char *)(p1->identifier + p1->ext_off); s2 = (unsigned char *)(p2->identifier + p2->ext_off); if (l > 1) { cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); } if (p1->ext_len < p2->ext_len) { s2 += l; l = p2->ext_len - p1->ext_len; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_len > p2->ext_len) { s1 += l; l = p1->ext_len - p2->ext_len; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } /* Compare File Version Number */ /* No operation. The File Version Number is always one. */ return (cmp); } static int isoent_cmp_node_joliet(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct idrent *e1 = (const struct idrent *)n1; const struct idrent *e2 = (const struct idrent *)n2; return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent)); } static int isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key) { const struct isoent *isoent = (const struct isoent *)key; const struct idrent *idrent = (const struct idrent *)node; return (isoent_cmp_joliet_identifier(isoent, idrent->isoent)); } static int isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent, struct idr *idr) { struct archive_rb_node *rn; struct isoent **children; children = malloc(isoent->children.cnt * sizeof(struct isoent *)); if (children == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } isoent->children_sorted = children; ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) { struct idrent *idrent = (struct idrent *)rn; *children ++ = idrent->isoent; } return (ARCHIVE_OK); } /* * - Generate ISO9660 and Joliet identifiers from basenames. * - Sort files by each directory. */ static int isoent_traverse_tree(struct archive_write *a, struct vdd* vdd) { struct iso9660 *iso9660 = a->format_data; struct isoent *np; struct idr idr; int depth; int r; int (*genid)(struct archive_write *, struct isoent *, struct idr *); idr_init(iso9660, vdd, &idr); np = vdd->rootent; depth = 0; if (vdd->vdd_type == VDD_JOLIET) genid = isoent_gen_joliet_identifier; else genid = isoent_gen_iso9660_identifier; do { if (np->virtual && !archive_entry_mtime_is_set(np->file->entry)) { /* Set properly times to virtual directory */ archive_entry_set_mtime(np->file->entry, iso9660->birth_time, 0); archive_entry_set_atime(np->file->entry, iso9660->birth_time, 0); archive_entry_set_ctime(np->file->entry, iso9660->birth_time, 0); } if (np->children.first != NULL) { if (vdd->vdd_type != VDD_JOLIET && !iso9660->opt.rr && depth + 1 >= vdd->max_depth) { if (np->children.cnt > 0) iso9660->directories_too_deep = np; } else { /* Generate Identifier */ r = genid(a, np, &idr); if (r < 0) goto exit_traverse_tree; r = isoent_make_sorted_files(a, np, &idr); if (r < 0) goto exit_traverse_tree; if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } } } while (np != np->parent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != np->parent); r = ARCHIVE_OK; exit_traverse_tree: idr_cleanup(&idr); return (r); } /* * Collect directory entries into path_table by a directory depth. */ static int isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth) { struct isoent *np; if (rootent == NULL) rootent = vdd->rootent; np = rootent; do { /* Register current directory to pathtable. */ path_table_add_entry(&(vdd->pathtbl[depth]), np); if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { /* Enter to sub directories. */ np = np->subdirs.first; depth++; continue; } while (np != rootent) { if (np->drnext == NULL) { /* Return to the parent directory. */ np = np->parent; depth--; } else { np = np->drnext; break; } } } while (np != rootent); return (ARCHIVE_OK); } /* * The entry whose number of levels in a directory hierarchy is * large than eight relocate to rr_move directory. */ static int isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved, struct isoent *curent, struct isoent **newent) { struct iso9660 *iso9660 = a->format_data; struct isoent *rrmoved, *mvent, *np; if ((rrmoved = *rr_moved) == NULL) { struct isoent *rootent = iso9660->primary.rootent; /* There isn't rr_move entry. * Create rr_move entry and insert it into the root entry. */ rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved"); if (rrmoved == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } /* Add "rr_moved" entry to the root entry. */ isoent_add_child_head(rootent, rrmoved); archive_entry_set_nlink(rootent->file->entry, archive_entry_nlink(rootent->file->entry) + 1); /* Register "rr_moved" entry to second level pathtable. */ path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved); /* Save rr_moved. */ *rr_moved = rrmoved; } /* * Make a clone of curent which is going to be relocated * to rr_moved. */ mvent = isoent_clone(curent); if (mvent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } /* linking.. and use for creating "CL", "PL" and "RE" */ mvent->rr_parent = curent->parent; curent->rr_child = mvent; /* * Move subdirectories from the curent to mvent */ if (curent->children.first != NULL) { *mvent->children.last = curent->children.first; mvent->children.last = curent->children.last; } for (np = mvent->children.first; np != NULL; np = np->chnext) np->parent = mvent; mvent->children.cnt = curent->children.cnt; curent->children.cnt = 0; curent->children.first = NULL; curent->children.last = &curent->children.first; if (curent->subdirs.first != NULL) { *mvent->subdirs.last = curent->subdirs.first; mvent->subdirs.last = curent->subdirs.last; } mvent->subdirs.cnt = curent->subdirs.cnt; curent->subdirs.cnt = 0; curent->subdirs.first = NULL; curent->subdirs.last = &curent->subdirs.first; /* * The mvent becomes a child of the rr_moved entry. */ isoent_add_child_tail(rrmoved, mvent); archive_entry_set_nlink(rrmoved->file->entry, archive_entry_nlink(rrmoved->file->entry) + 1); /* * This entry which relocated to the rr_moved directory * has to set the flag as a file. * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry. */ curent->dir = 0; *newent = mvent; return (ARCHIVE_OK); } static int isoent_rr_move(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct path_table *pt; struct isoent *rootent, *rr_moved; struct isoent *np, *last; int r; pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]); /* There aren't level 8 directories reaching a deeper level. */ if (pt->cnt == 0) return (ARCHIVE_OK); rootent = iso9660->primary.rootent; /* If "rr_moved" directory is already existing, * we have to use it. */ rr_moved = isoent_find_child(rootent, "rr_moved"); if (rr_moved != NULL && rr_moved != rootent->children.first) { /* * It's necessary that rr_move is the first entry * of the root. */ /* Remove "rr_moved" entry from children chain. */ isoent_remove_child(rootent, rr_moved); /* Add "rr_moved" entry into the head of children chain. */ isoent_add_child_head(rootent, rr_moved); } /* * Check level 8 path_table. * If find out sub directory entries, that entries move to rr_move. */ np = pt->first; while (np != NULL) { last = path_table_last_entry(pt); for (; np != NULL; np = np->ptnext) { struct isoent *mvent; struct isoent *newent; if (!np->dir) continue; for (mvent = np->subdirs.first; mvent != NULL; mvent = mvent->drnext) { r = isoent_rr_move_dir(a, &rr_moved, mvent, &newent); if (r < 0) return (r); isoent_collect_dirs(&(iso9660->primary), newent, 2); } } /* If new entries are added to level 8 path_talbe, * its sub directory entries move to rr_move too. */ np = last->ptnext; } return (ARCHIVE_OK); } /* * This comparing rule is according to ISO9660 Standard 6.9.1 */ static int _compare_path_table(const void *v1, const void *v2) { const struct isoent *p1, *p2; const char *s1, *s2; int cmp, l; p1 = *((const struct isoent **)(uintptr_t)v1); p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); /* Compare identifier */ s1 = p1->identifier; s2 = p2->identifier; l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = strncmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0x20 != *s2++) return (0x20 - *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0x20 != *s1++) return (*(const unsigned char *)(s1 - 1) - 0x20); } return (0); } static int _compare_path_table_joliet(const void *v1, const void *v2) { const struct isoent *p1, *p2; const unsigned char *s1, *s2; int cmp, l; p1 = *((const struct isoent **)(uintptr_t)v1); p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); /* Compare identifier */ s1 = (const unsigned char *)p1->identifier; s2 = (const unsigned char *)p2->identifier; l = p1->ext_off; if (l > p2->ext_off) l = p2->ext_off; cmp = memcmp(s1, s2, l); if (cmp != 0) return (cmp); if (p1->ext_off < p2->ext_off) { s2 += l; l = p2->ext_off - p1->ext_off; while (l--) if (0 != *s2++) return (- *(const unsigned char *)(s2 - 1)); } else if (p1->ext_off > p2->ext_off) { s1 += l; l = p1->ext_off - p2->ext_off; while (l--) if (0 != *s1++) return (*(const unsigned char *)(s1 - 1)); } return (0); } static inline void path_table_add_entry(struct path_table *pathtbl, struct isoent *ent) { ent->ptnext = NULL; *pathtbl->last = ent; pathtbl->last = &(ent->ptnext); pathtbl->cnt ++; } static inline struct isoent * path_table_last_entry(struct path_table *pathtbl) { if (pathtbl->first == NULL) return (NULL); return (((struct isoent *)(void *) ((char *)(pathtbl->last) - offsetof(struct isoent, ptnext)))); } /* * Sort directory entries in path_table * and assign directory number to each entries. */ static int isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd, int depth, int *dir_number) { struct isoent *np; struct isoent **enttbl; struct path_table *pt; int i; pt = &vdd->pathtbl[depth]; if (pt->cnt == 0) { pt->sorted = NULL; return (ARCHIVE_OK); } enttbl = malloc(pt->cnt * sizeof(struct isoent *)); if (enttbl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } pt->sorted = enttbl; for (np = pt->first; np != NULL; np = np->ptnext) *enttbl ++ = np; enttbl = pt->sorted; switch (vdd->vdd_type) { case VDD_PRIMARY: case VDD_ENHANCED: #ifdef __COMPAR_FN_T qsort(enttbl, pt->cnt, sizeof(struct isoent *), (__compar_fn_t)_compare_path_table); #else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table); #endif break; case VDD_JOLIET: #ifdef __COMPAR_FN_T qsort(enttbl, pt->cnt, sizeof(struct isoent *), (__compar_fn_t)_compare_path_table_joliet); #else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table_joliet); #endif break; } for (i = 0; i < pt->cnt; i++) enttbl[i]->dir_number = (*dir_number)++; return (ARCHIVE_OK); } static int isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd, int max_depth) { int i; vdd->max_depth = max_depth; vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth); if (vdd->pathtbl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } for (i = 0; i < vdd->max_depth; i++) { vdd->pathtbl[i].first = NULL; vdd->pathtbl[i].last = &(vdd->pathtbl[i].first); vdd->pathtbl[i].sorted = NULL; vdd->pathtbl[i].cnt = 0; } return (ARCHIVE_OK); } /* * Make Path Tables */ static int isoent_make_path_table(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int depth, r; int dir_number; /* * Init Path Table. */ if (iso9660->dircnt_max >= MAX_DEPTH && (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4)) r = isoent_alloc_path_table(a, &(iso9660->primary), iso9660->dircnt_max + 1); else /* The number of levels in the hierarchy cannot exceed * eight. */ r = isoent_alloc_path_table(a, &(iso9660->primary), MAX_DEPTH); if (r < 0) return (r); if (iso9660->opt.joliet) { r = isoent_alloc_path_table(a, &(iso9660->joliet), iso9660->dircnt_max + 1); if (r < 0) return (r); } /* Step 0. * - Collect directories for primary and joliet. */ isoent_collect_dirs(&(iso9660->primary), NULL, 0); if (iso9660->opt.joliet) isoent_collect_dirs(&(iso9660->joliet), NULL, 0); /* * Rockridge; move deeper depth directories to rr_moved. */ if (iso9660->opt.rr) { r = isoent_rr_move(a); if (r < 0) return (r); } /* Update nlink. */ isofile_connect_hardlink_files(iso9660); /* Step 1. * - Renew a value of the depth of that directories. * - Resolve hardlinks. * - Convert pathnames to ISO9660 name or UCS2(joliet). * - Sort files by each directory. */ r = isoent_traverse_tree(a, &(iso9660->primary)); if (r < 0) return (r); if (iso9660->opt.joliet) { r = isoent_traverse_tree(a, &(iso9660->joliet)); if (r < 0) return (r); } /* Step 2. * - Sort directories. * - Assign all directory number. */ dir_number = 1; for (depth = 0; depth < iso9660->primary.max_depth; depth++) { r = isoent_make_path_table_2(a, &(iso9660->primary), depth, &dir_number); if (r < 0) return (r); } if (iso9660->opt.joliet) { dir_number = 1; for (depth = 0; depth < iso9660->joliet.max_depth; depth++) { r = isoent_make_path_table_2(a, &(iso9660->joliet), depth, &dir_number); if (r < 0) return (r); } } if (iso9660->opt.limit_dirs && dir_number > 0xffff) { /* * Maximum number of directories is 65535(0xffff) * doe to size(16bit) of Parent Directory Number of * the Path Table. * See also ISO9660 Standard 9.4. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many directories(%d) over 65535.", dir_number); return (ARCHIVE_FATAL); } /* Get the size of the Path Table. */ calculate_path_table_size(&(iso9660->primary)); if (iso9660->opt.joliet) calculate_path_table_size(&(iso9660->joliet)); return (ARCHIVE_OK); } static int isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent) { struct iso9660 *iso9660 = a->format_data; /* Find a isoent of the boot file. */ iso9660->el_torito.boot = isoent_find_entry(rootent, iso9660->el_torito.boot_filename.s); if (iso9660->el_torito.boot == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't find the boot image file ``%s''", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } iso9660->el_torito.boot->file->boot = BOOT_IMAGE; return (ARCHIVE_OK); } static int isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; struct isoent *isoent; struct archive_entry *entry; (void)rootent; /* UNUSED */ /* * Create the entry which is the "boot.catalog" file. */ file = isofile_new(a, NULL); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } archive_entry_set_pathname(file->entry, iso9660->el_torito.catalog_filename.s); archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE); archive_entry_set_mtime(file->entry, iso9660->birth_time, 0); archive_entry_set_atime(file->entry, iso9660->birth_time, 0); archive_entry_set_ctime(file->entry, iso9660->birth_time, 0); archive_entry_set_uid(file->entry, getuid()); archive_entry_set_gid(file->entry, getgid()); archive_entry_set_mode(file->entry, AE_IFREG | 0444); archive_entry_set_nlink(file->entry, 1); if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { isofile_free(file); return (ARCHIVE_FATAL); } file->boot = BOOT_CATALOG; file->content.size = LOGICAL_BLOCK_SIZE; isofile_add_entry(iso9660, file); isoent = isoent_new(file); if (isoent == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } isoent->virtual = 1; /* Add the "boot.catalog" entry into tree */ if (isoent_tree(a, &isoent) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->el_torito.catalog = isoent; /* * Get a boot media type. */ switch (iso9660->opt.boot_type) { default: case OPT_BOOT_TYPE_AUTO: /* Try detecting a media type of the boot image. */ entry = iso9660->el_torito.boot->file->entry; if (archive_entry_size(entry) == FD_1_2M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_2M_DISKETTE; else if (archive_entry_size(entry) == FD_1_44M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_44M_DISKETTE; else if (archive_entry_size(entry) == FD_2_88M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_2_88M_DISKETTE; else /* We cannot decide whether the boot image is * hard-disk. */ iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; break; case OPT_BOOT_TYPE_NO_EMU: iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; break; case OPT_BOOT_TYPE_HARD_DISK: iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK; break; case OPT_BOOT_TYPE_FD: entry = iso9660->el_torito.boot->file->entry; if (archive_entry_size(entry) <= FD_1_2M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_2M_DISKETTE; else if (archive_entry_size(entry) <= FD_1_44M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_1_44M_DISKETTE; else if (archive_entry_size(entry) <= FD_2_88M_SIZE) iso9660->el_torito.media_type = BOOT_MEDIA_2_88M_DISKETTE; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Boot image file(``%s'') size is too big " "for fd type.", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } break; } /* * Get a system type. * TODO: `El Torito' specification says "A copy of byte 5 from the * Partition Table found in the boot image". */ iso9660->el_torito.system_type = 0; /* * Get an ID. */ if (iso9660->opt.publisher) archive_string_copy(&(iso9660->el_torito.id), &(iso9660->publisher_identifier)); return (ARCHIVE_OK); } /* * If a media type is floppy, return its image size. * otherwise return 0. */ static size_t fd_boot_image_size(int media_type) { switch (media_type) { case BOOT_MEDIA_1_2M_DISKETTE: return (FD_1_2M_SIZE); case BOOT_MEDIA_1_44M_DISKETTE: return (FD_1_44M_SIZE); case BOOT_MEDIA_2_88M_DISKETTE: return (FD_2_88M_SIZE); default: return (0); } } /* * Make a boot catalog image data. */ static int make_boot_catalog(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; unsigned char *block; unsigned char *p; uint16_t sum, *wp; block = wb_buffptr(a); memset(block, 0, LOGICAL_BLOCK_SIZE); p = block; /* * Validation Entry */ /* Header ID */ p[0] = 1; /* Platform ID */ p[1] = iso9660->el_torito.platform_id; /* Reserved */ p[2] = p[3] = 0; /* ID */ if (archive_strlen(&(iso9660->el_torito.id)) > 0) strncpy((char *)p+4, iso9660->el_torito.id.s, 23); p[27] = 0; /* Checksum */ p[28] = p[29] = 0; /* Key */ p[30] = 0x55; p[31] = 0xAA; sum = 0; wp = (uint16_t *)block; while (wp < (uint16_t *)&block[32]) sum += archive_le16dec(wp++); set_num_721(&block[28], (~sum) + 1); /* * Initial/Default Entry */ p = &block[32]; /* Boot Indicator */ p[0] = 0x88; /* Boot media type */ p[1] = iso9660->el_torito.media_type; /* Load Segment */ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) set_num_721(&p[2], iso9660->el_torito.boot_load_seg); else set_num_721(&p[2], 0); /* System Type */ p[4] = iso9660->el_torito.system_type; /* Unused */ p[5] = 0; /* Sector Count */ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) set_num_721(&p[6], iso9660->el_torito.boot_load_size); else set_num_721(&p[6], 1); /* Load RBA */ set_num_731(&p[8], iso9660->el_torito.boot->file->content.location); /* Unused */ memset(&p[12], 0, 20); return (wb_consume(a, LOGICAL_BLOCK_SIZE)); } static int setup_boot_information(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isoent *np; int64_t size; uint32_t sum; unsigned char buff[4096]; np = iso9660->el_torito.boot; lseek(iso9660->temp_fd, np->file->content.offset_of_temp + 64, SEEK_SET); size = archive_entry_size(np->file->entry) - 64; if (size <= 0) { archive_set_error(&a->archive, errno, "Boot file(%jd) is too small", (intmax_t)size + 64); return (ARCHIVE_FATAL); } sum = 0; while (size > 0) { size_t rsize; ssize_t i, rs; if (size > (int64_t)sizeof(buff)) rsize = sizeof(buff); else rsize = (size_t)size; rs = read(iso9660->temp_fd, buff, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } for (i = 0; i < rs; i += 4) sum += archive_le32dec(buff + i); size -= rs; } /* Set the location of Primary Volume Descriptor. */ set_num_731(buff, SYSTEM_AREA_BLOCK); /* Set the location of the boot file. */ set_num_731(buff+4, np->file->content.location); /* Set the size of the boot file. */ size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = archive_entry_size(np->file->entry); set_num_731(buff+8, (uint32_t)size); /* Set the sum of the boot file. */ set_num_731(buff+12, sum); /* Clear reserved bytes. */ memset(buff+16, 0, 40); /* Overwrite the boot file. */ lseek(iso9660->temp_fd, np->file->content.offset_of_temp + 8, SEEK_SET); return (write_to_temp(a, buff, 56)); } #ifdef HAVE_ZLIB_H static int zisofs_init_zstream(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int r; iso9660->zisofs.stream.next_in = NULL; iso9660->zisofs.stream.avail_in = 0; iso9660->zisofs.stream.total_in = 0; iso9660->zisofs.stream.total_out = 0; if (iso9660->zisofs.stream_valid) r = deflateReset(&(iso9660->zisofs.stream)); else { r = deflateInit(&(iso9660->zisofs.stream), iso9660->zisofs.compression_level); iso9660->zisofs.stream_valid = 1; } switch (r) { case Z_OK: break; default: case Z_STREAM_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: invalid setup parameter"); return (ARCHIVE_FATAL); case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Internal error initializing " "compression library"); return (ARCHIVE_FATAL); case Z_VERSION_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: invalid library version"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ static int zisofs_init(struct archive_write *a, struct isofile *file) { struct iso9660 *iso9660 = a->format_data; #ifdef HAVE_ZLIB_H uint64_t tsize; size_t _ceil, bpsize; int r; #endif iso9660->zisofs.detect_magic = 0; iso9660->zisofs.making = 0; if (!iso9660->opt.rr || !iso9660->opt.zisofs) return (ARCHIVE_OK); if (archive_entry_size(file->entry) >= 24 && archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) { /* Acceptable file size for zisofs. */ iso9660->zisofs.detect_magic = 1; iso9660->zisofs.magic_cnt = 0; } if (!iso9660->zisofs.detect_magic) return (ARCHIVE_OK); #ifdef HAVE_ZLIB_H /* The number of Logical Blocks which uncompressed data * will use in iso-image file is the same as the number of * Logical Blocks which zisofs(compressed) data will use * in ISO-image file. It won't reduce iso-image file size. */ if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE) return (ARCHIVE_OK); /* Initialize compression library */ r = zisofs_init_zstream(a); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Mark file->zisofs to create RRIP 'ZF' Use Entry. */ file->zisofs.header_size = ZF_HEADER_SIZE >> 2; file->zisofs.log2_bs = ZF_LOG2_BS; file->zisofs.uncompressed_size = (uint32_t)archive_entry_size(file->entry); /* Calculate a size of Block Pointers of zisofs. */ _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) >> file->zisofs.log2_bs; iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1; iso9660->zisofs.block_pointers_idx = 0; /* Ensure a buffer size used for Block Pointers */ bpsize = iso9660->zisofs.block_pointers_cnt * sizeof(iso9660->zisofs.block_pointers[0]); if (iso9660->zisofs.block_pointers_allocated < bpsize) { free(iso9660->zisofs.block_pointers); iso9660->zisofs.block_pointers = malloc(bpsize); if (iso9660->zisofs.block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } iso9660->zisofs.block_pointers_allocated = bpsize; } /* * Skip zisofs header and Block Pointers, which we will write * after all compressed data of a file written to the temporary * file. */ tsize = ZF_HEADER_SIZE + bpsize; if (write_null(a, (size_t)tsize) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Initialize some variables to make zisofs. */ archive_le32enc(&(iso9660->zisofs.block_pointers[0]), (uint32_t)tsize); iso9660->zisofs.remaining = file->zisofs.uncompressed_size; iso9660->zisofs.making = 1; iso9660->zisofs.allzero = 1; iso9660->zisofs.block_offset = tsize; iso9660->zisofs.total_size = tsize; iso9660->cur_file->cur_content->size = tsize; #endif return (ARCHIVE_OK); } static void zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; const unsigned char *p, *endp; const unsigned char *magic_buff; uint32_t uncompressed_size; unsigned char header_size; unsigned char log2_bs; size_t _ceil, doff; uint32_t bst, bed; int magic_max; int64_t entry_size; entry_size = archive_entry_size(file->entry); if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size) magic_max = (int)entry_size; else magic_max = sizeof(iso9660->zisofs.magic_buffer); if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max) /* It's unnecessary we copy buffer. */ magic_buff = buff; else { if (iso9660->zisofs.magic_cnt < magic_max) { size_t l; l = sizeof(iso9660->zisofs.magic_buffer) - iso9660->zisofs.magic_cnt; if (l > s) l = s; memcpy(iso9660->zisofs.magic_buffer + iso9660->zisofs.magic_cnt, buff, l); iso9660->zisofs.magic_cnt += (int)l; if (iso9660->zisofs.magic_cnt < magic_max) return; } magic_buff = iso9660->zisofs.magic_buffer; } iso9660->zisofs.detect_magic = 0; p = magic_buff; /* Check the magic code of zisofs. */ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) /* This is not zisofs file which made by mkzftree. */ return; p += sizeof(zisofs_magic); /* Read a zisofs header. */ uncompressed_size = archive_le32dec(p); header_size = p[4]; log2_bs = p[5]; if (uncompressed_size < 24 || header_size != 4 || log2_bs > 30 || log2_bs < 7) return;/* Invalid or not supported header. */ /* Calculate a size of Block Pointers of zisofs. */ _ceil = (uncompressed_size + (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs; doff = (_ceil + 1) * 4 + 16; if (entry_size < (int64_t)doff) return;/* Invalid data. */ /* Check every Block Pointer has valid value. */ p = magic_buff + 16; endp = magic_buff + magic_max; while (_ceil && p + 8 <= endp) { bst = archive_le32dec(p); if (bst != doff) return;/* Invalid data. */ p += 4; bed = archive_le32dec(p); if (bed < bst || bed > entry_size) return;/* Invalid data. */ doff += bed - bst; _ceil--; } file->zisofs.uncompressed_size = uncompressed_size; file->zisofs.header_size = header_size; file->zisofs.log2_bs = log2_bs; /* Disable making a zisofs image. */ iso9660->zisofs.making = 0; } #ifdef HAVE_ZLIB_H /* * Compress data and write it to a temporary file. */ static int zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; const unsigned char *b; z_stream *zstrm; size_t avail, csize; int flush, r; zstrm = &(iso9660->zisofs.stream); zstrm->next_out = wb_buffptr(a); zstrm->avail_out = (uInt)wb_remaining(a); b = (const unsigned char *)buff; do { avail = ZF_BLOCK_SIZE - zstrm->total_in; if (s < avail) { avail = s; flush = Z_NO_FLUSH; } else flush = Z_FINISH; iso9660->zisofs.remaining -= avail; if (iso9660->zisofs.remaining <= 0) flush = Z_FINISH; zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b; zstrm->avail_in = (uInt)avail; /* * Check if current data block are all zero. */ if (iso9660->zisofs.allzero) { const unsigned char *nonzero = b; const unsigned char *nonzeroend = b + avail; while (nonzero < nonzeroend) if (*nonzero++) { iso9660->zisofs.allzero = 0; break; } } b += avail; s -= avail; /* * If current data block are all zero, we do not use * compressed data. */ if (flush == Z_FINISH && iso9660->zisofs.allzero && avail + zstrm->total_in == ZF_BLOCK_SIZE) { if (iso9660->zisofs.block_offset != file->cur_content->size) { int64_t diff; r = wb_set_offset(a, file->cur_content->offset_of_temp + iso9660->zisofs.block_offset); if (r != ARCHIVE_OK) return (r); diff = file->cur_content->size - iso9660->zisofs.block_offset; file->cur_content->size -= diff; iso9660->zisofs.total_size -= diff; } zstrm->avail_in = 0; } /* * Compress file data. */ while (zstrm->avail_in > 0) { csize = zstrm->total_out; r = deflate(zstrm, flush); switch (r) { case Z_OK: case Z_STREAM_END: csize = zstrm->total_out - csize; if (wb_consume(a, csize) != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->zisofs.total_size += csize; iso9660->cur_file->cur_content->size += csize; zstrm->next_out = wb_buffptr(a); zstrm->avail_out = (uInt)wb_remaining(a); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Compression failed:" " deflate() call returned status %d", r); return (ARCHIVE_FATAL); } } if (flush == Z_FINISH) { /* * Save the information of one zisofs block. */ iso9660->zisofs.block_pointers_idx ++; archive_le32enc(&(iso9660->zisofs.block_pointers[ iso9660->zisofs.block_pointers_idx]), (uint32_t)iso9660->zisofs.total_size); r = zisofs_init_zstream(a); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); iso9660->zisofs.allzero = 1; iso9660->zisofs.block_offset = file->cur_content->size; } } while (s); return (ARCHIVE_OK); } static int zisofs_finish_entry(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file = iso9660->cur_file; unsigned char buff[16]; size_t s; int64_t tail; /* Direct temp file stream to zisofs temp file stream. */ archive_entry_set_size(file->entry, iso9660->zisofs.total_size); /* * Save a file pointer which points the end of current zisofs data. */ tail = wb_offset(a); /* * Make a header. * * +-----------------+----------------+-----------------+ * | Header 16 bytes | Block Pointers | Compressed data | * +-----------------+----------------+-----------------+ * 0 16 +X * Block Pointers : * 4 * (((Uncompressed file size + block_size -1) / block_size) + 1) * * Write zisofs header. * Magic number * +----+----+----+----+----+----+----+----+ * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 | * +----+----+----+----+----+----+----+----+ * 0 1 2 3 4 5 6 7 8 * * +------------------------+------------------+ * | Uncompressed file size | header_size >> 2 | * +------------------------+------------------+ * 8 12 13 * * +-----------------+----------------+ * | log2 block_size | Reserved(0000) | * +-----------------+----------------+ * 13 14 16 */ memcpy(buff, zisofs_magic, 8); set_num_731(buff+8, file->zisofs.uncompressed_size); buff[12] = file->zisofs.header_size; buff[13] = file->zisofs.log2_bs; buff[14] = buff[15] = 0;/* Reserved */ /* Move to the right position to write the header. */ wb_set_offset(a, file->content.offset_of_temp); /* Write the header. */ if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Write zisofs Block Pointers. */ s = iso9660->zisofs.block_pointers_cnt * sizeof(iso9660->zisofs.block_pointers[0]); if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Set a file pointer back to the end of the temporary file. */ wb_set_offset(a, tail); return (ARCHIVE_OK); } static int zisofs_free(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; int ret = ARCHIVE_OK; free(iso9660->zisofs.block_pointers); if (iso9660->zisofs.stream_valid && deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } iso9660->zisofs.block_pointers = NULL; iso9660->zisofs.stream_valid = 0; return (ret); } struct zisofs_extract { int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; size_t uncompressed_buffer_size; signed int initialized:1; signed int header_passed:1; uint32_t pz_offset; unsigned char *block_pointers; size_t block_pointers_size; size_t block_pointers_avail; size_t block_off; uint32_t block_avail; z_stream stream; int stream_valid; }; static ssize_t zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail = bytes; size_t _ceil, xsize; /* Allocate block pointers buffer. */ _ceil = (size_t)((zisofs->pz_uncompressed_size + (((int64_t)1) << zisofs->pz_log2_bs) - 1) >> zisofs->pz_log2_bs); xsize = (_ceil + 1) * 4; if (zisofs->block_pointers == NULL) { size_t alloc = ((xsize >> 10) + 1) << 10; zisofs->block_pointers = malloc(alloc); if (zisofs->block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for zisofs decompression"); return (ARCHIVE_FATAL); } } zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs; /* * Read the file header, and check the magic code of zisofs. */ if (!zisofs->header_passed) { int err = 0; if (avail < 16) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) err = 1; else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size) err = 1; else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs) err = 1; if (err) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } avail -= 16; p += 16; zisofs->header_passed = 1; } /* * Read block pointers. */ if (zisofs->header_passed && zisofs->block_pointers_avail < zisofs->block_pointers_size) { xsize = zisofs->block_pointers_size - zisofs->block_pointers_avail; if (avail < xsize) xsize = avail; memcpy(zisofs->block_pointers + zisofs->block_pointers_avail, p, xsize); zisofs->block_pointers_avail += xsize; avail -= xsize; if (zisofs->block_pointers_avail == zisofs->block_pointers_size) { /* We've got all block pointers and initialize * related variables. */ zisofs->block_off = 0; zisofs->block_avail = 0; /* Complete a initialization */ zisofs->initialized = 1; } } return ((ssize_t)avail); } static ssize_t zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail; int r; if (!zisofs->initialized) { ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes); if (rs < 0) return (rs); if (!zisofs->initialized) { /* We need more data. */ zisofs->pz_offset += (uint32_t)bytes; return (bytes); } avail = rs; p += bytes - avail; } else avail = bytes; /* * Get block offsets from block pointers. */ if (zisofs->block_avail == 0) { uint32_t bst, bed; if (zisofs->block_off + 4 >= zisofs->block_pointers_size) { /* There isn't a pair of offsets. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } bst = archive_le32dec( zisofs->block_pointers + zisofs->block_off); if (bst != zisofs->pz_offset + (bytes - avail)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers(cannot seek)"); return (ARCHIVE_FATAL); } bed = archive_le32dec( zisofs->block_pointers + zisofs->block_off + 4); if (bed < bst) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } zisofs->block_avail = bed - bst; zisofs->block_off += 4; /* Initialize compression library for new block. */ if (zisofs->stream_valid) r = inflateReset(&zisofs->stream); else r = inflateInit(&zisofs->stream); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize zisofs decompression."); return (ARCHIVE_FATAL); } zisofs->stream_valid = 1; zisofs->stream.total_in = 0; zisofs->stream.total_out = 0; } /* * Make uncompressed data. */ if (zisofs->block_avail == 0) { /* * It's basically 32K bytes NUL data. */ unsigned char *wb; size_t size, wsize; size = zisofs->uncompressed_buffer_size; while (size) { wb = wb_buffptr(a); if (size > wb_remaining(a)) wsize = wb_remaining(a); else wsize = size; memset(wb, 0, wsize); r = wb_consume(a, wsize); if (r < 0) return (r); size -= wsize; } } else { zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; if (avail > zisofs->block_avail) zisofs->stream.avail_in = zisofs->block_avail; else zisofs->stream.avail_in = (uInt)avail; zisofs->stream.next_out = wb_buffptr(a); zisofs->stream.avail_out = (uInt)wb_remaining(a); r = inflate(&zisofs->stream, 0); switch (r) { case Z_OK: /* Decompressor made some progress.*/ case Z_STREAM_END: /* Found end of stream. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zisofs decompression failed (%d)", r); return (ARCHIVE_FATAL); } avail -= zisofs->stream.next_in - p; zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out); if (r < 0) return (r); } zisofs->pz_offset += (uint32_t)bytes; return (bytes - avail); } static int zisofs_rewind_boot_file(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; struct isofile *file; unsigned char *rbuff; ssize_t r; size_t remaining, rbuff_size; struct zisofs_extract zext; int64_t read_offset, write_offset, new_offset; int fd, ret = ARCHIVE_OK; file = iso9660->el_torito.boot->file; /* * There is nothing to do if this boot file does not have * zisofs header. */ if (file->zisofs.header_size == 0) return (ARCHIVE_OK); /* * Uncompress the zisofs'ed file contents. */ memset(&zext, 0, sizeof(zext)); zext.pz_uncompressed_size = file->zisofs.uncompressed_size; zext.pz_log2_bs = file->zisofs.log2_bs; fd = iso9660->temp_fd; new_offset = wb_offset(a); read_offset = file->content.offset_of_temp; remaining = (size_t)file->content.size; if (remaining > 1024 * 32) rbuff_size = 1024 * 32; else rbuff_size = remaining; rbuff = malloc(rbuff_size); if (rbuff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } while (remaining) { size_t rsize; ssize_t rs; /* Get the current file pointer. */ write_offset = lseek(fd, 0, SEEK_CUR); /* Change the file pointer to read. */ lseek(fd, read_offset, SEEK_SET); rsize = rbuff_size; if (rsize > remaining) rsize = remaining; rs = read(iso9660->temp_fd, rbuff, rsize); if (rs <= 0) { archive_set_error(&a->archive, errno, "Can't read temporary file(%jd)", (intmax_t)rs); ret = ARCHIVE_FATAL; break; } remaining -= rs; read_offset += rs; /* Put the file pointer back to write. */ lseek(fd, write_offset, SEEK_SET); r = zisofs_extract(a, &zext, rbuff, rs); if (r < 0) { ret = (int)r; break; } } if (ret == ARCHIVE_OK) { /* * Change the boot file content from zisofs'ed data * to plain data. */ file->content.offset_of_temp = new_offset; file->content.size = file->zisofs.uncompressed_size; archive_entry_set_size(file->entry, file->content.size); /* Set to be no zisofs. */ file->zisofs.header_size = 0; file->zisofs.log2_bs = 0; file->zisofs.uncompressed_size = 0; r = wb_write_padding_to_temp(a, file->content.size); if (r < 0) ret = ARCHIVE_FATAL; } /* * Free the resource we used in this function only. */ free(rbuff); free(zext.block_pointers); if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } return (ret); } #else static int zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { (void)buff; /* UNUSED */ (void)s; /* UNUSED */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programming error"); return (ARCHIVE_FATAL); } static int zisofs_rewind_boot_file(struct archive_write *a) { struct iso9660 *iso9660 = a->format_data; if (iso9660->el_torito.boot->file->zisofs.header_size != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "We cannot extract the zisofs imaged boot file;" " this may not boot in being zisofs imaged"); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int zisofs_finish_entry(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int zisofs_free(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H */ Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_mtree.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_mtree.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_mtree.c (revision 368707) @@ -1,2228 +1,2217 @@ /*- * Copyright (c) 2008 Joerg Sonnenberger * Copyright (c) 2009-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #include "archive.h" #include "archive_digest_private.h" #include "archive_entry.h" +#include "archive_entry_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_string.h" #include "archive_write_private.h" #define INDENTNAMELEN 15 #define MAXLINELEN 80 #define SET_KEYS \ (F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME) struct attr_counter { struct attr_counter *prev; struct attr_counter *next; struct mtree_entry *m_entry; int count; }; struct att_counter_set { struct attr_counter *uid_list; struct attr_counter *gid_list; struct attr_counter *mode_list; struct attr_counter *flags_list; }; struct mtree_chain { struct mtree_entry *first; struct mtree_entry **last; }; /* * The Data only for a directory file. */ struct dir_info { struct archive_rb_tree rbtree; struct mtree_chain children; struct mtree_entry *chnext; int virtual; }; /* * The Data only for a regular file. */ struct reg_info { int compute_sum; uint32_t crc; -#ifdef ARCHIVE_HAS_MD5 - unsigned char buf_md5[16]; -#endif -#ifdef ARCHIVE_HAS_RMD160 - unsigned char buf_rmd160[20]; -#endif -#ifdef ARCHIVE_HAS_SHA1 - unsigned char buf_sha1[20]; -#endif -#ifdef ARCHIVE_HAS_SHA256 - unsigned char buf_sha256[32]; -#endif -#ifdef ARCHIVE_HAS_SHA384 - unsigned char buf_sha384[48]; -#endif -#ifdef ARCHIVE_HAS_SHA512 - unsigned char buf_sha512[64]; -#endif + struct ae_digest digest; }; struct mtree_entry { struct archive_rb_node rbnode; struct mtree_entry *next; struct mtree_entry *parent; struct dir_info *dir_info; struct reg_info *reg_info; struct archive_string parentdir; struct archive_string basename; struct archive_string pathname; struct archive_string symlink; struct archive_string uname; struct archive_string gname; struct archive_string fflags_text; unsigned int nlink; mode_t filetype; mode_t mode; int64_t size; int64_t uid; int64_t gid; time_t mtime; long mtime_nsec; unsigned long fflags_set; unsigned long fflags_clear; dev_t rdevmajor; dev_t rdevminor; dev_t devmajor; dev_t devminor; int64_t ino; }; struct mtree_writer { struct mtree_entry *mtree_entry; struct mtree_entry *root; struct mtree_entry *cur_dirent; struct archive_string cur_dirstr; struct mtree_chain file_list; struct archive_string ebuf; struct archive_string buf; int first; uint64_t entry_bytes_remaining; /* * Set global value. */ struct { int processing; mode_t type; int keys; int64_t uid; int64_t gid; mode_t mode; unsigned long fflags_set; unsigned long fflags_clear; } set; struct att_counter_set acs; int classic; int depth; /* check sum */ int compute_sum; uint32_t crc; uint64_t crc_len; #ifdef ARCHIVE_HAS_MD5 archive_md5_ctx md5ctx; #endif #ifdef ARCHIVE_HAS_RMD160 archive_rmd160_ctx rmd160ctx; #endif #ifdef ARCHIVE_HAS_SHA1 archive_sha1_ctx sha1ctx; #endif #ifdef ARCHIVE_HAS_SHA256 archive_sha256_ctx sha256ctx; #endif #ifdef ARCHIVE_HAS_SHA384 archive_sha384_ctx sha384ctx; #endif #ifdef ARCHIVE_HAS_SHA512 archive_sha512_ctx sha512ctx; #endif /* Keyword options */ int keys; #define F_CKSUM 0x00000001 /* checksum */ #define F_DEV 0x00000002 /* device type */ #define F_DONE 0x00000004 /* directory done */ #define F_FLAGS 0x00000008 /* file flags */ #define F_GID 0x00000010 /* gid */ #define F_GNAME 0x00000020 /* group name */ #define F_IGN 0x00000040 /* ignore */ #define F_MAGIC 0x00000080 /* name has magic chars */ #define F_MD5 0x00000100 /* MD5 digest */ #define F_MODE 0x00000200 /* mode */ #define F_NLINK 0x00000400 /* number of links */ #define F_NOCHANGE 0x00000800 /* If owner/mode "wrong", do * not change */ #define F_OPT 0x00001000 /* existence optional */ #define F_RMD160 0x00002000 /* RIPEMD160 digest */ #define F_SHA1 0x00004000 /* SHA-1 digest */ #define F_SIZE 0x00008000 /* size */ #define F_SLINK 0x00010000 /* symbolic link */ #define F_TAGS 0x00020000 /* tags */ #define F_TIME 0x00040000 /* modification time */ #define F_TYPE 0x00080000 /* file type */ #define F_UID 0x00100000 /* uid */ #define F_UNAME 0x00200000 /* user name */ #define F_VISIT 0x00400000 /* file visited */ #define F_SHA256 0x00800000 /* SHA-256 digest */ #define F_SHA384 0x01000000 /* SHA-384 digest */ #define F_SHA512 0x02000000 /* SHA-512 digest */ #define F_INO 0x04000000 /* inode number */ #define F_RESDEV 0x08000000 /* device ID on which the * entry resides */ /* Options */ int dironly; /* If it is set, ignore all files except * directory files, like mtree(8) -d option. */ int indent; /* If it is set, indent output data. */ int output_global_set; /* If it is set, use /set keyword to set * global values. When generating mtree * classic format, it is set by default. */ }; #define DEFAULT_KEYS (F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\ | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\ | F_UNAME) #define attr_counter_set_reset attr_counter_set_free static void attr_counter_free(struct attr_counter **); static int attr_counter_inc(struct attr_counter **, struct attr_counter *, struct attr_counter *, struct mtree_entry *); static struct attr_counter * attr_counter_new(struct mtree_entry *, struct attr_counter *); static int attr_counter_set_collect(struct mtree_writer *, struct mtree_entry *); static void attr_counter_set_free(struct mtree_writer *); static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *); static int mtree_entry_add_child_tail(struct mtree_entry *, struct mtree_entry *); static int mtree_entry_create_virtual_dir(struct archive_write *, const char *, struct mtree_entry **); static int mtree_entry_cmp_node(const struct archive_rb_node *, const struct archive_rb_node *); static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *); static int mtree_entry_exchange_same_entry(struct archive_write *, struct mtree_entry *, struct mtree_entry *); static void mtree_entry_free(struct mtree_entry *); static int mtree_entry_new(struct archive_write *, struct archive_entry *, struct mtree_entry **); static void mtree_entry_register_free(struct mtree_writer *); static void mtree_entry_register_init(struct mtree_writer *); static int mtree_entry_setup_filenames(struct archive_write *, struct mtree_entry *, struct archive_entry *); static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **); static void sum_init(struct mtree_writer *); static void sum_update(struct mtree_writer *, const void *, size_t); static void sum_final(struct mtree_writer *, struct reg_info *); static void sum_write(struct archive_string *, struct reg_info *); static int write_mtree_entry(struct archive_write *, struct mtree_entry *); static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *); #define COMPUTE_CRC(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] static const uint32_t crctab[] = { 0x0, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ /* !"$%&'()*+,-./ EXCLUSION:0x20( ) 0x23(#) */ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ /* 0123456789:;<>? EXCLUSION:0x3d(=) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ /* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; static void mtree_quote(struct archive_string *s, const char *str) { const char *start; char buf[4]; unsigned char c; for (start = str; *str != '\0'; ++str) { if (safe_char[*(const unsigned char *)str]) continue; if (start != str) archive_strncat(s, start, str - start); c = (unsigned char)*str; buf[0] = '\\'; buf[1] = (c / 64) + '0'; buf[2] = (c / 8 % 8) + '0'; buf[3] = (c % 8) + '0'; archive_strncat(s, buf, 4); start = str + 1; } if (start != str) archive_strncat(s, start, str - start); } /* * Indent a line as the mtree utility does so it is readable for people. */ static void mtree_indent(struct mtree_writer *mtree) { int i, fn, nd, pd; const char *r, *s, *x; if (mtree->classic) { if (mtree->indent) { nd = 0; pd = mtree->depth * 4; } else { nd = mtree->depth?4:0; pd = 0; } } else nd = pd = 0; fn = 1; s = r = mtree->ebuf.s; x = NULL; while (*r == ' ') r++; while ((r = strchr(r, ' ')) != NULL) { if (fn) { fn = 0; for (i = 0; i < nd + pd; i++) archive_strappend_char(&mtree->buf, ' '); archive_strncat(&mtree->buf, s, r - s); if (nd + (r -s) > INDENTNAMELEN) { archive_strncat(&mtree->buf, " \\\n", 3); for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++) archive_strappend_char(&mtree->buf, ' '); } else { for (i = (int)(r -s + nd); i < (INDENTNAMELEN + 1); i++) archive_strappend_char(&mtree->buf, ' '); } s = ++r; x = NULL; continue; } if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN) x = r++; else { if (x == NULL) x = r; archive_strncat(&mtree->buf, s, x - s); archive_strncat(&mtree->buf, " \\\n", 3); for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++) archive_strappend_char(&mtree->buf, ' '); s = r = ++x; x = NULL; } } if (fn) { for (i = 0; i < nd + pd; i++) archive_strappend_char(&mtree->buf, ' '); archive_strcat(&mtree->buf, s); s += strlen(s); } if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) { /* Last keyword is longer. */ archive_strncat(&mtree->buf, s, x - s); archive_strncat(&mtree->buf, " \\\n", 3); for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++) archive_strappend_char(&mtree->buf, ' '); s = ++x; } archive_strcat(&mtree->buf, s); archive_string_empty(&mtree->ebuf); } /* * Write /set keyword. * Set the most used value of uid, gid, mode and fflags, which are * collected by the attr_counter_set_collect() function. */ static void write_global(struct mtree_writer *mtree) { struct archive_string setstr; struct archive_string unsetstr; struct att_counter_set *acs; int keys, oldkeys, effkeys; archive_string_init(&setstr); archive_string_init(&unsetstr); keys = mtree->keys & SET_KEYS; oldkeys = mtree->set.keys; effkeys = keys; acs = &mtree->acs; if (mtree->set.processing) { /* * Check if the global data needs updating. */ effkeys &= ~F_TYPE; if (acs->uid_list == NULL) effkeys &= ~(F_UNAME | F_UID); else if (oldkeys & (F_UNAME | F_UID)) { if (acs->uid_list->count < 2 || mtree->set.uid == acs->uid_list->m_entry->uid) effkeys &= ~(F_UNAME | F_UID); } if (acs->gid_list == NULL) effkeys &= ~(F_GNAME | F_GID); else if (oldkeys & (F_GNAME | F_GID)) { if (acs->gid_list->count < 2 || mtree->set.gid == acs->gid_list->m_entry->gid) effkeys &= ~(F_GNAME | F_GID); } if (acs->mode_list == NULL) effkeys &= ~F_MODE; else if (oldkeys & F_MODE) { if (acs->mode_list->count < 2 || mtree->set.mode == acs->mode_list->m_entry->mode) effkeys &= ~F_MODE; } if (acs->flags_list == NULL) effkeys &= ~F_FLAGS; else if ((oldkeys & F_FLAGS) != 0) { if (acs->flags_list->count < 2 || (acs->flags_list->m_entry->fflags_set == mtree->set.fflags_set && acs->flags_list->m_entry->fflags_clear == mtree->set.fflags_clear)) effkeys &= ~F_FLAGS; } } else { if (acs->uid_list == NULL) keys &= ~(F_UNAME | F_UID); if (acs->gid_list == NULL) keys &= ~(F_GNAME | F_GID); if (acs->mode_list == NULL) keys &= ~F_MODE; if (acs->flags_list == NULL) keys &= ~F_FLAGS; } if ((keys & effkeys & F_TYPE) != 0) { if (mtree->dironly) { archive_strcat(&setstr, " type=dir"); mtree->set.type = AE_IFDIR; } else { archive_strcat(&setstr, " type=file"); mtree->set.type = AE_IFREG; } } if ((keys & effkeys & F_UNAME) != 0) { if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) { archive_strcat(&setstr, " uname="); mtree_quote(&setstr, acs->uid_list->m_entry->uname.s); } else { keys &= ~F_UNAME; if ((oldkeys & F_UNAME) != 0) archive_strcat(&unsetstr, " uname"); } } if ((keys & effkeys & F_UID) != 0) { mtree->set.uid = acs->uid_list->m_entry->uid; archive_string_sprintf(&setstr, " uid=%jd", (intmax_t)mtree->set.uid); } if ((keys & effkeys & F_GNAME) != 0) { if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) { archive_strcat(&setstr, " gname="); mtree_quote(&setstr, acs->gid_list->m_entry->gname.s); } else { keys &= ~F_GNAME; if ((oldkeys & F_GNAME) != 0) archive_strcat(&unsetstr, " gname"); } } if ((keys & effkeys & F_GID) != 0) { mtree->set.gid = acs->gid_list->m_entry->gid; archive_string_sprintf(&setstr, " gid=%jd", (intmax_t)mtree->set.gid); } if ((keys & effkeys & F_MODE) != 0) { mtree->set.mode = acs->mode_list->m_entry->mode; archive_string_sprintf(&setstr, " mode=%o", (unsigned int)mtree->set.mode); } if ((keys & effkeys & F_FLAGS) != 0) { if (archive_strlen( &(acs->flags_list->m_entry->fflags_text)) > 0) { archive_strcat(&setstr, " flags="); mtree_quote(&setstr, acs->flags_list->m_entry->fflags_text.s); mtree->set.fflags_set = acs->flags_list->m_entry->fflags_set; mtree->set.fflags_clear = acs->flags_list->m_entry->fflags_clear; } else { keys &= ~F_FLAGS; if ((oldkeys & F_FLAGS) != 0) archive_strcat(&unsetstr, " flags"); } } if (unsetstr.length > 0) archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s); archive_string_free(&unsetstr); if (setstr.length > 0) archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s); archive_string_free(&setstr); mtree->set.keys = keys; mtree->set.processing = 1; } static struct attr_counter * attr_counter_new(struct mtree_entry *me, struct attr_counter *prev) { struct attr_counter *ac; ac = malloc(sizeof(*ac)); if (ac != NULL) { ac->prev = prev; ac->next = NULL; ac->count = 1; ac->m_entry = me; } return (ac); } static void attr_counter_free(struct attr_counter **top) { struct attr_counter *ac, *tac; if (*top == NULL) return; ac = *top; while (ac != NULL) { tac = ac->next; free(ac); ac = tac; } *top = NULL; } static int attr_counter_inc(struct attr_counter **top, struct attr_counter *ac, struct attr_counter *last, struct mtree_entry *me) { struct attr_counter *pac; if (ac != NULL) { ac->count++; if (*top == ac || ac->prev->count >= ac->count) return (0); for (pac = ac->prev; pac; pac = pac->prev) { if (pac->count >= ac->count) break; } ac->prev->next = ac->next; if (ac->next != NULL) ac->next->prev = ac->prev; if (pac != NULL) { ac->prev = pac; ac->next = pac->next; pac->next = ac; if (ac->next != NULL) ac->next->prev = ac; } else { ac->prev = NULL; ac->next = *top; *top = ac; ac->next->prev = ac; } } else if (last != NULL) { ac = attr_counter_new(me, last); if (ac == NULL) return (-1); last->next = ac; } return (0); } /* * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set. */ static int attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me) { struct attr_counter *ac, *last; struct att_counter_set *acs = &mtree->acs; int keys = mtree->keys; if (keys & (F_UNAME | F_UID)) { if (acs->uid_list == NULL) { acs->uid_list = attr_counter_new(me, NULL); if (acs->uid_list == NULL) return (-1); } else { last = NULL; for (ac = acs->uid_list; ac; ac = ac->next) { if (ac->m_entry->uid == me->uid) break; last = ac; } if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0) return (-1); } } if (keys & (F_GNAME | F_GID)) { if (acs->gid_list == NULL) { acs->gid_list = attr_counter_new(me, NULL); if (acs->gid_list == NULL) return (-1); } else { last = NULL; for (ac = acs->gid_list; ac; ac = ac->next) { if (ac->m_entry->gid == me->gid) break; last = ac; } if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0) return (-1); } } if (keys & F_MODE) { if (acs->mode_list == NULL) { acs->mode_list = attr_counter_new(me, NULL); if (acs->mode_list == NULL) return (-1); } else { last = NULL; for (ac = acs->mode_list; ac; ac = ac->next) { if (ac->m_entry->mode == me->mode) break; last = ac; } if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0) return (-1); } } if (keys & F_FLAGS) { if (acs->flags_list == NULL) { acs->flags_list = attr_counter_new(me, NULL); if (acs->flags_list == NULL) return (-1); } else { last = NULL; for (ac = acs->flags_list; ac; ac = ac->next) { if (ac->m_entry->fflags_set == me->fflags_set && ac->m_entry->fflags_clear == me->fflags_clear) break; last = ac; } if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0) return (-1); } } return (0); } static void attr_counter_set_free(struct mtree_writer *mtree) { struct att_counter_set *acs = &mtree->acs; attr_counter_free(&acs->uid_list); attr_counter_free(&acs->gid_list); attr_counter_free(&acs->mode_list); attr_counter_free(&acs->flags_list); } static int get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me) { int keys; keys = mtree->keys; /* * If a keyword has been set by /set, we do not need to * output it. */ if (mtree->set.keys == 0) return (keys);/* /set is not used. */ if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 && mtree->set.gid == me->gid) keys &= ~(F_GNAME | F_GID); if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 && mtree->set.uid == me->uid) keys &= ~(F_UNAME | F_UID); if (mtree->set.keys & F_FLAGS) { if (mtree->set.fflags_set == me->fflags_set && mtree->set.fflags_clear == me->fflags_clear) keys &= ~F_FLAGS; } if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode) keys &= ~F_MODE; switch (me->filetype) { case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: case AE_IFBLK: case AE_IFIFO: break; case AE_IFDIR: if ((mtree->set.keys & F_TYPE) != 0 && mtree->set.type == AE_IFDIR) keys &= ~F_TYPE; break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((mtree->set.keys & F_TYPE) != 0 && mtree->set.type == AE_IFREG) keys &= ~F_TYPE; break; } return (keys); } static int mtree_entry_new(struct archive_write *a, struct archive_entry *entry, struct mtree_entry **m_entry) { struct mtree_entry *me; const char *s; int r; static const struct archive_rb_tree_ops rb_ops = { mtree_entry_cmp_node, mtree_entry_cmp_key }; me = calloc(1, sizeof(*me)); if (me == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a mtree entry"); *m_entry = NULL; return (ARCHIVE_FATAL); } r = mtree_entry_setup_filenames(a, me, entry); if (r < ARCHIVE_WARN) { mtree_entry_free(me); *m_entry = NULL; return (r); } if ((s = archive_entry_symlink(entry)) != NULL) archive_strcpy(&me->symlink, s); me->nlink = archive_entry_nlink(entry); me->filetype = archive_entry_filetype(entry); me->mode = archive_entry_mode(entry) & 07777; me->uid = archive_entry_uid(entry); me->gid = archive_entry_gid(entry); if ((s = archive_entry_uname(entry)) != NULL) archive_strcpy(&me->uname, s); if ((s = archive_entry_gname(entry)) != NULL) archive_strcpy(&me->gname, s); if ((s = archive_entry_fflags_text(entry)) != NULL) archive_strcpy(&me->fflags_text, s); archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear); me->mtime = archive_entry_mtime(entry); me->mtime_nsec = archive_entry_mtime_nsec(entry); me->rdevmajor = archive_entry_rdevmajor(entry); me->rdevminor = archive_entry_rdevminor(entry); me->devmajor = archive_entry_devmajor(entry); me->devminor = archive_entry_devminor(entry); me->ino = archive_entry_ino(entry); me->size = archive_entry_size(entry); if (me->filetype == AE_IFDIR) { me->dir_info = calloc(1, sizeof(*me->dir_info)); if (me->dir_info == NULL) { mtree_entry_free(me); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a mtree entry"); *m_entry = NULL; return (ARCHIVE_FATAL); } __archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops); me->dir_info->children.first = NULL; me->dir_info->children.last = &(me->dir_info->children.first); me->dir_info->chnext = NULL; } else if (me->filetype == AE_IFREG) { me->reg_info = calloc(1, sizeof(*me->reg_info)); if (me->reg_info == NULL) { mtree_entry_free(me); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a mtree entry"); *m_entry = NULL; return (ARCHIVE_FATAL); } me->reg_info->compute_sum = 0; } *m_entry = me; return (ARCHIVE_OK); } static void mtree_entry_free(struct mtree_entry *me) { archive_string_free(&me->parentdir); archive_string_free(&me->basename); archive_string_free(&me->pathname); archive_string_free(&me->symlink); archive_string_free(&me->uname); archive_string_free(&me->gname); archive_string_free(&me->fflags_text); free(me->dir_info); free(me->reg_info); free(me); } static int archive_write_mtree_header(struct archive_write *a, struct archive_entry *entry) { struct mtree_writer *mtree= a->format_data; struct mtree_entry *mtree_entry; int r, r2; if (mtree->first) { mtree->first = 0; archive_strcat(&mtree->buf, "#mtree\n"); if ((mtree->keys & SET_KEYS) == 0) mtree->output_global_set = 0;/* Disabled. */ } mtree->entry_bytes_remaining = archive_entry_size(entry); /* While directory only mode, we do not handle non directory files. */ if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) return (ARCHIVE_OK); r2 = mtree_entry_new(a, entry, &mtree_entry); if (r2 < ARCHIVE_WARN) return (r2); r = mtree_entry_tree_add(a, &mtree_entry); if (r < ARCHIVE_WARN) { mtree_entry_free(mtree_entry); return (r); } mtree->mtree_entry = mtree_entry; /* If the current file is a regular file, we have to * compute the sum of its content. * Initialize a bunch of checksum context. */ if (mtree_entry->reg_info) sum_init(mtree); return (r2); } static int write_mtree_entry(struct archive_write *a, struct mtree_entry *me) { struct mtree_writer *mtree = a->format_data; struct archive_string *str; int keys, ret; if (me->dir_info) { if (mtree->classic) { /* * Output a comment line to describe the full * pathname of the entry as mtree utility does * while generating classic format. */ if (!mtree->dironly) archive_strappend_char(&mtree->buf, '\n'); if (me->parentdir.s) archive_string_sprintf(&mtree->buf, "# %s/%s\n", me->parentdir.s, me->basename.s); else archive_string_sprintf(&mtree->buf, "# %s\n", me->basename.s); } if (mtree->output_global_set) write_global(mtree); } archive_string_empty(&mtree->ebuf); str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf; if (!mtree->classic && me->parentdir.s) { /* * If generating format is not classic one(v1), output * a full pathname. */ mtree_quote(str, me->parentdir.s); archive_strappend_char(str, '/'); } mtree_quote(str, me->basename.s); keys = get_global_set_keys(mtree, me); if ((keys & F_NLINK) != 0 && me->nlink != 1 && me->filetype != AE_IFDIR) archive_string_sprintf(str, " nlink=%u", me->nlink); if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) { archive_strcat(str, " gname="); mtree_quote(str, me->gname.s); } if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) { archive_strcat(str, " uname="); mtree_quote(str, me->uname.s); } if ((keys & F_FLAGS) != 0) { if (archive_strlen(&me->fflags_text) > 0) { archive_strcat(str, " flags="); mtree_quote(str, me->fflags_text.s); } else if (mtree->set.processing && (mtree->set.keys & F_FLAGS) != 0) /* Overwrite the global parameter. */ archive_strcat(str, " flags=none"); } if ((keys & F_TIME) != 0) archive_string_sprintf(str, " time=%jd.%jd", (intmax_t)me->mtime, (intmax_t)me->mtime_nsec); if ((keys & F_MODE) != 0) archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode); if ((keys & F_GID) != 0) archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid); if ((keys & F_UID) != 0) archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid); if ((keys & F_INO) != 0) archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino); if ((keys & F_RESDEV) != 0) { archive_string_sprintf(str, " resdevice=native,%ju,%ju", (uintmax_t)me->devmajor, (uintmax_t)me->devminor); } switch (me->filetype) { case AE_IFLNK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=link"); if ((keys & F_SLINK) != 0) { archive_strcat(str, " link="); mtree_quote(str, me->symlink.s); } break; case AE_IFSOCK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=socket"); break; case AE_IFCHR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=char"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%ju,%ju", (uintmax_t)me->rdevmajor, (uintmax_t)me->rdevminor); } break; case AE_IFBLK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=block"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%ju,%ju", (uintmax_t)me->rdevmajor, (uintmax_t)me->rdevminor); } break; case AE_IFDIR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=dir"); break; case AE_IFIFO: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=fifo"); break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((keys & F_TYPE) != 0) archive_strcat(str, " type=file"); if ((keys & F_SIZE) != 0) archive_string_sprintf(str, " size=%jd", (intmax_t)me->size); break; } /* Write a bunch of sum. */ if (me->reg_info) sum_write(str, me->reg_info); archive_strappend_char(str, '\n'); if (mtree->indent || mtree->classic) mtree_indent(mtree); if (mtree->buf.length > 32768) { ret = __archive_write_output( a, mtree->buf.s, mtree->buf.length); archive_string_empty(&mtree->buf); } else ret = ARCHIVE_OK; return (ret); } static int write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n) { struct mtree_writer *mtree = a->format_data; int ret; if (n->parentdir.s) { if (mtree->indent) { int i, pd = mtree->depth * 4; for (i = 0; i < pd; i++) archive_strappend_char(&mtree->buf, ' '); } archive_string_sprintf(&mtree->buf, "# %s/%s\n", n->parentdir.s, n->basename.s); } if (mtree->indent) { archive_string_empty(&mtree->ebuf); archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4); mtree_indent(mtree); } else archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4); if (mtree->buf.length > 32768) { ret = __archive_write_output( a, mtree->buf.s, mtree->buf.length); archive_string_empty(&mtree->buf); } else ret = ARCHIVE_OK; return (ret); } /* * Write mtree entries saved at attr_counter_set_collect() function. */ static int write_mtree_entry_tree(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; struct mtree_entry *np = mtree->root; struct archive_rb_node *n; int ret; do { if (mtree->output_global_set) { /* * Collect attribute information to know which value * is frequently used among the children. */ attr_counter_set_reset(mtree); ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) { struct mtree_entry *e = (struct mtree_entry *)n; if (attr_counter_set_collect(mtree, e) < 0) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } if (!np->dir_info->virtual || mtree->classic) { ret = write_mtree_entry(a, np); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { /* Whenever output_global_set is enabled * output global value(/set keywords) * even if the directory entry is not allowed * to be written because the global values * can be used for the children. */ if (mtree->output_global_set) write_global(mtree); } /* * Output the attribute of all files except directory files. */ mtree->depth++; ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) { struct mtree_entry *e = (struct mtree_entry *)n; if (e->dir_info) mtree_entry_add_child_tail(np, e); else { ret = write_mtree_entry(a, e); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } } mtree->depth--; if (np->dir_info->children.first != NULL) { /* * Descend the tree. */ np = np->dir_info->children.first; if (mtree->indent) mtree->depth++; continue; } else if (mtree->classic) { /* * While printing mtree classic, if there are not * any directory files(except "." and "..") in the * directory, output two dots ".." as returning * the parent directory. */ ret = write_dot_dot_entry(a, np); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } while (np != np->parent) { if (np->dir_info->chnext == NULL) { /* * Ascend the tree; go back to the parent. */ if (mtree->indent) mtree->depth--; if (mtree->classic) { ret = write_dot_dot_entry(a, np->parent); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } np = np->parent; } else { /* * Switch to next mtree entry in the directory. */ np = np->dir_info->chnext; break; } } } while (np != np->parent); return (ARCHIVE_OK); } static int archive_write_mtree_finish_entry(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; struct mtree_entry *me; if ((me = mtree->mtree_entry) == NULL) return (ARCHIVE_OK); mtree->mtree_entry = NULL; if (me->reg_info) sum_final(mtree, me->reg_info); return (ARCHIVE_OK); } static int archive_write_mtree_close(struct archive_write *a) { struct mtree_writer *mtree= a->format_data; int ret; if (mtree->root != NULL) { ret = write_mtree_entry_tree(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } archive_write_set_bytes_in_last_block(&a->archive, 1); return __archive_write_output(a, mtree->buf.s, mtree->buf.length); } static ssize_t archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n) { struct mtree_writer *mtree= a->format_data; if (n > mtree->entry_bytes_remaining) n = (size_t)mtree->entry_bytes_remaining; mtree->entry_bytes_remaining -= n; /* We don't need to compute a regular file sum */ if (mtree->mtree_entry == NULL) return (n); if (mtree->mtree_entry->filetype == AE_IFREG) sum_update(mtree, buff, n); return (n); } static int archive_write_mtree_free(struct archive_write *a) { struct mtree_writer *mtree= a->format_data; if (mtree == NULL) return (ARCHIVE_OK); /* Make sure we do not leave any entries. */ mtree_entry_register_free(mtree); archive_string_free(&mtree->cur_dirstr); archive_string_free(&mtree->ebuf); archive_string_free(&mtree->buf); attr_counter_set_free(mtree); free(mtree); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_mtree_options(struct archive_write *a, const char *key, const char *value) { struct mtree_writer *mtree= a->format_data; int keybit = 0; switch (key[0]) { case 'a': if (strcmp(key, "all") == 0) keybit = ~0; break; case 'c': if (strcmp(key, "cksum") == 0) keybit = F_CKSUM; break; case 'd': if (strcmp(key, "device") == 0) keybit = F_DEV; else if (strcmp(key, "dironly") == 0) { mtree->dironly = (value != NULL)? 1: 0; return (ARCHIVE_OK); } break; case 'f': if (strcmp(key, "flags") == 0) keybit = F_FLAGS; break; case 'g': if (strcmp(key, "gid") == 0) keybit = F_GID; else if (strcmp(key, "gname") == 0) keybit = F_GNAME; break; case 'i': if (strcmp(key, "indent") == 0) { mtree->indent = (value != NULL)? 1: 0; return (ARCHIVE_OK); } else if (strcmp(key, "inode") == 0) { keybit = F_INO; } break; case 'l': if (strcmp(key, "link") == 0) keybit = F_SLINK; break; case 'm': if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) keybit = F_MD5; if (strcmp(key, "mode") == 0) keybit = F_MODE; break; case 'n': if (strcmp(key, "nlink") == 0) keybit = F_NLINK; break; case 'r': if (strcmp(key, "resdevice") == 0) { keybit = F_RESDEV; } else if (strcmp(key, "ripemd160digest") == 0 || strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) keybit = F_RMD160; break; case 's': if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) keybit = F_SHA1; if (strcmp(key, "sha256") == 0 || strcmp(key, "sha256digest") == 0) keybit = F_SHA256; if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) keybit = F_SHA384; if (strcmp(key, "sha512") == 0 || strcmp(key, "sha512digest") == 0) keybit = F_SHA512; if (strcmp(key, "size") == 0) keybit = F_SIZE; break; case 't': if (strcmp(key, "time") == 0) keybit = F_TIME; else if (strcmp(key, "type") == 0) keybit = F_TYPE; break; case 'u': if (strcmp(key, "uid") == 0) keybit = F_UID; else if (strcmp(key, "uname") == 0) keybit = F_UNAME; else if (strcmp(key, "use-set") == 0) { mtree->output_global_set = (value != NULL)? 1: 0; return (ARCHIVE_OK); } break; } if (keybit != 0) { if (value != NULL) mtree->keys |= keybit; else mtree->keys &= ~keybit; return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_write_set_format_mtree_default(struct archive *_a, const char *fn) { struct archive_write *a = (struct archive_write *)_a; struct mtree_writer *mtree; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn); if (a->format_free != NULL) (a->format_free)(a); if ((mtree = calloc(1, sizeof(*mtree))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } mtree->mtree_entry = NULL; mtree->first = 1; memset(&(mtree->set), 0, sizeof(mtree->set)); mtree->keys = DEFAULT_KEYS; mtree->dironly = 0; mtree->indent = 0; archive_string_init(&mtree->ebuf); archive_string_init(&mtree->buf); mtree_entry_register_init(mtree); a->format_data = mtree; a->format_free = archive_write_mtree_free; a->format_name = "mtree"; a->format_options = archive_write_mtree_options; a->format_write_header = archive_write_mtree_header; a->format_close = archive_write_mtree_close; a->format_write_data = archive_write_mtree_data; a->format_finish_entry = archive_write_mtree_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_MTREE; a->archive.archive_format_name = "mtree"; return (ARCHIVE_OK); } int archive_write_set_format_mtree(struct archive *_a) { return archive_write_set_format_mtree_default(_a, "archive_write_set_format_mtree"); } int archive_write_set_format_mtree_classic(struct archive *_a) { int r; r = archive_write_set_format_mtree_default(_a, "archive_write_set_format_mtree_classic"); if (r == ARCHIVE_OK) { struct archive_write *a = (struct archive_write *)_a; struct mtree_writer *mtree; mtree = (struct mtree_writer *)a->format_data; /* Set to output a mtree archive in classic format. */ mtree->classic = 1; /* Basically, mtree classic format uses '/set' global * value. */ mtree->output_global_set = 1; } return (r); } static void sum_init(struct mtree_writer *mtree) { mtree->compute_sum = 0; if (mtree->keys & F_CKSUM) { mtree->compute_sum |= F_CKSUM; mtree->crc = 0; mtree->crc_len = 0; } #ifdef ARCHIVE_HAS_MD5 if (mtree->keys & F_MD5) { if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK) mtree->compute_sum |= F_MD5; else mtree->keys &= ~F_MD5;/* Not supported. */ } #endif #ifdef ARCHIVE_HAS_RMD160 if (mtree->keys & F_RMD160) { if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK) mtree->compute_sum |= F_RMD160; else mtree->keys &= ~F_RMD160;/* Not supported. */ } #endif #ifdef ARCHIVE_HAS_SHA1 if (mtree->keys & F_SHA1) { if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK) mtree->compute_sum |= F_SHA1; else mtree->keys &= ~F_SHA1;/* Not supported. */ } #endif #ifdef ARCHIVE_HAS_SHA256 if (mtree->keys & F_SHA256) { if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK) mtree->compute_sum |= F_SHA256; else mtree->keys &= ~F_SHA256;/* Not supported. */ } #endif #ifdef ARCHIVE_HAS_SHA384 if (mtree->keys & F_SHA384) { if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK) mtree->compute_sum |= F_SHA384; else mtree->keys &= ~F_SHA384;/* Not supported. */ } #endif #ifdef ARCHIVE_HAS_SHA512 if (mtree->keys & F_SHA512) { if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK) mtree->compute_sum |= F_SHA512; else mtree->keys &= ~F_SHA512;/* Not supported. */ } #endif } static void sum_update(struct mtree_writer *mtree, const void *buff, size_t n) { if (mtree->compute_sum & F_CKSUM) { /* * Compute a POSIX 1003.2 checksum */ const unsigned char *p; size_t nn; for (nn = n, p = buff; nn--; ++p) COMPUTE_CRC(mtree->crc, *p); mtree->crc_len += n; } #ifdef ARCHIVE_HAS_MD5 if (mtree->compute_sum & F_MD5) archive_md5_update(&mtree->md5ctx, buff, n); #endif #ifdef ARCHIVE_HAS_RMD160 if (mtree->compute_sum & F_RMD160) archive_rmd160_update(&mtree->rmd160ctx, buff, n); #endif #ifdef ARCHIVE_HAS_SHA1 if (mtree->compute_sum & F_SHA1) archive_sha1_update(&mtree->sha1ctx, buff, n); #endif #ifdef ARCHIVE_HAS_SHA256 if (mtree->compute_sum & F_SHA256) archive_sha256_update(&mtree->sha256ctx, buff, n); #endif #ifdef ARCHIVE_HAS_SHA384 if (mtree->compute_sum & F_SHA384) archive_sha384_update(&mtree->sha384ctx, buff, n); #endif #ifdef ARCHIVE_HAS_SHA512 if (mtree->compute_sum & F_SHA512) archive_sha512_update(&mtree->sha512ctx, buff, n); #endif } static void sum_final(struct mtree_writer *mtree, struct reg_info *reg) { if (mtree->compute_sum & F_CKSUM) { uint64_t len; /* Include the length of the file. */ for (len = mtree->crc_len; len != 0; len >>= 8) COMPUTE_CRC(mtree->crc, len & 0xff); reg->crc = ~mtree->crc; } #ifdef ARCHIVE_HAS_MD5 if (mtree->compute_sum & F_MD5) - archive_md5_final(&mtree->md5ctx, reg->buf_md5); + archive_md5_final(&mtree->md5ctx, reg->digest.md5); #endif #ifdef ARCHIVE_HAS_RMD160 if (mtree->compute_sum & F_RMD160) - archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160); + archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160); #endif #ifdef ARCHIVE_HAS_SHA1 if (mtree->compute_sum & F_SHA1) - archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1); + archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1); #endif #ifdef ARCHIVE_HAS_SHA256 if (mtree->compute_sum & F_SHA256) - archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256); + archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256); #endif #ifdef ARCHIVE_HAS_SHA384 if (mtree->compute_sum & F_SHA384) - archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384); + archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384); #endif #ifdef ARCHIVE_HAS_SHA512 if (mtree->compute_sum & F_SHA512) - archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512); + archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512); #endif /* Save what types of sum are computed. */ reg->compute_sum = mtree->compute_sum; } #if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \ defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \ defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512) static void strappend_bin(struct archive_string *s, const unsigned char *bin, int n) { static const char hex[] = "0123456789abcdef"; int i; for (i = 0; i < n; i++) { archive_strappend_char(s, hex[bin[i] >> 4]); archive_strappend_char(s, hex[bin[i] & 0x0f]); } } #endif static void sum_write(struct archive_string *str, struct reg_info *reg) { if (reg->compute_sum & F_CKSUM) { archive_string_sprintf(str, " cksum=%ju", (uintmax_t)reg->crc); } + +#define append_digest(_s, _r, _t) \ + strappend_bin(_s, _r->digest._t, sizeof(_r->digest._t)) + #ifdef ARCHIVE_HAS_MD5 if (reg->compute_sum & F_MD5) { archive_strcat(str, " md5digest="); - strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5)); + append_digest(str, reg, md5); } #endif #ifdef ARCHIVE_HAS_RMD160 if (reg->compute_sum & F_RMD160) { archive_strcat(str, " rmd160digest="); - strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160)); + append_digest(str, reg, rmd160); } #endif #ifdef ARCHIVE_HAS_SHA1 if (reg->compute_sum & F_SHA1) { archive_strcat(str, " sha1digest="); - strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1)); + append_digest(str, reg, sha1); } #endif #ifdef ARCHIVE_HAS_SHA256 if (reg->compute_sum & F_SHA256) { archive_strcat(str, " sha256digest="); - strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256)); + append_digest(str, reg, sha256); } #endif #ifdef ARCHIVE_HAS_SHA384 if (reg->compute_sum & F_SHA384) { archive_strcat(str, " sha384digest="); - strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384)); + append_digest(str, reg, sha384); } #endif #ifdef ARCHIVE_HAS_SHA512 if (reg->compute_sum & F_SHA512) { archive_strcat(str, " sha512digest="); - strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512)); + append_digest(str, reg, sha512); } #endif +#undef append_digest } static int mtree_entry_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct mtree_entry *e1 = (const struct mtree_entry *)n1; const struct mtree_entry *e2 = (const struct mtree_entry *)n2; return (strcmp(e2->basename.s, e1->basename.s)); } static int mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key) { const struct mtree_entry *e = (const struct mtree_entry *)n; return (strcmp((const char *)key, e->basename.s)); } #if defined(_WIN32) || defined(__CYGWIN__) static int cleanup_backslash_1(char *p) { int mb, dos; mb = dos = 0; while (*p) { if (*(unsigned char *)p > 127) mb = 1; if (*p == '\\') { /* If we have not met any multi-byte characters, * we can replace '\' with '/'. */ if (!mb) *p = '/'; dos = 1; } p++; } if (!mb || !dos) return (0); return (-1); } static void cleanup_backslash_2(wchar_t *p) { /* Convert a path-separator from '\' to '/' */ while (*p != L'\0') { if (*p == L'\\') *p = L'/'; p++; } } #endif /* * Generate a parent directory name and a base name from a pathname. */ static int mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, struct archive_entry *entry) { const char *pathname; char *p, *dirname, *slash; size_t len; int ret = ARCHIVE_OK; archive_strcpy(&file->pathname, archive_entry_pathname(entry)); #if defined(_WIN32) || defined(__CYGWIN__) /* * Convert a path-separator from '\' to '/' */ if (cleanup_backslash_1(file->pathname.s) != 0) { const wchar_t *wp = archive_entry_pathname_w(entry); struct archive_wstring ws; if (wp != NULL) { int r; archive_string_init(&ws); archive_wstrcpy(&ws, wp); cleanup_backslash_2(ws.s); archive_string_empty(&(file->pathname)); r = archive_string_append_from_wcs(&(file->pathname), ws.s, ws.length); archive_wstring_free(&ws); if (r < 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } } #else (void)a; /* UNUSED */ #endif pathname = file->pathname.s; if (strcmp(pathname, ".") == 0) { archive_strcpy(&file->basename, "."); return (ARCHIVE_OK); } archive_strcpy(&(file->parentdir), pathname); len = file->parentdir.length; p = dirname = file->parentdir.s; /* * Remove leading '/' and '../' elements */ while (*p) { if (p[0] == '/') { p++; len--; } else if (p[0] != '.') break; else if (p[1] == '.' && p[2] == '/') { p += 3; len -= 3; } else break; } if (p != dirname) { memmove(dirname, p, len+1); p = dirname; } /* * Remove "/","/." and "/.." elements from tail. */ while (len > 0) { size_t ll = len; if (len > 0 && p[len-1] == '/') { p[len-1] = '\0'; len--; } if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { p[len-2] = '\0'; len -= 2; } if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && p[len-1] == '.') { p[len-3] = '\0'; len -= 3; } if (ll == len) break; } while (*p) { if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ memmove(p, p+1, strlen(p+1) + 1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ memmove(p, p+2, strlen(p+2) + 1); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' */ char *rp = p -1; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { strcpy(rp, p+3); p = rp; } else { strcpy(dirname, p+4); p = dirname; } } else p++; } else p++; } p = dirname; len = strlen(p); /* * Add "./" prefix. * NOTE: If the pathname does not have a path separator, we have * to add "./" to the head of the pathname because mtree reader * will suppose that it is v1(a.k.a classic) mtree format and * change the directory unexpectedly and so it will make a wrong * path. */ if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) { struct archive_string as; archive_string_init(&as); archive_strcpy(&as, "./"); archive_strncat(&as, p, len); archive_string_empty(&file->parentdir); archive_string_concat(&file->parentdir, &as); archive_string_free(&as); p = file->parentdir.s; len = archive_strlen(&file->parentdir); } /* * Find out the position which points the last position of * path separator('/'). */ slash = NULL; for (; *p != '\0'; p++) { if (*p == '/') slash = p; } if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; archive_string_copy(&(file->basename), &(file->parentdir)); archive_string_empty(&(file->parentdir)); *file->parentdir.s = '\0'; return (ret); } /* Make a basename from file->parentdir.s and slash */ *slash = '\0'; file->parentdir.length = slash - file->parentdir.s; archive_strcpy(&(file->basename), slash + 1); return (ret); } static int mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname, struct mtree_entry **m_entry) { struct archive_entry *entry; struct mtree_entry *file; int r; entry = archive_entry_new(); if (entry == NULL) { *m_entry = NULL; archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } archive_entry_copy_pathname(entry, pathname); archive_entry_set_mode(entry, AE_IFDIR | 0755); archive_entry_set_mtime(entry, time(NULL), 0); r = mtree_entry_new(a, entry, &file); archive_entry_free(entry); if (r < ARCHIVE_WARN) { *m_entry = NULL; archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } file->dir_info->virtual = 1; *m_entry = file; return (ARCHIVE_OK); } static void mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file) { file->next = NULL; *mtree->file_list.last = file; mtree->file_list.last = &(file->next); } static void mtree_entry_register_init(struct mtree_writer *mtree) { mtree->file_list.first = NULL; mtree->file_list.last = &(mtree->file_list.first); } static void mtree_entry_register_free(struct mtree_writer *mtree) { struct mtree_entry *file, *file_next; file = mtree->file_list.first; while (file != NULL) { file_next = file->next; mtree_entry_free(file); file = file_next; } } static int mtree_entry_add_child_tail(struct mtree_entry *parent, struct mtree_entry *child) { child->dir_info->chnext = NULL; *parent->dir_info->children.last = child; parent->dir_info->children.last = &(child->dir_info->chnext); return (1); } /* * Find a entry from a parent entry with the name. */ static struct mtree_entry * mtree_entry_find_child(struct mtree_entry *parent, const char *child_name) { struct mtree_entry *np; if (parent == NULL) return (NULL); np = (struct mtree_entry *)__archive_rb_tree_find_node( &(parent->dir_info->rbtree), child_name); return (np); } static int get_path_component(char *name, size_t n, const char *fn) { char *p; size_t l; p = strchr(fn, '/'); if (p == NULL) { if ((l = strlen(fn)) == 0) return (0); } else l = p - fn; if (l > n -1) return (-1); memcpy(name, fn, l); name[l] = '\0'; return ((int)l); } /* * Add a new entry into the tree. */ static int mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct mtree_writer *mtree = (struct mtree_writer *)a->format_data; struct mtree_entry *dent, *file, *np; const char *fn, *p; int l, r; file = *filep; if (file->parentdir.length == 0 && file->basename.length == 1 && file->basename.s[0] == '.') { file->parent = file; if (mtree->root != NULL) { np = mtree->root; goto same_entry; } mtree->root = file; mtree_entry_register_add(mtree, file); return (ARCHIVE_OK); } if (file->parentdir.length == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal programming error " "in generating canonical name for %s", file->pathname.s); return (ARCHIVE_FAILED); } fn = p = file->parentdir.s; /* * If the path of the parent directory of `file' entry is * the same as the path of `cur_dirent', add `file' entry to * `cur_dirent'. */ if (archive_strlen(&(mtree->cur_dirstr)) == archive_strlen(&(file->parentdir)) && strcmp(mtree->cur_dirstr.s, fn) == 0) { if (!__archive_rb_tree_insert_node( &(mtree->cur_dirent->dir_info->rbtree), (struct archive_rb_node *)file)) { /* There is the same name in the tree. */ np = (struct mtree_entry *)__archive_rb_tree_find_node( &(mtree->cur_dirent->dir_info->rbtree), file->basename.s); goto same_entry; } file->parent = mtree->cur_dirent; mtree_entry_register_add(mtree, file); return (ARCHIVE_OK); } dent = mtree->root; for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) { np = NULL; break; } if (l < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); return (ARCHIVE_FATAL); } if (l == 1 && name[0] == '.' && dent != NULL && dent == mtree->root) { fn += l; if (fn[0] == '/') fn++; continue; } np = mtree_entry_find_child(dent, name); if (np == NULL || fn[0] == '\0') break; /* Find next sub directory. */ if (!np->dir_info) { /* NOT Directory! */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "`%s' is not directory, we cannot insert `%s' ", np->pathname.s, file->pathname.s); return (ARCHIVE_FAILED); } fn += l; if (fn[0] == '/') fn++; dent = np; } if (np == NULL) { /* * Create virtual parent directories. */ while (fn[0] != '\0') { struct mtree_entry *vp; struct archive_string as; archive_string_init(&as); archive_strncat(&as, p, fn - p + l); if (as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } r = mtree_entry_create_virtual_dir(a, as.s, &vp); archive_string_free(&as); if (r < ARCHIVE_WARN) return (r); if (strcmp(vp->pathname.s, ".") == 0) { vp->parent = vp; mtree->root = vp; } else { __archive_rb_tree_insert_node( &(dent->dir_info->rbtree), (struct archive_rb_node *)vp); vp->parent = dent; } mtree_entry_register_add(mtree, vp); np = vp; fn += l; if (fn[0] == '/') fn++; l = get_path_component(name, sizeof(name), fn); if (l < 0) { archive_string_free(&as); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); return (ARCHIVE_FATAL); } dent = np; } /* Found out the parent directory where `file' can be * inserted. */ mtree->cur_dirent = dent; archive_string_empty(&(mtree->cur_dirstr)); archive_string_ensure(&(mtree->cur_dirstr), archive_strlen(&(dent->parentdir)) + archive_strlen(&(dent->basename)) + 2); if (archive_strlen(&(dent->parentdir)) + archive_strlen(&(dent->basename)) == 0) mtree->cur_dirstr.s[0] = 0; else { if (archive_strlen(&(dent->parentdir)) > 0) { archive_string_copy(&(mtree->cur_dirstr), &(dent->parentdir)); archive_strappend_char( &(mtree->cur_dirstr), '/'); } archive_string_concat(&(mtree->cur_dirstr), &(dent->basename)); } if (!__archive_rb_tree_insert_node( &(dent->dir_info->rbtree), (struct archive_rb_node *)file)) { np = (struct mtree_entry *)__archive_rb_tree_find_node( &(dent->dir_info->rbtree), file->basename.s); goto same_entry; } file->parent = dent; mtree_entry_register_add(mtree, file); return (ARCHIVE_OK); } same_entry: /* * We have already has the entry the filename of which is * the same. */ r = mtree_entry_exchange_same_entry(a, np, file); if (r < ARCHIVE_WARN) return (r); if (np->dir_info) np->dir_info->virtual = 0; *filep = np; mtree_entry_free(file); return (ARCHIVE_WARN); } static int mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np, struct mtree_entry *file) { if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found duplicate entries `%s' and its file type is " "different", np->pathname.s); return (ARCHIVE_FAILED); } /* Update the existent mtree entry's attributes by the new one's. */ archive_string_empty(&np->symlink); archive_string_concat(&np->symlink, &file->symlink); archive_string_empty(&np->uname); archive_string_concat(&np->uname, &file->uname); archive_string_empty(&np->gname); archive_string_concat(&np->gname, &file->gname); archive_string_empty(&np->fflags_text); archive_string_concat(&np->fflags_text, &file->fflags_text); np->nlink = file->nlink; np->filetype = file->filetype; np->mode = file->mode; np->size = file->size; np->uid = file->uid; np->gid = file->gid; np->fflags_set = file->fflags_set; np->fflags_clear = file->fflags_clear; np->mtime = file->mtime; np->mtime_nsec = file->mtime_nsec; np->rdevmajor = file->rdevmajor; np->rdevminor = file->rdevminor; np->devmajor = file->devmajor; np->devminor = file->devminor; np->ino = file->ino; return (ARCHIVE_WARN); } Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_xar.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_xar.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_xar.c (revision 368707) @@ -1,3259 +1,3259 @@ /*- * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #if HAVE_LIBXML_XMLWRITER_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #if HAVE_LZMA_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_digest_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_string.h" #include "archive_write_private.h" /* * Differences to xar utility. * - Subdocument is not supported yet. * - ACL is not supported yet. * - When writing an XML element , * which is a file type a symbolic link is referencing is always marked * as "broken". Xar utility uses stat(2) to get the file type, but, in * libarchive format writer, we should not use it; if it is needed, we * should get about it at archive_read_disk.c. * - It is possible to appear both and elements. * Xar utility generates on BSD platform and on Linux * platform. * */ #if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\ LIBXML_VERSION >= 20703) ||\ !defined(HAVE_ZLIB_H) || \ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) /* * xar needs several external libraries. * o libxml2 * o openssl or MD5/SHA1 hash function * o zlib * o bzlib2 (option) * o liblzma (option) */ int archive_write_set_format_xar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Xar not supported on this platform"); return (ARCHIVE_WARN); } #else /* Support xar format */ /*#define DEBUG_PRINT_TOC 1 */ #define BAD_CAST_CONST (const xmlChar *) #define HEADER_MAGIC 0x78617221 #define HEADER_SIZE 28 #define HEADER_VERSION 1 enum sumalg { CKSUM_NONE = 0, CKSUM_SHA1 = 1, CKSUM_MD5 = 2 }; #define MD5_SIZE 16 #define SHA1_SIZE 20 #define MAX_SUM_SIZE 20 #define MD5_NAME "md5" #define SHA1_NAME "sha1" enum enctype { NONE, GZIP, BZIP2, LZMA, XZ, }; struct chksumwork { enum sumalg alg; #ifdef ARCHIVE_HAS_MD5 archive_md5_ctx md5ctx; #endif #ifdef ARCHIVE_HAS_SHA1 archive_sha1_ctx sha1ctx; #endif }; enum la_zaction { ARCHIVE_Z_FINISH, ARCHIVE_Z_RUN }; /* * Universal zstream. */ struct la_zstream { const unsigned char *next_in; size_t avail_in; uint64_t total_in; unsigned char *next_out; size_t avail_out; uint64_t total_out; int valid; void *real_stream; int (*code) (struct archive *a, struct la_zstream *lastrm, enum la_zaction action); int (*end)(struct archive *a, struct la_zstream *lastrm); }; struct chksumval { enum sumalg alg; size_t len; unsigned char val[MAX_SUM_SIZE]; }; struct heap_data { int id; struct heap_data *next; uint64_t temp_offset; uint64_t length; /* archived size. */ uint64_t size; /* extracted size. */ enum enctype compression; struct chksumval a_sum; /* archived checksum. */ struct chksumval e_sum; /* extracted checksum. */ }; struct file { struct archive_rb_node rbnode; int id; struct archive_entry *entry; struct archive_rb_tree rbtree; struct file *next; struct file *chnext; struct file *hlnext; /* For hardlinked files. * Use only when archive_entry_nlink() > 1 */ struct file *hardlink_target; struct file *parent; /* parent directory entry */ /* * To manage sub directory files. * We use 'chnext' (a member of struct file) to chain. */ struct { struct file *first; struct file **last; } children; /* For making a directory tree. */ struct archive_string parentdir; struct archive_string basename; struct archive_string symlink; int ea_idx; struct { struct heap_data *first; struct heap_data **last; } xattr; struct heap_data data; struct archive_string script; signed int virtual:1; signed int dir:1; }; struct hardlink { struct archive_rb_node rbnode; int nlink; struct { struct file *first; struct file **last; } file_list; }; struct xar { int temp_fd; uint64_t temp_offset; int file_idx; struct file *root; struct file *cur_dirent; struct archive_string cur_dirstr; struct file *cur_file; uint64_t bytes_remaining; struct archive_string tstr; struct archive_string vstr; enum sumalg opt_toc_sumalg; enum sumalg opt_sumalg; enum enctype opt_compression; int opt_compression_level; uint32_t opt_threads; struct chksumwork a_sumwrk; /* archived checksum. */ struct chksumwork e_sumwrk; /* extracted checksum. */ struct la_zstream stream; struct archive_string_conv *sconv; /* * Compressed data buffer. */ unsigned char wbuff[1024 * 64]; size_t wbuff_remaining; struct heap_data toc; /* * The list of all file entries is used to manage struct file * objects. * We use 'next' (a member of struct file) to chain. */ struct { struct file *first; struct file **last; } file_list; /* * The list of hard-linked file entries. * We use 'hlnext' (a member of struct file) to chain. */ struct archive_rb_tree hardlink_rbtree; }; static int xar_options(struct archive_write *, const char *, const char *); static int xar_write_header(struct archive_write *, struct archive_entry *); static ssize_t xar_write_data(struct archive_write *, const void *, size_t); static int xar_finish_entry(struct archive_write *); static int xar_close(struct archive_write *); static int xar_free(struct archive_write *); static struct file *file_new(struct archive_write *a, struct archive_entry *); static void file_free(struct file *); static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *, const char *); static int file_add_child_tail(struct file *, struct file *); static struct file *file_find_child(struct file *, const char *); static int file_gen_utility_names(struct archive_write *, struct file *); static int get_path_component(char *, int, const char *); static int file_tree(struct archive_write *, struct file **); static void file_register(struct xar *, struct file *); static void file_init_register(struct xar *); static void file_free_register(struct xar *); static int file_register_hardlink(struct archive_write *, struct file *); static void file_connect_hardlink_files(struct xar *); static void file_init_hardlinks(struct xar *); static void file_free_hardlinks(struct xar *); static void checksum_init(struct chksumwork *, enum sumalg); static void checksum_update(struct chksumwork *, const void *, size_t); static void checksum_final(struct chksumwork *, struct chksumval *); static int compression_init_encoder_gzip(struct archive *, struct la_zstream *, int, int); static int compression_code_gzip(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_gzip(struct archive *, struct la_zstream *); static int compression_init_encoder_bzip2(struct archive *, struct la_zstream *, int); #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) static int compression_code_bzip2(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_bzip2(struct archive *, struct la_zstream *); #endif static int compression_init_encoder_lzma(struct archive *, struct la_zstream *, int); static int compression_init_encoder_xz(struct archive *, struct la_zstream *, int, int); #if defined(HAVE_LZMA_H) static int compression_code_lzma(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_lzma(struct archive *, struct la_zstream *); #endif static int xar_compression_init_encoder(struct archive_write *); static int compression_code(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end(struct archive *, struct la_zstream *); static int save_xattrs(struct archive_write *, struct file *); static int getalgsize(enum sumalg); static const char *getalgname(enum sumalg); int archive_write_set_format_xar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct xar *xar; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_xar"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); xar = calloc(1, sizeof(*xar)); if (xar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate xar data"); return (ARCHIVE_FATAL); } xar->temp_fd = -1; file_init_register(xar); file_init_hardlinks(xar); archive_string_init(&(xar->tstr)); archive_string_init(&(xar->vstr)); /* * Create the root directory. */ xar->root = file_create_virtual_dir(a, xar, ""); if (xar->root == NULL) { free(xar); archive_set_error(&a->archive, ENOMEM, "Can't allocate xar data"); return (ARCHIVE_FATAL); } xar->root->parent = xar->root; file_register(xar, xar->root); xar->cur_dirent = xar->root; archive_string_init(&(xar->cur_dirstr)); archive_string_ensure(&(xar->cur_dirstr), 1); xar->cur_dirstr.s[0] = 0; /* * Initialize option. */ /* Set default checksum type. */ xar->opt_toc_sumalg = CKSUM_SHA1; xar->opt_sumalg = CKSUM_SHA1; /* Set default compression type, level, and number of threads. */ xar->opt_compression = GZIP; xar->opt_compression_level = 6; xar->opt_threads = 1; a->format_data = xar; a->format_name = "xar"; a->format_options = xar_options; a->format_write_header = xar_write_header; a->format_write_data = xar_write_data; a->format_finish_entry = xar_finish_entry; a->format_close = xar_close; a->format_free = xar_free; a->archive.archive_format = ARCHIVE_FORMAT_XAR; a->archive.archive_format_name = "xar"; return (ARCHIVE_OK); } static int xar_options(struct archive_write *a, const char *key, const char *value) { struct xar *xar; xar = (struct xar *)a->format_data; if (strcmp(key, "checksum") == 0) { if (value == NULL) xar->opt_sumalg = CKSUM_NONE; else if (strcmp(value, "none") == 0) xar->opt_sumalg = CKSUM_NONE; else if (strcmp(value, "sha1") == 0) xar->opt_sumalg = CKSUM_SHA1; else if (strcmp(value, "md5") == 0) xar->opt_sumalg = CKSUM_MD5; else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unknown checksum name: `%s'", value); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } if (strcmp(key, "compression") == 0) { const char *name = NULL; if (value == NULL) xar->opt_compression = NONE; else if (strcmp(value, "none") == 0) xar->opt_compression = NONE; else if (strcmp(value, "gzip") == 0) xar->opt_compression = GZIP; else if (strcmp(value, "bzip2") == 0) #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) xar->opt_compression = BZIP2; #else name = "bzip2"; #endif else if (strcmp(value, "lzma") == 0) #if HAVE_LZMA_H xar->opt_compression = LZMA; #else name = "lzma"; #endif else if (strcmp(value, "xz") == 0) #if HAVE_LZMA_H xar->opt_compression = XZ; #else name = "xz"; #endif else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unknown compression name: `%s'", value); return (ARCHIVE_FAILED); } if (name != NULL) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "`%s' compression not supported " "on this platform", name); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || value[1] != '\0') { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Illegal value `%s'", value); return (ARCHIVE_FAILED); } xar->opt_compression_level = value[0] - '0'; return (ARCHIVE_OK); } if (strcmp(key, "toc-checksum") == 0) { if (value == NULL) xar->opt_toc_sumalg = CKSUM_NONE; else if (strcmp(value, "none") == 0) xar->opt_toc_sumalg = CKSUM_NONE; else if (strcmp(value, "sha1") == 0) xar->opt_toc_sumalg = CKSUM_SHA1; else if (strcmp(value, "md5") == 0) xar->opt_toc_sumalg = CKSUM_MD5; else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unknown checksum name: `%s'", value); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } if (strcmp(key, "threads") == 0) { char *endptr; if (value == NULL) return (ARCHIVE_FAILED); errno = 0; xar->opt_threads = (int)strtoul(value, &endptr, 10); if (errno != 0 || *endptr != '\0') { xar->opt_threads = 1; archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Illegal value `%s'", value); return (ARCHIVE_FAILED); } if (xar->opt_threads == 0) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT xar->opt_threads = lzma_cputhreads(); #else xar->opt_threads = 1; #endif } } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int xar_write_header(struct archive_write *a, struct archive_entry *entry) { struct xar *xar; struct file *file; struct archive_entry *file_entry; int r, r2; xar = (struct xar *)a->format_data; xar->cur_file = NULL; xar->bytes_remaining = 0; if (xar->sconv == NULL) { xar->sconv = archive_string_conversion_to_charset( &a->archive, "UTF-8", 1); if (xar->sconv == NULL) return (ARCHIVE_FATAL); } file = file_new(a, entry); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); return (ARCHIVE_FATAL); } r2 = file_gen_utility_names(a, file); if (r2 < ARCHIVE_WARN) return (r2); /* * Ignore a path which looks like the top of directory name * since we have already made the root directory of an Xar archive. */ if (archive_strlen(&(file->parentdir)) == 0 && archive_strlen(&(file->basename)) == 0) { file_free(file); return (r2); } /* Add entry into tree */ file_entry = file->entry; r = file_tree(a, &file); if (r != ARCHIVE_OK) return (r); /* There is the same file in tree and * the current file is older than the file in tree. * So we don't need the current file data anymore. */ if (file->entry != file_entry) return (r2); if (file->id == 0) file_register(xar, file); /* A virtual file, which is a directory, does not have * any contents and we won't store it into a archive * file other than its name. */ if (file->virtual) return (r2); /* * Prepare to save the contents of the file. */ if (xar->temp_fd == -1) { int algsize; xar->temp_offset = 0; xar->temp_fd = __archive_mktemp(NULL); if (xar->temp_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't create temporary file"); return (ARCHIVE_FATAL); } algsize = getalgsize(xar->opt_toc_sumalg); if (algsize > 0) { if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) { archive_set_error(&(a->archive), errno, "lseek failed"); return (ARCHIVE_FATAL); } xar->temp_offset = algsize; } } if (archive_entry_hardlink(file->entry) == NULL) { r = save_xattrs(a, file); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Non regular files contents are unneeded to be saved to * a temporary file. */ if (archive_entry_filetype(file->entry) != AE_IFREG) return (r2); /* * Set the current file to cur_file to read its contents. */ xar->cur_file = file; if (archive_entry_nlink(file->entry) > 1) { r = file_register_hardlink(a, file); if (r != ARCHIVE_OK) return (r); if (archive_entry_hardlink(file->entry) != NULL) { archive_entry_unset_size(file->entry); return (r2); } } /* Save a offset of current file in temporary file. */ file->data.temp_offset = xar->temp_offset; file->data.size = archive_entry_size(file->entry); file->data.compression = xar->opt_compression; xar->bytes_remaining = archive_entry_size(file->entry); checksum_init(&(xar->a_sumwrk), xar->opt_sumalg); checksum_init(&(xar->e_sumwrk), xar->opt_sumalg); r = xar_compression_init_encoder(a); if (r != ARCHIVE_OK) return (r); else return (r2); } static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { struct xar *xar; const unsigned char *p; ssize_t ws; xar = (struct xar *)a->format_data; p = (const unsigned char *)buff; while (s) { ws = write(xar->temp_fd, p, s); if (ws < 0) { archive_set_error(&(a->archive), errno, "fwrite function failed"); return (ARCHIVE_FATAL); } s -= ws; p += ws; xar->temp_offset += ws; } return (ARCHIVE_OK); } static ssize_t xar_write_data(struct archive_write *a, const void *buff, size_t s) { struct xar *xar; enum la_zaction run; size_t size = 0; size_t rsize; int r; xar = (struct xar *)a->format_data; if (s > xar->bytes_remaining) s = (size_t)xar->bytes_remaining; if (s == 0 || xar->cur_file == NULL) return (0); if (xar->cur_file->data.compression == NONE) { checksum_update(&(xar->e_sumwrk), buff, s); checksum_update(&(xar->a_sumwrk), buff, s); size = rsize = s; } else { xar->stream.next_in = (const unsigned char *)buff; xar->stream.avail_in = s; if (xar->bytes_remaining > s) run = ARCHIVE_Z_RUN; else run = ARCHIVE_Z_FINISH; /* Compress file data. */ for (;;) { r = compression_code(&(a->archive), &(xar->stream), run); if (r != ARCHIVE_OK && r != ARCHIVE_EOF) return (ARCHIVE_FATAL); if (xar->stream.avail_out == 0 || run == ARCHIVE_Z_FINISH) { size = sizeof(xar->wbuff) - xar->stream.avail_out; checksum_update(&(xar->a_sumwrk), xar->wbuff, size); xar->cur_file->data.length += size; if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (r == ARCHIVE_OK) { /* Output buffer was full */ xar->stream.next_out = xar->wbuff; xar->stream.avail_out = sizeof(xar->wbuff); } else { /* ARCHIVE_EOF - We are done */ break; } } else { /* Compressor wants more input */ break; } } rsize = s - xar->stream.avail_in; checksum_update(&(xar->e_sumwrk), buff, rsize); } #if !defined(_WIN32) || defined(__CYGWIN__) if (xar->bytes_remaining == (uint64_t)archive_entry_size(xar->cur_file->entry)) { /* * Get the path of a shell script if so. */ const unsigned char *b = (const unsigned char *)buff; archive_string_empty(&(xar->cur_file->script)); if (rsize > 2 && b[0] == '#' && b[1] == '!') { size_t i, end, off; off = 2; if (b[off] == ' ') off++; #ifdef PATH_MAX if ((rsize - off) > PATH_MAX) end = off + PATH_MAX; else #endif end = rsize; /* Find the end of a script path. */ for (i = off; i < end && b[i] != '\0' && b[i] != '\n' && b[i] != '\r' && b[i] != ' ' && b[i] != '\t'; i++) ; archive_strncpy(&(xar->cur_file->script), b + off, i - off); } } #endif if (xar->cur_file->data.compression == NONE) { if (write_to_temp(a, buff, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); xar->cur_file->data.length += size; } xar->bytes_remaining -= rsize; return (rsize); } static int xar_finish_entry(struct archive_write *a) { struct xar *xar; struct file *file; size_t s; ssize_t w; xar = (struct xar *)a->format_data; if (xar->cur_file == NULL) return (ARCHIVE_OK); while (xar->bytes_remaining > 0) { s = (size_t)xar->bytes_remaining; if (s > a->null_length) s = a->null_length; w = xar_write_data(a, a->nulls, s); if (w > 0) xar->bytes_remaining -= w; else return (w); } file = xar->cur_file; checksum_final(&(xar->e_sumwrk), &(file->data.e_sum)); checksum_final(&(xar->a_sumwrk), &(file->data.a_sum)); xar->cur_file = NULL; return (ARCHIVE_OK); } static int xmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer, const char *key, const char *value, const char *attrkey, const char *attrvalue) { int r; r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key)); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } if (attrkey != NULL && attrvalue != NULL) { r = xmlTextWriterWriteAttribute(writer, BAD_CAST_CONST(attrkey), BAD_CAST_CONST(attrvalue)); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteAttribute() failed: %d", r); return (ARCHIVE_FATAL); } } if (value != NULL) { r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value)); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteString() failed: %d", r); return (ARCHIVE_FATAL); } } r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static int xmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer, const char *key, const char *value) { int r; if (value == NULL) return (ARCHIVE_OK); r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key)); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } if (value != NULL) { r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value)); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteString() failed: %d", r); return (ARCHIVE_FATAL); } } r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static int xmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer, const char *key, const char *fmt, ...) { struct xar *xar; va_list ap; xar = (struct xar *)a->format_data; va_start(ap, fmt); archive_string_empty(&xar->vstr); archive_string_vsprintf(&xar->vstr, fmt, ap); va_end(ap); return (xmlwrite_string(a, writer, key, xar->vstr.s)); } static int xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer, const char *key, time_t t, int z) { char timestr[100]; struct tm tm; #if defined(HAVE__GMTIME64_S) __time64_t tmptime; #endif #if defined(HAVE_GMTIME_R) gmtime_r(&t, &tm); #elif defined(HAVE__GMTIME64_S) tmptime = t; _gmtime64_s(&tm, &tmptime); #else memcpy(&tm, gmtime(&t), sizeof(tm)); #endif memset(×tr, 0, sizeof(timestr)); /* Do not use %F and %T for portability. */ strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm); if (z) strcat(timestr, "Z"); return (xmlwrite_string(a, writer, key, timestr)); } static int xmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer, const char *key, mode_t mode) { char ms[5]; ms[0] = '0'; ms[1] = '0' + ((mode >> 6) & 07); ms[2] = '0' + ((mode >> 3) & 07); ms[3] = '0' + (mode & 07); ms[4] = '\0'; return (xmlwrite_string(a, writer, key, ms)); } static int xmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer, const char *key, struct chksumval *sum) { const char *algname; int algsize; char buff[MAX_SUM_SIZE*2 + 1]; char *p; unsigned char *s; int i, r; if (sum->len > 0) { algname = getalgname(sum->alg); algsize = getalgsize(sum->alg); if (algname != NULL) { const char *hex = "0123456789abcdef"; p = buff; s = sum->val; for (i = 0; i < algsize; i++) { *p++ = hex[(*s >> 4)]; *p++ = hex[(*s & 0x0f)]; s++; } *p = '\0'; r = xmlwrite_string_attr(a, writer, key, buff, "style", algname); if (r < 0) return (ARCHIVE_FATAL); } } return (ARCHIVE_OK); } static int xmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer, struct heap_data *heap) { const char *encname; int r; r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size); if (r < 0) return (ARCHIVE_FATAL); switch (heap->compression) { case GZIP: encname = "application/x-gzip"; break; case BZIP2: encname = "application/x-bzip2"; break; case LZMA: encname = "application/x-lzma"; break; case XZ: encname = "application/x-xz"; break; default: encname = "application/octet-stream"; break; } r = xmlwrite_string_attr(a, writer, "encoding", NULL, "style", encname); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum)); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum)); if (r < 0) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } /* * xar utility records fflags as following xml elements: * * * ..... * * or * * * ..... * * If xar is running on BSD platform, records ..; * if xar is running on linux platform, records ..; * otherwise does not record. * * Our implements records both and if it's necessary. */ static int make_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer, const char *element, const char *fflags_text) { static const struct flagentry { const char *name; const char *xarname; } flagbsd[] = { { "sappnd", "SystemAppend"}, { "sappend", "SystemAppend"}, { "arch", "SystemArchived"}, { "archived", "SystemArchived"}, { "schg", "SystemImmutable"}, { "schange", "SystemImmutable"}, { "simmutable", "SystemImmutable"}, { "nosunlnk", "SystemNoUnlink"}, { "nosunlink", "SystemNoUnlink"}, { "snapshot", "SystemSnapshot"}, { "uappnd", "UserAppend"}, { "uappend", "UserAppend"}, { "uchg", "UserImmutable"}, { "uchange", "UserImmutable"}, { "uimmutable", "UserImmutable"}, { "nodump", "UserNoDump"}, { "noopaque", "UserOpaque"}, { "nouunlnk", "UserNoUnlink"}, { "nouunlink", "UserNoUnlink"}, { NULL, NULL} }, flagext2[] = { { "sappnd", "AppendOnly"}, { "sappend", "AppendOnly"}, { "schg", "Immutable"}, { "schange", "Immutable"}, { "simmutable", "Immutable"}, { "nodump", "NoDump"}, { "nouunlnk", "Undelete"}, { "nouunlink", "Undelete"}, { "btree", "BTree"}, { "comperr", "CompError"}, { "compress", "Compress"}, { "noatime", "NoAtime"}, { "compdirty", "CompDirty"}, { "comprblk", "CompBlock"}, { "dirsync", "DirSync"}, { "hashidx", "HashIndexed"}, { "imagic", "iMagic"}, { "journal", "Journaled"}, { "securedeletion", "SecureDeletion"}, { "sync", "Synchronous"}, { "notail", "NoTail"}, { "topdir", "TopDir"}, { "reserved", "Reserved"}, { NULL, NULL} }; const struct flagentry *fe, *flagentry; #define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd)) const struct flagentry *avail[FLAGENTRY_MAXSIZE]; const char *p; int i, n, r; if (strcmp(element, "ext2") == 0) flagentry = flagext2; else flagentry = flagbsd; n = 0; p = fflags_text; do { const char *cp; cp = strchr(p, ','); if (cp == NULL) cp = p + strlen(p); for (fe = flagentry; fe->name != NULL; fe++) { if (fe->name[cp - p] != '\0' || p[0] != fe->name[0]) continue; if (strncmp(p, fe->name, cp - p) == 0) { avail[n++] = fe; break; } } if (*cp == ',') p = cp + 1; else p = NULL; } while (p != NULL); if (n > 0) { r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(element)); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } for (i = 0; i < n; i++) { r = xmlwrite_string(a, writer, avail[i]->xarname, NULL); if (r != ARCHIVE_OK) return (r); } r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } } return (ARCHIVE_OK); } static int make_file_entry(struct archive_write *a, xmlTextWriterPtr writer, struct file *file) { struct xar *xar; const char *filetype, *filelink, *fflags; struct archive_string linkto; struct heap_data *heap; unsigned char *tmp; const char *p; size_t len; int r, r2, l, ll; xar = (struct xar *)a->format_data; r2 = ARCHIVE_OK; /* * Make a file name entry, "". */ l = ll = archive_strlen(&(file->basename)); tmp = malloc(l); if (tmp == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll); free(tmp); if (r < 0) { r = xmlTextWriterStartElement(writer, BAD_CAST("name")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlTextWriterWriteAttribute(writer, BAD_CAST("enctype"), BAD_CAST("base64")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteAttribute() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlTextWriterWriteBase64(writer, file->basename.s, 0, archive_strlen(&(file->basename))); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteBase64() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } } else { r = xmlwrite_string(a, writer, "name", file->basename.s); if (r < 0) return (ARCHIVE_FATAL); } /* * Make a file type entry, "". */ filelink = NULL; archive_string_init(&linkto); switch (archive_entry_filetype(file->entry)) { case AE_IFDIR: filetype = "directory"; break; case AE_IFLNK: filetype = "symlink"; break; case AE_IFCHR: filetype = "character special"; break; case AE_IFBLK: filetype = "block special"; break; case AE_IFSOCK: filetype = "socket"; break; case AE_IFIFO: filetype = "fifo"; break; case AE_IFREG: default: if (file->hardlink_target != NULL) { filetype = "hardlink"; filelink = "link"; if (file->hardlink_target == file) archive_strcpy(&linkto, "original"); else archive_string_sprintf(&linkto, "%d", file->hardlink_target->id); } else filetype = "file"; break; } r = xmlwrite_string_attr(a, writer, "type", filetype, filelink, linkto.s); archive_string_free(&linkto); if (r < 0) return (ARCHIVE_FATAL); /* * On a virtual directory, we record "name" and "type" only. */ if (file->virtual) return (ARCHIVE_OK); switch (archive_entry_filetype(file->entry)) { case AE_IFLNK: /* * xar utility has checked a file type, which * a symbolic-link file has referenced. * For example: * ../ref/ * The symlink target file is "../ref/" and its * file type is a directory. * * ../f * The symlink target file is "../f" and its * file type is a regular file. * * But our implementation cannot do it, and then we * always record that a attribute "type" is "broken", * for example: * foo/bar * It means "foo/bar" is not reachable. */ r = xmlwrite_string_attr(a, writer, "link", file->symlink.s, "type", "broken"); if (r < 0) return (ARCHIVE_FATAL); break; case AE_IFCHR: case AE_IFBLK: r = xmlTextWriterStartElement(writer, BAD_CAST("device")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlwrite_fstring(a, writer, "major", "%d", archive_entry_rdevmajor(file->entry)); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_fstring(a, writer, "minor", "%d", archive_entry_rdevminor(file->entry)); if (r < 0) return (ARCHIVE_FATAL); r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } break; default: break; } /* * Make a inode entry, "". */ r = xmlwrite_fstring(a, writer, "inode", "%jd", archive_entry_ino64(file->entry)); if (r < 0) return (ARCHIVE_FATAL); if (archive_entry_dev(file->entry) != 0) { r = xmlwrite_fstring(a, writer, "deviceno", "%d", archive_entry_dev(file->entry)); if (r < 0) return (ARCHIVE_FATAL); } /* * Make a file mode entry, "". */ r = xmlwrite_mode(a, writer, "mode", archive_entry_mode(file->entry)); if (r < 0) return (ARCHIVE_FATAL); /* * Make a user entry, "" and ". */ r = xmlwrite_fstring(a, writer, "uid", "%d", archive_entry_uid(file->entry)); if (r < 0) return (ARCHIVE_FATAL); r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to UTF-8", archive_entry_uname(file->entry)); r2 = ARCHIVE_WARN; } if (len > 0) { r = xmlwrite_string(a, writer, "user", p); if (r < 0) return (ARCHIVE_FATAL); } /* * Make a group entry, "" and ". */ r = xmlwrite_fstring(a, writer, "gid", "%d", archive_entry_gid(file->entry)); if (r < 0) return (ARCHIVE_FATAL); r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to UTF-8", archive_entry_gname(file->entry)); r2 = ARCHIVE_WARN; } if (len > 0) { r = xmlwrite_string(a, writer, "group", p); if (r < 0) return (ARCHIVE_FATAL); } /* * Make a ctime entry, "". */ if (archive_entry_ctime_is_set(file->entry)) { r = xmlwrite_time(a, writer, "ctime", archive_entry_ctime(file->entry), 1); if (r < 0) return (ARCHIVE_FATAL); } /* * Make a mtime entry, "". */ if (archive_entry_mtime_is_set(file->entry)) { r = xmlwrite_time(a, writer, "mtime", archive_entry_mtime(file->entry), 1); if (r < 0) return (ARCHIVE_FATAL); } /* * Make a atime entry, "". */ if (archive_entry_atime_is_set(file->entry)) { r = xmlwrite_time(a, writer, "atime", archive_entry_atime(file->entry), 1); if (r < 0) return (ARCHIVE_FATAL); } /* * Make fflags entries, "" and "". */ fflags = archive_entry_fflags_text(file->entry); if (fflags != NULL) { r = make_fflags_entry(a, writer, "flags", fflags); if (r < 0) return (r); r = make_fflags_entry(a, writer, "ext2", fflags); if (r < 0) return (r); } /* * Make extended attribute entries, "". */ archive_entry_xattr_reset(file->entry); for (heap = file->xattr.first; heap != NULL; heap = heap->next) { const char *name; const void *value; size_t size; archive_entry_xattr_next(file->entry, &name, &value, &size); r = xmlTextWriterStartElement(writer, BAD_CAST("ea")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("id"), "%d", heap->id); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteAttribute() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlwrite_heap(a, writer, heap); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_string(a, writer, "name", name); if (r < 0) return (ARCHIVE_FATAL); r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } } /* * Make a file data entry, "". */ if (file->data.length > 0) { r = xmlTextWriterStartElement(writer, BAD_CAST("data")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlwrite_heap(a, writer, &(file->data)); if (r < 0) return (ARCHIVE_FATAL); r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } } if (archive_strlen(&file->script) > 0) { r = xmlTextWriterStartElement(writer, BAD_CAST("content")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); return (ARCHIVE_FATAL); } r = xmlwrite_string(a, writer, "interpreter", file->script.s); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_string(a, writer, "type", "script"); if (r < 0) return (ARCHIVE_FATAL); r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); return (ARCHIVE_FATAL); } } return (r2); } /* * Make the TOC */ static int make_toc(struct archive_write *a) { struct xar *xar; struct file *np; xmlBufferPtr bp; xmlTextWriterPtr writer; int algsize; int r, ret; xar = (struct xar *)a->format_data; ret = ARCHIVE_FATAL; /* * Initialize xml writer. */ writer = NULL; bp = xmlBufferCreate(); if (bp == NULL) { archive_set_error(&a->archive, ENOMEM, "xmlBufferCreate() " "couldn't create xml buffer"); goto exit_toc; } writer = xmlNewTextWriterMemory(bp, 0); if (writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlNewTextWriterMemory() " "couldn't create xml writer"); goto exit_toc; } r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartDocument() failed: %d", r); goto exit_toc; } r = xmlTextWriterSetIndent(writer, 4); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterSetIndent() failed: %d", r); goto exit_toc; } /* * Start recording TOC */ r = xmlTextWriterStartElement(writer, BAD_CAST("xar")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); goto exit_toc; } r = xmlTextWriterStartElement(writer, BAD_CAST("toc")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartDocument() failed: %d", r); goto exit_toc; } /* * Record the creation time of the archive file. */ r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0); if (r < 0) goto exit_toc; /* * Record the checksum value of TOC */ algsize = getalgsize(xar->opt_toc_sumalg); if (algsize) { /* * Record TOC checksum */ r = xmlTextWriterStartElement(writer, BAD_CAST("checksum")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() failed: %d", r); goto exit_toc; } r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST_CONST(getalgname(xar->opt_toc_sumalg))); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteAttribute() failed: %d", r); goto exit_toc; } /* * Record the offset of the value of checksum of TOC */ r = xmlwrite_string(a, writer, "offset", "0"); if (r < 0) goto exit_toc; /* * Record the size of the value of checksum of TOC */ r = xmlwrite_fstring(a, writer, "size", "%d", algsize); if (r < 0) goto exit_toc; r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() failed: %d", r); goto exit_toc; } } np = xar->root; do { if (np != np->parent) { r = make_file_entry(a, writer, np); if (r != ARCHIVE_OK) goto exit_toc; } if (np->dir && np->children.first != NULL) { /* Enter to sub directories. */ np = np->children.first; r = xmlTextWriterStartElement(writer, BAD_CAST("file")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() " "failed: %d", r); goto exit_toc; } r = xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("id"), "%d", np->id); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteAttribute() " "failed: %d", r); goto exit_toc; } continue; } while (np != np->parent) { r = xmlTextWriterEndElement(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndElement() " "failed: %d", r); goto exit_toc; } if (np->chnext == NULL) { /* Return to the parent directory. */ np = np->parent; } else { np = np->chnext; r = xmlTextWriterStartElement(writer, BAD_CAST("file")); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterStartElement() " "failed: %d", r); goto exit_toc; } r = xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("id"), "%d", np->id); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterWriteAttribute() " "failed: %d", r); goto exit_toc; } break; } } } while (np != np->parent); r = xmlTextWriterEndDocument(writer); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "xmlTextWriterEndDocument() failed: %d", r); goto exit_toc; } #if DEBUG_PRINT_TOC fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n", strlen((const char *)bp->content), bp->content); #endif /* * Compress the TOC and calculate the sum of the TOC. */ xar->toc.temp_offset = xar->temp_offset; xar->toc.size = bp->use; checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg); r = compression_init_encoder_gzip(&(a->archive), &(xar->stream), 6, 1); if (r != ARCHIVE_OK) goto exit_toc; xar->stream.next_in = bp->content; xar->stream.avail_in = bp->use; xar->stream.total_in = 0; xar->stream.next_out = xar->wbuff; xar->stream.avail_out = sizeof(xar->wbuff); xar->stream.total_out = 0; for (;;) { size_t size; r = compression_code(&(a->archive), &(xar->stream), ARCHIVE_Z_FINISH); if (r != ARCHIVE_OK && r != ARCHIVE_EOF) goto exit_toc; size = sizeof(xar->wbuff) - xar->stream.avail_out; checksum_update(&(xar->a_sumwrk), xar->wbuff, size); if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK) goto exit_toc; if (r == ARCHIVE_EOF) break; xar->stream.next_out = xar->wbuff; xar->stream.avail_out = sizeof(xar->wbuff); } r = compression_end(&(a->archive), &(xar->stream)); if (r != ARCHIVE_OK) goto exit_toc; xar->toc.length = xar->stream.total_out; xar->toc.compression = GZIP; checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum)); ret = ARCHIVE_OK; exit_toc: if (writer) xmlFreeTextWriter(writer); if (bp) xmlBufferFree(bp); return (ret); } static int flush_wbuff(struct archive_write *a) { struct xar *xar; int r; size_t s; xar = (struct xar *)a->format_data; s = sizeof(xar->wbuff) - xar->wbuff_remaining; r = __archive_write_output(a, xar->wbuff, s); if (r != ARCHIVE_OK) return (r); xar->wbuff_remaining = sizeof(xar->wbuff); return (r); } static int copy_out(struct archive_write *a, uint64_t offset, uint64_t length) { struct xar *xar; int r; xar = (struct xar *)a->format_data; if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) { archive_set_error(&(a->archive), errno, "lseek failed"); return (ARCHIVE_FATAL); } while (length) { size_t rsize; ssize_t rs; unsigned char *wb; if (length > xar->wbuff_remaining) rsize = xar->wbuff_remaining; else rsize = (size_t)length; wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining); rs = read(xar->temp_fd, wb, rsize); if (rs < 0) { archive_set_error(&(a->archive), errno, "Can't read temporary file(%jd)", (intmax_t)rs); return (ARCHIVE_FATAL); } if (rs == 0) { archive_set_error(&(a->archive), 0, "Truncated xar archive"); return (ARCHIVE_FATAL); } xar->wbuff_remaining -= rs; length -= rs; if (xar->wbuff_remaining == 0) { r = flush_wbuff(a); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } static int xar_close(struct archive_write *a) { struct xar *xar; unsigned char *wb; uint64_t length; int r; xar = (struct xar *)a->format_data; /* Empty! */ if (xar->root->children.first == NULL) return (ARCHIVE_OK); /* Save the length of all file extended attributes and contents. */ length = xar->temp_offset; /* Connect hardlinked files */ file_connect_hardlink_files(xar); /* Make the TOC */ r = make_toc(a); if (r != ARCHIVE_OK) return (r); /* * Make the xar header on wbuff(write buffer). */ wb = xar->wbuff; xar->wbuff_remaining = sizeof(xar->wbuff); archive_be32enc(&wb[0], HEADER_MAGIC); archive_be16enc(&wb[4], HEADER_SIZE); archive_be16enc(&wb[6], HEADER_VERSION); archive_be64enc(&wb[8], xar->toc.length); archive_be64enc(&wb[16], xar->toc.size); archive_be32enc(&wb[24], xar->toc.a_sum.alg); xar->wbuff_remaining -= HEADER_SIZE; /* * Write the TOC */ r = copy_out(a, xar->toc.temp_offset, xar->toc.length); if (r != ARCHIVE_OK) return (r); /* Write the checksum value of the TOC. */ if (xar->toc.a_sum.len) { if (xar->wbuff_remaining < xar->toc.a_sum.len) { r = flush_wbuff(a); if (r != ARCHIVE_OK) return (r); } wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining); memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len); xar->wbuff_remaining -= xar->toc.a_sum.len; } /* * Write all file extended attributes and contents. */ r = copy_out(a, xar->toc.a_sum.len, length); if (r != ARCHIVE_OK) return (r); r = flush_wbuff(a); return (r); } static int xar_free(struct archive_write *a) { struct xar *xar; xar = (struct xar *)a->format_data; /* Close the temporary file. */ if (xar->temp_fd >= 0) close(xar->temp_fd); archive_string_free(&(xar->cur_dirstr)); archive_string_free(&(xar->tstr)); archive_string_free(&(xar->vstr)); file_free_hardlinks(xar); file_free_register(xar); compression_end(&(a->archive), &(xar->stream)); free(xar); return (ARCHIVE_OK); } static int file_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct file *f1 = (const struct file *)n1; const struct file *f2 = (const struct file *)n2; return (strcmp(f1->basename.s, f2->basename.s)); } static int file_cmp_key(const struct archive_rb_node *n, const void *key) { const struct file *f = (const struct file *)n; return (strcmp(f->basename.s, (const char *)key)); } static struct file * file_new(struct archive_write *a, struct archive_entry *entry) { struct file *file; static const struct archive_rb_tree_ops rb_ops = { file_cmp_node, file_cmp_key }; file = calloc(1, sizeof(*file)); if (file == NULL) return (NULL); if (entry != NULL) file->entry = archive_entry_clone(entry); else file->entry = archive_entry_new2(&a->archive); if (file->entry == NULL) { free(file); return (NULL); } __archive_rb_tree_init(&(file->rbtree), &rb_ops); file->children.first = NULL; file->children.last = &(file->children.first); file->xattr.first = NULL; file->xattr.last = &(file->xattr.first); archive_string_init(&(file->parentdir)); archive_string_init(&(file->basename)); archive_string_init(&(file->symlink)); archive_string_init(&(file->script)); if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR) file->dir = 1; return (file); } static void file_free(struct file *file) { struct heap_data *heap, *next_heap; heap = file->xattr.first; while (heap != NULL) { next_heap = heap->next; free(heap); heap = next_heap; } archive_string_free(&(file->parentdir)); archive_string_free(&(file->basename)); archive_string_free(&(file->symlink)); archive_string_free(&(file->script)); archive_entry_free(file->entry); free(file); } static struct file * file_create_virtual_dir(struct archive_write *a, struct xar *xar, const char *pathname) { struct file *file; (void)xar; /* UNUSED */ file = file_new(a, NULL); if (file == NULL) return (NULL); archive_entry_set_pathname(file->entry, pathname); archive_entry_set_mode(file->entry, 0555 | AE_IFDIR); file->dir = 1; file->virtual = 1; return (file); } static int file_add_child_tail(struct file *parent, struct file *child) { if (!__archive_rb_tree_insert_node( &(parent->rbtree), (struct archive_rb_node *)child)) return (0); child->chnext = NULL; *parent->children.last = child; parent->children.last = &(child->chnext); child->parent = parent; return (1); } /* * Find a entry from `parent' */ static struct file * file_find_child(struct file *parent, const char *child_name) { struct file *np; np = (struct file *)__archive_rb_tree_find_node( &(parent->rbtree), child_name); return (np); } #if defined(_WIN32) || defined(__CYGWIN__) static void cleanup_backslash(char *utf8, size_t len) { /* Convert a path-separator from '\' to '/' */ while (*utf8 != '\0' && len) { if (*utf8 == '\\') *utf8 = '/'; ++utf8; --len; } } #else #define cleanup_backslash(p, len) /* nop */ #endif /* * Generate a parent directory name and a base name from a pathname. */ static int file_gen_utility_names(struct archive_write *a, struct file *file) { struct xar *xar; const char *pp; char *p, *dirname, *slash; size_t len; int r = ARCHIVE_OK; xar = (struct xar *)a->format_data; archive_string_empty(&(file->parentdir)); archive_string_empty(&(file->basename)); archive_string_empty(&(file->symlink)); if (file->parent == file)/* virtual root */ return (ARCHIVE_OK); if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to UTF-8", archive_entry_pathname(file->entry)); r = ARCHIVE_WARN; } archive_strncpy(&(file->parentdir), pp, len); len = file->parentdir.length; p = dirname = file->parentdir.s; /* * Convert a path-separator from '\' to '/' */ cleanup_backslash(p, len); /* * Remove leading '/', '../' and './' elements */ while (*p) { if (p[0] == '/') { p++; len--; } else if (p[0] != '.') break; else if (p[1] == '.' && p[2] == '/') { p += 3; len -= 3; } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) { p += 2; len -= 2; } else if (p[1] == '\0') { p++; len--; } else break; } if (p != dirname) { memmove(dirname, p, len+1); p = dirname; } /* * Remove "/","/." and "/.." elements from tail. */ while (len > 0) { size_t ll = len; if (p[len-1] == '/') { p[len-1] = '\0'; len--; } if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { p[len-2] = '\0'; len -= 2; } if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && p[len-1] == '.') { p[len-3] = '\0'; len -= 3; } if (ll == len) break; } while (*p) { if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ memmove(p, p+1, strlen(p+1) + 1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ memmove(p, p+2, strlen(p+2) + 1); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' */ char *rp = p -1; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { strcpy(rp, p+3); p = rp; } else { strcpy(dirname, p+4); p = dirname; } } else p++; } else p++; } p = dirname; len = strlen(p); if (archive_entry_filetype(file->entry) == AE_IFLNK) { size_t len2; /* Convert symlink name too. */ if (archive_entry_symlink_l(file->entry, &pp, &len2, xar->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate symlink '%s' to UTF-8", archive_entry_symlink(file->entry)); r = ARCHIVE_WARN; } archive_strncpy(&(file->symlink), pp, len2); cleanup_backslash(file->symlink.s, file->symlink.length); } /* * - Count up directory elements. * - Find out the position which points the last position of * path separator('/'). */ slash = NULL; for (; *p != '\0'; p++) if (*p == '/') slash = p; if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; archive_string_copy(&(file->basename), &(file->parentdir)); archive_string_empty(&(file->parentdir)); *file->parentdir.s = '\0'; return (r); } /* Make a basename from dirname and slash */ *slash = '\0'; file->parentdir.length = slash - dirname; archive_strcpy(&(file->basename), slash + 1); return (r); } static int get_path_component(char *name, int n, const char *fn) { char *p; int l; p = strchr(fn, '/'); if (p == NULL) { if ((l = strlen(fn)) == 0) return (0); } else l = p - fn; if (l > n -1) return (-1); memcpy(name, fn, l); name[l] = '\0'; return (l); } /* * Add a new entry into the tree. */ static int file_tree(struct archive_write *a, struct file **filepp) { #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 char name[NAME_MAX+1]; #else char name[256]; #endif struct xar *xar = (struct xar *)a->format_data; struct file *dent, *file, *np; struct archive_entry *ent; const char *fn, *p; int l; file = *filepp; dent = xar->root; if (file->parentdir.length > 0) fn = p = file->parentdir.s; else fn = p = ""; /* * If the path of the parent directory of `file' entry is * the same as the path of `cur_dirent', add isoent to * `cur_dirent'. */ if (archive_strlen(&(xar->cur_dirstr)) == archive_strlen(&(file->parentdir)) && strcmp(xar->cur_dirstr.s, fn) == 0) { if (!file_add_child_tail(xar->cur_dirent, file)) { np = (struct file *)__archive_rb_tree_find_node( &(xar->cur_dirent->rbtree), file->basename.s); goto same_entry; } return (ARCHIVE_OK); } for (;;) { l = get_path_component(name, sizeof(name), fn); if (l == 0) { np = NULL; break; } if (l < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); file_free(file); *filepp = NULL; return (ARCHIVE_FATAL); } np = file_find_child(dent, name); if (np == NULL || fn[0] == '\0') break; /* Find next subdirectory. */ if (!np->dir) { /* NOT Directory! */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "`%s' is not directory, we cannot insert `%s' ", archive_entry_pathname(np->entry), archive_entry_pathname(file->entry)); file_free(file); *filepp = NULL; return (ARCHIVE_FAILED); } fn += l; if (fn[0] == '/') fn++; dent = np; } if (np == NULL) { /* * Create virtual parent directories. */ while (fn[0] != '\0') { struct file *vp; struct archive_string as; archive_string_init(&as); archive_strncat(&as, p, fn - p + l); if (as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } vp = file_create_virtual_dir(a, xar, as.s); if (vp == NULL) { archive_string_free(&as); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); file_free(file); *filepp = NULL; return (ARCHIVE_FATAL); } archive_string_free(&as); if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED) return (ARCHIVE_FATAL); file_add_child_tail(dent, vp); file_register(xar, vp); np = vp; fn += l; if (fn[0] == '/') fn++; l = get_path_component(name, sizeof(name), fn); if (l < 0) { archive_string_free(&as); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "A name buffer is too small"); file_free(file); *filepp = NULL; return (ARCHIVE_FATAL); } dent = np; } /* Found out the parent directory where isoent can be * inserted. */ xar->cur_dirent = dent; archive_string_empty(&(xar->cur_dirstr)); archive_string_ensure(&(xar->cur_dirstr), archive_strlen(&(dent->parentdir)) + archive_strlen(&(dent->basename)) + 2); if (archive_strlen(&(dent->parentdir)) + archive_strlen(&(dent->basename)) == 0) xar->cur_dirstr.s[0] = 0; else { if (archive_strlen(&(dent->parentdir)) > 0) { archive_string_copy(&(xar->cur_dirstr), &(dent->parentdir)); archive_strappend_char(&(xar->cur_dirstr), '/'); } archive_string_concat(&(xar->cur_dirstr), &(dent->basename)); } if (!file_add_child_tail(dent, file)) { np = (struct file *)__archive_rb_tree_find_node( &(dent->rbtree), file->basename.s); goto same_entry; } return (ARCHIVE_OK); } same_entry: /* * We have already has the entry the filename of which is * the same. */ if (archive_entry_filetype(np->entry) != archive_entry_filetype(file->entry)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found duplicate entries `%s' and its file type is " "different", archive_entry_pathname(np->entry)); file_free(file); *filepp = NULL; return (ARCHIVE_FAILED); } /* Swap files. */ ent = np->entry; np->entry = file->entry; file->entry = ent; np->virtual = 0; file_free(file); *filepp = np; return (ARCHIVE_OK); } static void file_register(struct xar *xar, struct file *file) { file->id = xar->file_idx++; file->next = NULL; *xar->file_list.last = file; xar->file_list.last = &(file->next); } static void file_init_register(struct xar *xar) { xar->file_list.first = NULL; xar->file_list.last = &(xar->file_list.first); } static void file_free_register(struct xar *xar) { struct file *file, *file_next; file = xar->file_list.first; while (file != NULL) { file_next = file->next; file_free(file); file = file_next; } } /* * Register entry to get a hardlink target. */ static int file_register_hardlink(struct archive_write *a, struct file *file) { struct xar *xar = (struct xar *)a->format_data; struct hardlink *hl; const char *pathname; archive_entry_set_nlink(file->entry, 1); pathname = archive_entry_hardlink(file->entry); if (pathname == NULL) { /* This `file` is a hardlink target. */ hl = malloc(sizeof(*hl)); if (hl == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } hl->nlink = 1; /* A hardlink target must be the first position. */ file->hlnext = NULL; hl->file_list.first = file; hl->file_list.last = &(file->hlnext); __archive_rb_tree_insert_node(&(xar->hardlink_rbtree), (struct archive_rb_node *)hl); } else { hl = (struct hardlink *)__archive_rb_tree_find_node( &(xar->hardlink_rbtree), pathname); if (hl != NULL) { /* Insert `file` entry into the tail. */ file->hlnext = NULL; *hl->file_list.last = file; hl->file_list.last = &(file->hlnext); hl->nlink++; } archive_entry_unset_size(file->entry); } return (ARCHIVE_OK); } /* * Hardlinked files have to have the same location of extent. * We have to find out hardlink target entries for entries which * have a hardlink target name. */ static void file_connect_hardlink_files(struct xar *xar) { struct archive_rb_node *n; struct hardlink *hl; struct file *target, *nf; ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) { hl = (struct hardlink *)n; /* The first entry must be a hardlink target. */ target = hl->file_list.first; archive_entry_set_nlink(target->entry, hl->nlink); if (hl->nlink > 1) /* It means this file is a hardlink * target itself. */ target->hardlink_target = target; for (nf = target->hlnext; nf != NULL; nf = nf->hlnext) { nf->hardlink_target = target; archive_entry_set_nlink(nf->entry, hl->nlink); } } } static int file_hd_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct hardlink *h1 = (const struct hardlink *)n1; const struct hardlink *h2 = (const struct hardlink *)n2; return (strcmp(archive_entry_pathname(h1->file_list.first->entry), archive_entry_pathname(h2->file_list.first->entry))); } static int file_hd_cmp_key(const struct archive_rb_node *n, const void *key) { const struct hardlink *h = (const struct hardlink *)n; return (strcmp(archive_entry_pathname(h->file_list.first->entry), (const char *)key)); } static void file_init_hardlinks(struct xar *xar) { static const struct archive_rb_tree_ops rb_ops = { file_hd_cmp_node, file_hd_cmp_key, }; __archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops); } static void file_free_hardlinks(struct xar *xar) { struct archive_rb_node *n, *tmp; ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) { __archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n); free(n); } } static void checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg) { sumwrk->alg = sum_alg; switch (sum_alg) { case CKSUM_NONE: break; case CKSUM_SHA1: archive_sha1_init(&(sumwrk->sha1ctx)); break; case CKSUM_MD5: archive_md5_init(&(sumwrk->md5ctx)); break; } } static void checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size) { switch (sumwrk->alg) { case CKSUM_NONE: break; case CKSUM_SHA1: archive_sha1_update(&(sumwrk->sha1ctx), buff, size); break; case CKSUM_MD5: archive_md5_update(&(sumwrk->md5ctx), buff, size); break; } } static void checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval) { switch (sumwrk->alg) { case CKSUM_NONE: sumval->len = 0; break; case CKSUM_SHA1: archive_sha1_final(&(sumwrk->sha1ctx), sumval->val); sumval->len = SHA1_SIZE; break; case CKSUM_MD5: archive_md5_final(&(sumwrk->md5ctx), sumval->val); sumval->len = MD5_SIZE; break; } sumval->alg = sumwrk->alg; } #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H) static int compression_unsupported_encoder(struct archive *a, struct la_zstream *lastrm, const char *name) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "%s compression not supported on this platform", name); lastrm->valid = 0; lastrm->real_stream = NULL; return (ARCHIVE_FAILED); } #endif static int compression_init_encoder_gzip(struct archive *a, struct la_zstream *lastrm, int level, int withheader) { z_stream *strm; if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm)); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for gzip stream"); return (ARCHIVE_FATAL); } /* zlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; if (deflateInit2(strm, level, Z_DEFLATED, (withheader)?15:-15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_gzip; lastrm->end = compression_end_gzip; return (ARCHIVE_OK); } static int compression_code_gzip(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { z_stream *strm; int r; strm = (z_stream *)lastrm->real_stream; /* zlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; r = deflate(strm, (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH); lastrm->next_in = strm->next_in; lastrm->avail_in = strm->avail_in; lastrm->total_in = strm->total_in; lastrm->next_out = strm->next_out; lastrm->avail_out = strm->avail_out; lastrm->total_out = strm->total_out; switch (r) { case Z_OK: return (ARCHIVE_OK); case Z_STREAM_END: return (ARCHIVE_EOF); default: archive_set_error(a, ARCHIVE_ERRNO_MISC, "GZip compression failed:" " deflate() call returned status %d", r); return (ARCHIVE_FATAL); } } static int compression_end_gzip(struct archive *a, struct la_zstream *lastrm) { z_stream *strm; int r; strm = (z_stream *)lastrm->real_stream; r = deflateEnd(strm); free(strm); lastrm->real_stream = NULL; lastrm->valid = 0; if (r != Z_OK) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) static int compression_init_encoder_bzip2(struct archive *a, struct la_zstream *lastrm, int level) { bz_stream *strm; if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm)); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for bzip2 stream"); return (ARCHIVE_FATAL); } /* bzlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_bzip2; lastrm->end = compression_end_bzip2; return (ARCHIVE_OK); } static int compression_code_bzip2(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { bz_stream *strm; int r; strm = (bz_stream *)lastrm->real_stream; /* bzlib.h is not const-correct, so we need this one bit * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); r = BZ2_bzCompress(strm, (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN); lastrm->next_in = (const unsigned char *)strm->next_in; lastrm->avail_in = strm->avail_in; lastrm->total_in = (((uint64_t)(uint32_t)strm->total_in_hi32) << 32) + (uint64_t)(uint32_t)strm->total_in_lo32; lastrm->next_out = (unsigned char *)strm->next_out; lastrm->avail_out = strm->avail_out; lastrm->total_out = (((uint64_t)(uint32_t)strm->total_out_hi32) << 32) + (uint64_t)(uint32_t)strm->total_out_lo32; switch (r) { case BZ_RUN_OK: /* Non-finishing */ case BZ_FINISH_OK: /* Finishing: There's more work to do */ return (ARCHIVE_OK); case BZ_STREAM_END: /* Finishing: all done */ /* Only occurs in finishing case */ return (ARCHIVE_EOF); default: /* Any other return value indicates an error */ archive_set_error(a, ARCHIVE_ERRNO_MISC, "Bzip2 compression failed:" " BZ2_bzCompress() call returned status %d", r); return (ARCHIVE_FATAL); } } static int compression_end_bzip2(struct archive *a, struct la_zstream *lastrm) { bz_stream *strm; int r; strm = (bz_stream *)lastrm->real_stream; r = BZ2_bzCompressEnd(strm); free(strm); lastrm->real_stream = NULL; lastrm->valid = 0; if (r != BZ_OK) { archive_set_error(a, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } #else static int compression_init_encoder_bzip2(struct archive *a, struct la_zstream *lastrm, int level) { (void) level; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "bzip2")); } #endif #if defined(HAVE_LZMA_H) static int compression_init_encoder_lzma(struct archive *a, struct la_zstream *lastrm, int level) { static const lzma_stream lzma_init_data = LZMA_STREAM_INIT; lzma_stream *strm; lzma_options_lzma lzma_opt; int r; if (lastrm->valid) compression_end(a, lastrm); if (lzma_lzma_preset(&lzma_opt, level)) { lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } strm = calloc(1, sizeof(*strm)); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for lzma stream"); return (ARCHIVE_FATAL); } *strm = lzma_init_data; r = lzma_alone_encoder(strm, &lzma_opt); switch (r) { case LZMA_OK: lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_lzma; lastrm->end = compression_end_lzma; r = ARCHIVE_OK; break; case LZMA_MEM_ERROR: free(strm); lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Internal error initializing compression library: " "Cannot allocate memory"); r = ARCHIVE_FATAL; break; default: free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "It's a bug in liblzma"); r = ARCHIVE_FATAL; break; } return (r); } static int compression_init_encoder_xz(struct archive *a, struct la_zstream *lastrm, int level, int threads) { static const lzma_stream lzma_init_data = LZMA_STREAM_INIT; lzma_stream *strm; lzma_filter *lzmafilters; lzma_options_lzma lzma_opt; int r; #ifdef HAVE_LZMA_STREAM_ENCODER_MT lzma_mt mt_options; #endif (void)threads; /* UNUSED (if multi-threaded LZMA library not avail) */ if (lastrm->valid) compression_end(a, lastrm); strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2); if (strm == NULL) { archive_set_error(a, ENOMEM, "Can't allocate memory for xz stream"); return (ARCHIVE_FATAL); } lzmafilters = (lzma_filter *)(strm+1); - if (level > 6) - level = 6; + if (level > 9) + level = 9; if (lzma_lzma_preset(&lzma_opt, level)) { free(strm); lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Internal error initializing compression library"); return (ARCHIVE_FATAL); } lzmafilters[0].id = LZMA_FILTER_LZMA2; lzmafilters[0].options = &lzma_opt; lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ *strm = lzma_init_data; #ifdef HAVE_LZMA_STREAM_ENCODER_MT if (threads > 1) { memset(&mt_options, 0, sizeof(mt_options)); mt_options.threads = threads; mt_options.timeout = 300; mt_options.filters = lzmafilters; mt_options.check = LZMA_CHECK_CRC64; r = lzma_stream_encoder_mt(strm, &mt_options); } else #endif r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64); switch (r) { case LZMA_OK: lastrm->real_stream = strm; lastrm->valid = 1; lastrm->code = compression_code_lzma; lastrm->end = compression_end_lzma; r = ARCHIVE_OK; break; case LZMA_MEM_ERROR: free(strm); lastrm->real_stream = NULL; archive_set_error(a, ENOMEM, "Internal error initializing compression library: " "Cannot allocate memory"); r = ARCHIVE_FATAL; break; default: free(strm); lastrm->real_stream = NULL; archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "It's a bug in liblzma"); r = ARCHIVE_FATAL; break; } return (r); } static int compression_code_lzma(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { lzma_stream *strm; int r; strm = (lzma_stream *)lastrm->real_stream; strm->next_in = lastrm->next_in; strm->avail_in = lastrm->avail_in; strm->total_in = lastrm->total_in; strm->next_out = lastrm->next_out; strm->avail_out = lastrm->avail_out; strm->total_out = lastrm->total_out; r = lzma_code(strm, (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN); lastrm->next_in = strm->next_in; lastrm->avail_in = strm->avail_in; lastrm->total_in = strm->total_in; lastrm->next_out = strm->next_out; lastrm->avail_out = strm->avail_out; lastrm->total_out = strm->total_out; switch (r) { case LZMA_OK: /* Non-finishing case */ return (ARCHIVE_OK); case LZMA_STREAM_END: /* This return can only occur in finishing case. */ return (ARCHIVE_EOF); case LZMA_MEMLIMIT_ERROR: archive_set_error(a, ENOMEM, "lzma compression error:" " %ju MiB would have been needed", (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1) / (1024 * 1024))); return (ARCHIVE_FATAL); default: /* Any other return value indicates an error */ archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzma compression failed:" " lzma_code() call returned status %d", r); return (ARCHIVE_FATAL); } } static int compression_end_lzma(struct archive *a, struct la_zstream *lastrm) { lzma_stream *strm; (void)a; /* UNUSED */ strm = (lzma_stream *)lastrm->real_stream; lzma_end(strm); free(strm); lastrm->valid = 0; lastrm->real_stream = NULL; return (ARCHIVE_OK); } #else static int compression_init_encoder_lzma(struct archive *a, struct la_zstream *lastrm, int level) { (void) level; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "lzma")); } static int compression_init_encoder_xz(struct archive *a, struct la_zstream *lastrm, int level, int threads) { (void) level; /* UNUSED */ (void) threads; /* UNUSED */ if (lastrm->valid) compression_end(a, lastrm); return (compression_unsupported_encoder(a, lastrm, "xz")); } #endif static int xar_compression_init_encoder(struct archive_write *a) { struct xar *xar; int r; xar = (struct xar *)a->format_data; switch (xar->opt_compression) { case GZIP: r = compression_init_encoder_gzip( &(a->archive), &(xar->stream), xar->opt_compression_level, 1); break; case BZIP2: r = compression_init_encoder_bzip2( &(a->archive), &(xar->stream), xar->opt_compression_level); break; case LZMA: r = compression_init_encoder_lzma( &(a->archive), &(xar->stream), xar->opt_compression_level); break; case XZ: r = compression_init_encoder_xz( &(a->archive), &(xar->stream), xar->opt_compression_level, xar->opt_threads); break; default: r = ARCHIVE_OK; break; } if (r == ARCHIVE_OK) { xar->stream.total_in = 0; xar->stream.next_out = xar->wbuff; xar->stream.avail_out = sizeof(xar->wbuff); xar->stream.total_out = 0; } return (r); } static int compression_code(struct archive *a, struct la_zstream *lastrm, enum la_zaction action) { if (lastrm->valid) return (lastrm->code(a, lastrm, action)); return (ARCHIVE_OK); } static int compression_end(struct archive *a, struct la_zstream *lastrm) { if (lastrm->valid) return (lastrm->end(a, lastrm)); return (ARCHIVE_OK); } static int save_xattrs(struct archive_write *a, struct file *file) { struct xar *xar; const char *name; const void *value; struct heap_data *heap; size_t size; int count, r; xar = (struct xar *)a->format_data; count = archive_entry_xattr_reset(file->entry); if (count == 0) return (ARCHIVE_OK); while (count--) { archive_entry_xattr_next(file->entry, &name, &value, &size); checksum_init(&(xar->a_sumwrk), xar->opt_sumalg); checksum_init(&(xar->e_sumwrk), xar->opt_sumalg); heap = calloc(1, sizeof(*heap)); if (heap == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for xattr"); return (ARCHIVE_FATAL); } heap->id = file->ea_idx++; heap->temp_offset = xar->temp_offset; heap->size = size;/* save a extracted size */ heap->compression = xar->opt_compression; /* Get a extracted sumcheck value. */ checksum_update(&(xar->e_sumwrk), value, size); checksum_final(&(xar->e_sumwrk), &(heap->e_sum)); /* * Not compression to xattr is simple way. */ if (heap->compression == NONE) { checksum_update(&(xar->a_sumwrk), value, size); checksum_final(&(xar->a_sumwrk), &(heap->a_sum)); if (write_to_temp(a, value, size) != ARCHIVE_OK) { free(heap); return (ARCHIVE_FATAL); } heap->length = size; /* Add heap to the tail of file->xattr. */ heap->next = NULL; *file->xattr.last = heap; file->xattr.last = &(heap->next); /* Next xattr */ continue; } /* * Init compression library. */ r = xar_compression_init_encoder(a); if (r != ARCHIVE_OK) { free(heap); return (ARCHIVE_FATAL); } xar->stream.next_in = (const unsigned char *)value; xar->stream.avail_in = size; for (;;) { r = compression_code(&(a->archive), &(xar->stream), ARCHIVE_Z_FINISH); if (r != ARCHIVE_OK && r != ARCHIVE_EOF) { free(heap); return (ARCHIVE_FATAL); } size = sizeof(xar->wbuff) - xar->stream.avail_out; checksum_update(&(xar->a_sumwrk), xar->wbuff, size); if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK) { free(heap); return (ARCHIVE_FATAL); } if (r == ARCHIVE_OK) { xar->stream.next_out = xar->wbuff; xar->stream.avail_out = sizeof(xar->wbuff); } else { checksum_final(&(xar->a_sumwrk), &(heap->a_sum)); heap->length = xar->stream.total_out; /* Add heap to the tail of file->xattr. */ heap->next = NULL; *file->xattr.last = heap; file->xattr.last = &(heap->next); break; } } /* Clean up compression library. */ r = compression_end(&(a->archive), &(xar->stream)); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static int getalgsize(enum sumalg sumalg) { switch (sumalg) { default: case CKSUM_NONE: return (0); case CKSUM_SHA1: return (SHA1_SIZE); case CKSUM_MD5: return (MD5_SIZE); } } static const char * getalgname(enum sumalg sumalg) { switch (sumalg) { default: case CKSUM_NONE: return (NULL); case CKSUM_SHA1: return (SHA1_NAME); case CKSUM_MD5: return (MD5_NAME); } } #endif /* Support xar format */ Index: stable/11/contrib/libarchive/libarchive/archive_write_set_format_zip.c =================================================================== --- stable/11/contrib/libarchive/libarchive/archive_write_set_format_zip.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/archive_write_set_format_zip.c (revision 368707) @@ -1,1695 +1,1700 @@ /*- * Copyright (c) 2008 Anselm Strauss * Copyright (c) 2009 Joerg Sonnenberger * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Development supported by Google Summer of Code 2008. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_hmac_private.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #define ZIP_ENTRY_FLAG_ENCRYPTED (1<<0) #define ZIP_ENTRY_FLAG_LENGTH_AT_END (1<<3) #define ZIP_ENTRY_FLAG_UTF8_NAME (1 << 11) #define ZIP_4GB_MAX ARCHIVE_LITERAL_LL(0xffffffff) #define ZIP_4GB_MAX_UNCOMPRESSED ARCHIVE_LITERAL_LL(0xff000000) enum compression { COMPRESSION_UNSPECIFIED = -1, COMPRESSION_STORE = 0, COMPRESSION_DEFLATE = 8 }; #ifdef HAVE_ZLIB_H #define COMPRESSION_DEFAULT COMPRESSION_DEFLATE #else #define COMPRESSION_DEFAULT COMPRESSION_STORE #endif enum encryption { ENCRYPTION_NONE = 0, ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */ ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */ ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */ }; #define TRAD_HEADER_SIZE 12 /* * See "WinZip - AES Encryption Information" * http://www.winzip.com/aes_info.htm */ /* Value used in compression method. */ #define WINZIP_AES_ENCRYPTION 99 /* A WinZip AES header size which is stored at the beginning of * file contents. */ #define WINZIP_AES128_HEADER_SIZE (8 + 2) #define WINZIP_AES256_HEADER_SIZE (16 + 2) /* AES vendor version. */ #define AES_VENDOR_AE_1 0x0001 #define AES_VENDOR_AE_2 0x0002 /* Authentication code size. */ #define AUTH_CODE_SIZE 10 /**/ #define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) struct cd_segment { struct cd_segment *next; size_t buff_size; unsigned char *buff; unsigned char *p; }; struct trad_enc_ctx { uint32_t keys[3]; }; struct zip { int64_t entry_offset; int64_t entry_compressed_size; int64_t entry_uncompressed_size; int64_t entry_compressed_written; int64_t entry_uncompressed_written; int64_t entry_uncompressed_limit; struct archive_entry *entry; uint32_t entry_crc32; enum compression entry_compression; enum encryption entry_encryption; int entry_flags; int entry_uses_zip64; int experiments; struct trad_enc_ctx tctx; char tctx_valid; unsigned char trad_chkdat; unsigned aes_vendor; archive_crypto_ctx cctx; char cctx_valid; archive_hmac_sha1_ctx hctx; char hctx_valid; unsigned char *file_header; size_t file_header_extra_offset; unsigned long (*crc32func)(unsigned long crc, const void *buff, size_t len); struct cd_segment *central_directory; struct cd_segment *central_directory_last; size_t central_directory_bytes; size_t central_directory_entries; int64_t written_bytes; /* Overall position in file. */ struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; enum compression requested_compression; int deflate_compression_level; int init_default_conversion; enum encryption encryption_type; #define ZIP_FLAG_AVOID_ZIP64 1 #define ZIP_FLAG_FORCE_ZIP64 2 #define ZIP_FLAG_EXPERIMENT_xl 4 int flags; #ifdef HAVE_ZLIB_H z_stream stream; #endif size_t len_buf; unsigned char *buf; }; /* Don't call this min or MIN, since those are already defined on lots of platforms (but not all). */ #define zipmin(a, b) ((a) > (b) ? (b) : (a)) static ssize_t archive_write_zip_data(struct archive_write *, const void *buff, size_t s); static int archive_write_zip_close(struct archive_write *); static int archive_write_zip_free(struct archive_write *); static int archive_write_zip_finish_entry(struct archive_write *); static int archive_write_zip_header(struct archive_write *, struct archive_entry *); static int archive_write_zip_options(struct archive_write *, const char *, const char *); static unsigned int dos_time(const time_t); static size_t path_length(struct archive_entry *); static int write_path(struct archive_entry *, struct archive_write *); static void copy_path(struct archive_entry *, unsigned char *); static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *); static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t); static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *, size_t, uint8_t *, size_t); static int init_traditional_pkware_encryption(struct archive_write *); static int is_traditional_pkware_encryption_supported(void); static int init_winzip_aes_encryption(struct archive_write *); static int is_winzip_aes_encryption_supported(int encryption); static unsigned char * cd_alloc(struct zip *zip, size_t length) { unsigned char *p; if (zip->central_directory == NULL || (zip->central_directory_last->p + length > zip->central_directory_last->buff + zip->central_directory_last->buff_size)) { struct cd_segment *segment = calloc(1, sizeof(*segment)); if (segment == NULL) return NULL; segment->buff_size = 64 * 1024; segment->buff = malloc(segment->buff_size); if (segment->buff == NULL) { free(segment); return NULL; } segment->p = segment->buff; if (zip->central_directory == NULL) { zip->central_directory = zip->central_directory_last = segment; } else { zip->central_directory_last->next = segment; zip->central_directory_last = segment; } } p = zip->central_directory_last->p; zip->central_directory_last->p += length; zip->central_directory_bytes += length; return (p); } static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { return crc32(crc, buff, (unsigned int)len); } static unsigned long fake_crc32(unsigned long crc, const void *buff, size_t len) { (void)crc; /* UNUSED */ (void)buff; /* UNUSED */ (void)len; /* UNUSED */ return 0; } static int archive_write_zip_options(struct archive_write *a, const char *key, const char *val) { struct zip *zip = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "compression") == 0) { /* * Set compression to use on all future entries. * This only affects regular files. */ if (val == NULL || val[0] == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: compression option needs a compression name", a->format_name); } else if (strcmp(val, "deflate") == 0) { #ifdef HAVE_ZLIB_H zip->requested_compression = COMPRESSION_DEFLATE; ret = ARCHIVE_OK; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "deflate compression not supported"); #endif } else if (strcmp(val, "store") == 0) { zip->requested_compression = COMPRESSION_STORE; ret = ARCHIVE_OK; } return (ret); } else if (strcmp(key, "compression-level") == 0) { if (val == NULL || !(val[0] >= '0' && val[0] <= '9') || val[1] != '\0') { return ARCHIVE_WARN; } if (val[0] == '0') { zip->requested_compression = COMPRESSION_STORE; return ARCHIVE_OK; } else { #ifdef HAVE_ZLIB_H zip->requested_compression = COMPRESSION_DEFLATE; zip->deflate_compression_level = val[0] - '0'; return ARCHIVE_OK; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "deflate compression not supported"); #endif } } else if (strcmp(key, "encryption") == 0) { if (val == NULL) { zip->encryption_type = ENCRYPTION_NONE; ret = ARCHIVE_OK; } else if (val[0] == '1' || strcmp(val, "traditional") == 0 || strcmp(val, "zipcrypt") == 0 || strcmp(val, "ZipCrypt") == 0) { if (is_traditional_pkware_encryption_supported()) { zip->encryption_type = ENCRYPTION_TRADITIONAL; ret = ARCHIVE_OK; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "encryption not supported"); } } else if (strcmp(val, "aes128") == 0) { if (is_winzip_aes_encryption_supported( ENCRYPTION_WINZIP_AES128)) { zip->encryption_type = ENCRYPTION_WINZIP_AES128; ret = ARCHIVE_OK; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "encryption not supported"); } } else if (strcmp(val, "aes256") == 0) { if (is_winzip_aes_encryption_supported( ENCRYPTION_WINZIP_AES256)) { zip->encryption_type = ENCRYPTION_WINZIP_AES256; ret = ARCHIVE_OK; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "encryption not supported"); } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: unknown encryption '%s'", a->format_name, val); } return (ret); } else if (strcmp(key, "experimental") == 0) { if (val == NULL || val[0] == 0) { zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl; } else { zip->flags |= ZIP_FLAG_EXPERIMENT_xl; } return (ARCHIVE_OK); } else if (strcmp(key, "fakecrc32") == 0) { /* * FOR TESTING ONLY: disable CRC calculation to speed up * certain complex tests. */ if (val == NULL || val[0] == 0) { zip->crc32func = real_crc32; } else { zip->crc32func = fake_crc32; } return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { /* * Set the character set used in translating filenames. */ if (val == NULL || val[0] == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); } else { zip->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (zip->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "zip64") == 0) { /* * Bias decisions about Zip64: force them to be * generated in certain cases where they are not * forbidden or avoid them in certain cases where they * are not strictly required. */ if (val != NULL && *val != '\0') { zip->flags |= ZIP_FLAG_FORCE_ZIP64; zip->flags &= ~ZIP_FLAG_AVOID_ZIP64; } else { zip->flags &= ~ZIP_FLAG_FORCE_ZIP64; zip->flags |= ZIP_FLAG_AVOID_ZIP64; } return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } int archive_write_zip_set_compression_deflate(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int ret = ARCHIVE_FAILED; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_zip_set_compression_deflate"); if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can only use archive_write_zip_set_compression_deflate" " with zip format"); ret = ARCHIVE_FATAL; } else { #ifdef HAVE_ZLIB_H struct zip *zip = a->format_data; zip->requested_compression = COMPRESSION_DEFLATE; ret = ARCHIVE_OK; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "deflate compression not supported"); ret = ARCHIVE_FAILED; #endif } return (ret); } int archive_write_zip_set_compression_store(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct zip *zip = a->format_data; int ret = ARCHIVE_FAILED; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_zip_set_compression_deflate"); if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can only use archive_write_zip_set_compression_store" " with zip format"); ret = ARCHIVE_FATAL; } else { zip->requested_compression = COMPRESSION_STORE; ret = ARCHIVE_OK; } return (ret); } int archive_write_set_format_zip(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct zip *zip; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_zip"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); zip = (struct zip *) calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } /* "Unspecified" lets us choose the appropriate compression. */ zip->requested_compression = COMPRESSION_UNSPECIFIED; #ifdef HAVE_ZLIB_H zip->deflate_compression_level = Z_DEFAULT_COMPRESSION; #endif zip->crc32func = real_crc32; /* A buffer used for both compression and encryption. */ zip->len_buf = 65536; zip->buf = malloc(zip->len_buf); if (zip->buf == NULL) { free(zip); archive_set_error(&a->archive, ENOMEM, "Can't allocate compression buffer"); return (ARCHIVE_FATAL); } a->format_data = zip; a->format_name = "zip"; a->format_options = archive_write_zip_options; a->format_write_header = archive_write_zip_header; a->format_write_data = archive_write_zip_data; a->format_finish_entry = archive_write_zip_finish_entry; a->format_close = archive_write_zip_close; a->format_free = archive_write_zip_free; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; a->archive.archive_format_name = "ZIP"; return (ARCHIVE_OK); } static int is_all_ascii(const char *p) { const unsigned char *pp = (const unsigned char *)p; while (*pp) { if (*pp++ > 127) return (0); } return (1); } static int archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) { unsigned char local_header[32]; unsigned char local_extra[144]; struct zip *zip = a->format_data; unsigned char *e; unsigned char *cd_extra; size_t filename_length; const char *slink = NULL; size_t slink_size = 0; struct archive_string_conv *sconv = get_sconv(a, zip); int ret, ret2 = ARCHIVE_OK; mode_t type; int version_needed = 10; /* Ignore types of entries that we don't support. */ type = archive_entry_filetype(entry); if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) { __archive_write_entry_filetype_unsupported( &a->archive, entry, "zip"); return ARCHIVE_FAILED; }; /* If we're not using Zip64, reject large files. */ if (zip->flags & ZIP_FLAG_AVOID_ZIP64) { /* Reject entries over 4GB. */ if (archive_entry_size_is_set(entry) && (archive_entry_size(entry) > ZIP_4GB_MAX)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Files > 4GB require Zip64 extensions"); return ARCHIVE_FAILED; } /* Reject entries if archive is > 4GB. */ if (zip->written_bytes > ZIP_4GB_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Archives > 4GB require Zip64 extensions"); return ARCHIVE_FAILED; } } /* Only regular files can have size > 0. */ if (type != AE_IFREG) archive_entry_set_size(entry, 0); /* Reset information from last entry. */ zip->entry_offset = zip->written_bytes; zip->entry_uncompressed_limit = INT64_MAX; zip->entry_compressed_size = 0; zip->entry_uncompressed_size = 0; zip->entry_compressed_written = 0; zip->entry_uncompressed_written = 0; zip->entry_flags = 0; zip->entry_uses_zip64 = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); zip->entry_encryption = 0; archive_entry_free(zip->entry); zip->entry = NULL; if (zip->cctx_valid) archive_encrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; if (type == AE_IFREG &&(!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)) { switch (zip->encryption_type) { case ENCRYPTION_TRADITIONAL: case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED; zip->entry_encryption = zip->encryption_type; break; + case ENCRYPTION_NONE: default: break; } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ zip->entry = __la_win_entry_in_posix_pathseparator(entry); if (zip->entry == entry) zip->entry = archive_entry_clone(entry); #else zip->entry = archive_entry_clone(entry); #endif if (zip->entry == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data"); return (ARCHIVE_FATAL); } if (sconv != NULL) { const char *p; size_t len; if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate Pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (len > 0) archive_entry_set_pathname(zip->entry, p); /* * There is no standard for symlink handling; we convert * it using the same character-set translation that we use * for filename. */ if (type == AE_IFLNK) { if (archive_entry_symlink_l(entry, &p, &len, sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory " " for Symlink"); return (ARCHIVE_FATAL); } /* No error if we can't convert. */ } else if (len > 0) archive_entry_set_symlink(zip->entry, p); } } /* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */ if (!is_all_ascii(archive_entry_pathname(zip->entry))) { if (zip->opt_sconv != NULL) { if (strcmp(archive_string_conversion_charset_name( zip->opt_sconv), "UTF-8") == 0) zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME; #if HAVE_NL_LANGINFO } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) { zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME; #endif } } filename_length = path_length(zip->entry); /* Determine appropriate compression and size for this entry. */ if (type == AE_IFLNK) { slink = archive_entry_symlink(zip->entry); if (slink != NULL) slink_size = strlen(slink); else slink_size = 0; zip->entry_uncompressed_limit = slink_size; zip->entry_compressed_size = slink_size; zip->entry_uncompressed_size = slink_size; zip->entry_crc32 = zip->crc32func(zip->entry_crc32, (const unsigned char *)slink, slink_size); zip->entry_compression = COMPRESSION_STORE; version_needed = 20; } else if (type != AE_IFREG) { zip->entry_compression = COMPRESSION_STORE; zip->entry_uncompressed_limit = 0; version_needed = 20; } else if (archive_entry_size_is_set(zip->entry)) { int64_t size = archive_entry_size(zip->entry); int64_t additional_size = 0; zip->entry_uncompressed_limit = size; zip->entry_compression = zip->requested_compression; if (zip->entry_compression == COMPRESSION_UNSPECIFIED) { zip->entry_compression = COMPRESSION_DEFAULT; } if (zip->entry_compression == COMPRESSION_STORE) { zip->entry_compressed_size = size; zip->entry_uncompressed_size = size; version_needed = 10; } else { zip->entry_uncompressed_size = size; version_needed = 20; } if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: additional_size = TRAD_HEADER_SIZE; version_needed = 20; break; case ENCRYPTION_WINZIP_AES128: additional_size = WINZIP_AES128_HEADER_SIZE + AUTH_CODE_SIZE; version_needed = 20; break; case ENCRYPTION_WINZIP_AES256: additional_size = WINZIP_AES256_HEADER_SIZE + AUTH_CODE_SIZE; version_needed = 20; break; + case ENCRYPTION_NONE: default: break; } if (zip->entry_compression == COMPRESSION_STORE) zip->entry_compressed_size += additional_size; } /* * Set Zip64 extension in any of the following cases * (this was suggested by discussion on info-zip-dev * mailing list): * = Zip64 is being forced by user * = File is over 4GiB uncompressed * (including encryption header, if any) * = File is close to 4GiB and is being compressed * (compression might make file larger) */ if ((zip->flags & ZIP_FLAG_FORCE_ZIP64) || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX) || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED && zip->entry_compression != COMPRESSION_STORE)) { zip->entry_uses_zip64 = 1; version_needed = 45; } /* We may know the size, but never the CRC. */ zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END; } else { /* We don't know the size. In this case, we prefer * deflate (it has a clear end-of-data marker which * makes length-at-end more reliable) and will * enable Zip64 extensions unless we're told not to. */ zip->entry_compression = COMPRESSION_DEFAULT; zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END; if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) { zip->entry_uses_zip64 = 1; version_needed = 45; } else if (zip->entry_compression == COMPRESSION_STORE) { version_needed = 10; } else { version_needed = 20; } if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: if (version_needed < 20) version_needed = 20; break; + case ENCRYPTION_NONE: default: break; } } } /* Format the local header. */ memset(local_header, 0, sizeof(local_header)); memcpy(local_header, "PK\003\004", 4); archive_le16enc(local_header + 4, version_needed); archive_le16enc(local_header + 6, zip->entry_flags); if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 || zip->entry_encryption == ENCRYPTION_WINZIP_AES256) archive_le16enc(local_header + 8, WINZIP_AES_ENCRYPTION); else archive_le16enc(local_header + 8, zip->entry_compression); archive_le32enc(local_header + 10, dos_time(archive_entry_mtime(zip->entry))); archive_le32enc(local_header + 14, zip->entry_crc32); if (zip->entry_uses_zip64) { /* Zip64 data in the local header "must" include both * compressed and uncompressed sizes AND those fields * are included only if these are 0xffffffff; * THEREFORE these must be set this way, even if we * know one of them is smaller. */ archive_le32enc(local_header + 18, ZIP_4GB_MAX); archive_le32enc(local_header + 22, ZIP_4GB_MAX); } else { archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size); archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size); } archive_le16enc(local_header + 26, (uint16_t)filename_length); if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) { if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) zip->trad_chkdat = local_header[11]; else zip->trad_chkdat = local_header[17]; } /* Format as much of central directory file header as we can: */ zip->file_header = cd_alloc(zip, 46); /* If (zip->file_header == NULL) XXXX */ ++zip->central_directory_entries; memset(zip->file_header, 0, 46); memcpy(zip->file_header, "PK\001\002", 4); /* "Made by PKZip 2.0 on Unix." */ archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed); archive_le16enc(zip->file_header + 6, version_needed); archive_le16enc(zip->file_header + 8, zip->entry_flags); if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 || zip->entry_encryption == ENCRYPTION_WINZIP_AES256) archive_le16enc(zip->file_header + 10, WINZIP_AES_ENCRYPTION); else archive_le16enc(zip->file_header + 10, zip->entry_compression); archive_le32enc(zip->file_header + 12, dos_time(archive_entry_mtime(zip->entry))); archive_le16enc(zip->file_header + 28, (uint16_t)filename_length); /* Following Info-Zip, store mode in the "external attributes" field. */ archive_le32enc(zip->file_header + 38, ((uint32_t)archive_entry_mode(zip->entry)) << 16); e = cd_alloc(zip, filename_length); /* If (e == NULL) XXXX */ copy_path(zip->entry, e); /* Format extra data. */ memset(local_extra, 0, sizeof(local_extra)); e = local_extra; /* First, extra blocks that are the same between * the local file header and the central directory. * We format them once and then duplicate them. */ /* UT timestamp, length depends on what timestamps are set. */ memcpy(e, "UT", 2); archive_le16enc(e + 2, 1 + (archive_entry_mtime_is_set(entry) ? 4 : 0) + (archive_entry_atime_is_set(entry) ? 4 : 0) + (archive_entry_ctime_is_set(entry) ? 4 : 0)); e += 4; *e++ = (archive_entry_mtime_is_set(entry) ? 1 : 0) | (archive_entry_atime_is_set(entry) ? 2 : 0) | (archive_entry_ctime_is_set(entry) ? 4 : 0); if (archive_entry_mtime_is_set(entry)) { archive_le32enc(e, (uint32_t)archive_entry_mtime(entry)); e += 4; } if (archive_entry_atime_is_set(entry)) { archive_le32enc(e, (uint32_t)archive_entry_atime(entry)); e += 4; } if (archive_entry_ctime_is_set(entry)) { archive_le32enc(e, (uint32_t)archive_entry_ctime(entry)); e += 4; } /* ux Unix extra data, length 11, version 1 */ /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */ memcpy(e, "ux\013\000\001", 5); e += 5; *e++ = 4; /* Length of following UID */ archive_le32enc(e, (uint32_t)archive_entry_uid(entry)); e += 4; *e++ = 4; /* Length of following GID */ archive_le32enc(e, (uint32_t)archive_entry_gid(entry)); e += 4; /* AES extra data field: WinZIP AES information, ID=0x9901 */ if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) { memcpy(e, "\001\231\007\000\001\000AE", 8); /* AES vendor version AE-2 does not store a CRC. * WinZip 11 uses AE-1, which does store the CRC, * but it does not store the CRC when the file size * is less than 20 bytes. So we simulate what * WinZip 11 does. * NOTE: WinZip 9.0 and 10.0 uses AE-2 by default. */ if (archive_entry_size_is_set(zip->entry) && archive_entry_size(zip->entry) < 20) { archive_le16enc(e+4, AES_VENDOR_AE_2); zip->aes_vendor = AES_VENDOR_AE_2;/* no CRC. */ } else zip->aes_vendor = AES_VENDOR_AE_1; e += 8; /* AES encryption strength. */ *e++ = (zip->entry_encryption == ENCRYPTION_WINZIP_AES128)?1:3; /* Actual compression method. */ archive_le16enc(e, zip->entry_compression); e += 2; } /* Copy UT ,ux, and AES-extra into central directory as well. */ zip->file_header_extra_offset = zip->central_directory_bytes; cd_extra = cd_alloc(zip, e - local_extra); memcpy(cd_extra, local_extra, e - local_extra); /* * Following extra blocks vary between local header and * central directory. These are the local header versions. * Central directory versions get formatted in * archive_write_zip_finish_entry() below. */ /* "[Zip64 entry] in the local header MUST include BOTH * original [uncompressed] and compressed size fields." */ if (zip->entry_uses_zip64) { unsigned char *zip64_start = e; memcpy(e, "\001\000\020\000", 4); e += 4; archive_le64enc(e, zip->entry_uncompressed_size); e += 8; archive_le64enc(e, zip->entry_compressed_size); e += 8; archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4))); } if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) { /* Experimental 'xl' extension to improve streaming. */ unsigned char *external_info = e; int included = 7; memcpy(e, "xl\000\000", 4); // 0x6c65 + 2-byte length e += 4; e[0] = included; /* bitmap of included fields */ e += 1; if (included & 1) { archive_le16enc(e, /* "Version created by" */ 3 * 256 + version_needed); e += 2; } if (included & 2) { archive_le16enc(e, 0); /* internal file attributes */ e += 2; } if (included & 4) { archive_le32enc(e, /* external file attributes */ ((uint32_t)archive_entry_mode(zip->entry)) << 16); e += 4; } if (included & 8) { // Libarchive does not currently support file comments. } archive_le16enc(external_info + 2, (uint16_t)(e - (external_info + 4))); } /* Update local header with size of extra data and write it all out: */ archive_le16enc(local_header + 28, (uint16_t)(e - local_extra)); ret = __archive_write_output(a, local_header, 30); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 30; ret = write_path(zip->entry, a); if (ret <= ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += ret; ret = __archive_write_output(a, local_extra, e - local_extra); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += e - local_extra; /* For symlinks, write the body now. */ if (slink != NULL) { ret = __archive_write_output(a, slink, slink_size); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->entry_compressed_written += slink_size; zip->entry_uncompressed_written += slink_size; zip->written_bytes += slink_size; } #ifdef HAVE_ZLIB_H if (zip->entry_compression == COMPRESSION_DEFLATE) { zip->stream.zalloc = Z_NULL; zip->stream.zfree = Z_NULL; zip->stream.opaque = Z_NULL; zip->stream.next_out = zip->buf; zip->stream.avail_out = (uInt)zip->len_buf; if (deflateInit2(&zip->stream, zip->deflate_compression_level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor"); return (ARCHIVE_FATAL); } } #endif return (ret2); } static ssize_t archive_write_zip_data(struct archive_write *a, const void *buff, size_t s) { int ret; struct zip *zip = a->format_data; if ((int64_t)s > zip->entry_uncompressed_limit) s = (size_t)zip->entry_uncompressed_limit; zip->entry_uncompressed_written += s; if (s == 0) return 0; if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: /* Initialize traditional PKWARE encryption context. */ if (!zip->tctx_valid) { ret = init_traditional_pkware_encryption(a); if (ret != ARCHIVE_OK) return (ret); zip->tctx_valid = 1; } break; case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: if (!zip->cctx_valid) { ret = init_winzip_aes_encryption(a); if (ret != ARCHIVE_OK) return (ret); zip->cctx_valid = zip->hctx_valid = 1; } break; + case ENCRYPTION_NONE: default: break; } } switch (zip->entry_compression) { case COMPRESSION_STORE: if (zip->tctx_valid || zip->cctx_valid) { const uint8_t *rb = (const uint8_t *)buff; const uint8_t * const re = rb + s; while (rb < re) { size_t l; if (zip->tctx_valid) { l = trad_enc_encrypt_update(&zip->tctx, rb, re - rb, zip->buf, zip->len_buf); } else { l = zip->len_buf; ret = archive_encrypto_aes_ctr_update( &zip->cctx, rb, re - rb, zip->buf, &l); if (ret < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to encrypt file"); return (ARCHIVE_FAILED); } archive_hmac_sha1_update(&zip->hctx, zip->buf, l); } ret = __archive_write_output(a, zip->buf, l); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += l; zip->written_bytes += l; rb += l; } } else { ret = __archive_write_output(a, buff, s); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += s; zip->entry_compressed_written += s; } break; #if HAVE_ZLIB_H case COMPRESSION_DEFLATE: zip->stream.next_in = (unsigned char*)(uintptr_t)buff; zip->stream.avail_in = (uInt)s; do { ret = deflate(&zip->stream, Z_NO_FLUSH); if (ret == Z_STREAM_ERROR) return (ARCHIVE_FATAL); if (zip->stream.avail_out == 0) { if (zip->tctx_valid) { trad_enc_encrypt_update(&zip->tctx, zip->buf, zip->len_buf, zip->buf, zip->len_buf); } else if (zip->cctx_valid) { size_t outl = zip->len_buf; ret = archive_encrypto_aes_ctr_update( &zip->cctx, zip->buf, zip->len_buf, zip->buf, &outl); if (ret < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to encrypt file"); return (ARCHIVE_FAILED); } archive_hmac_sha1_update(&zip->hctx, zip->buf, zip->len_buf); } ret = __archive_write_output(a, zip->buf, zip->len_buf); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += zip->len_buf; zip->written_bytes += zip->len_buf; zip->stream.next_out = zip->buf; zip->stream.avail_out = (uInt)zip->len_buf; } } while (zip->stream.avail_in != 0); break; #endif + case COMPRESSION_UNSPECIFIED: default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid ZIP compression type"); return ARCHIVE_FATAL; } zip->entry_uncompressed_limit -= s; if (!zip->cctx_valid || zip->aes_vendor != AES_VENDOR_AE_2) zip->entry_crc32 = zip->crc32func(zip->entry_crc32, buff, (unsigned)s); return (s); } static int archive_write_zip_finish_entry(struct archive_write *a) { struct zip *zip = a->format_data; int ret; #if HAVE_ZLIB_H if (zip->entry_compression == COMPRESSION_DEFLATE) { for (;;) { size_t remainder; ret = deflate(&zip->stream, Z_FINISH); if (ret == Z_STREAM_ERROR) return (ARCHIVE_FATAL); remainder = zip->len_buf - zip->stream.avail_out; if (zip->tctx_valid) { trad_enc_encrypt_update(&zip->tctx, zip->buf, remainder, zip->buf, remainder); } else if (zip->cctx_valid) { size_t outl = remainder; ret = archive_encrypto_aes_ctr_update( &zip->cctx, zip->buf, remainder, zip->buf, &outl); if (ret < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to encrypt file"); return (ARCHIVE_FAILED); } archive_hmac_sha1_update(&zip->hctx, zip->buf, remainder); } ret = __archive_write_output(a, zip->buf, remainder); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += remainder; zip->written_bytes += remainder; zip->stream.next_out = zip->buf; if (zip->stream.avail_out != 0) break; zip->stream.avail_out = (uInt)zip->len_buf; } deflateEnd(&zip->stream); } #endif if (zip->hctx_valid) { uint8_t hmac[20]; size_t hmac_len = 20; archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); ret = __archive_write_output(a, hmac, AUTH_CODE_SIZE); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += AUTH_CODE_SIZE; zip->written_bytes += AUTH_CODE_SIZE; } /* Write trailing data descriptor. */ if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) != 0) { char d[24]; memcpy(d, "PK\007\010", 4); if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2) archive_le32enc(d + 4, 0);/* no CRC.*/ else archive_le32enc(d + 4, zip->entry_crc32); if (zip->entry_uses_zip64) { archive_le64enc(d + 8, (uint64_t)zip->entry_compressed_written); archive_le64enc(d + 16, (uint64_t)zip->entry_uncompressed_written); ret = __archive_write_output(a, d, 24); zip->written_bytes += 24; } else { archive_le32enc(d + 8, (uint32_t)zip->entry_compressed_written); archive_le32enc(d + 12, (uint32_t)zip->entry_uncompressed_written); ret = __archive_write_output(a, d, 16); zip->written_bytes += 16; } if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Append Zip64 extra data to central directory information. */ if (zip->entry_compressed_written > ZIP_4GB_MAX || zip->entry_uncompressed_written > ZIP_4GB_MAX || zip->entry_offset > ZIP_4GB_MAX) { unsigned char zip64[32]; unsigned char *z = zip64, *zd; memcpy(z, "\001\000\000\000", 4); z += 4; if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) { archive_le64enc(z, zip->entry_uncompressed_written); z += 8; } if (zip->entry_compressed_written >= ZIP_4GB_MAX) { archive_le64enc(z, zip->entry_compressed_written); z += 8; } if (zip->entry_offset >= ZIP_4GB_MAX) { archive_le64enc(z, zip->entry_offset); z += 8; } archive_le16enc(zip64 + 2, (uint16_t)(z - (zip64 + 4))); zd = cd_alloc(zip, z - zip64); if (zd == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } memcpy(zd, zip64, z - zip64); /* Zip64 means version needs to be set to at least 4.5 */ if (archive_le16dec(zip->file_header + 6) < 45) archive_le16enc(zip->file_header + 6, 45); } /* Fix up central directory file header. */ if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2) archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/ else archive_le32enc(zip->file_header + 16, zip->entry_crc32); archive_le32enc(zip->file_header + 20, (uint32_t)zipmin(zip->entry_compressed_written, ZIP_4GB_MAX)); archive_le32enc(zip->file_header + 24, (uint32_t)zipmin(zip->entry_uncompressed_written, ZIP_4GB_MAX)); archive_le16enc(zip->file_header + 30, (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset)); archive_le32enc(zip->file_header + 42, (uint32_t)zipmin(zip->entry_offset, ZIP_4GB_MAX)); return (ARCHIVE_OK); } static int archive_write_zip_close(struct archive_write *a) { uint8_t buff[64]; int64_t offset_start, offset_end; struct zip *zip = a->format_data; struct cd_segment *segment; int ret; offset_start = zip->written_bytes; segment = zip->central_directory; while (segment != NULL) { ret = __archive_write_output(a, segment->buff, segment->p - segment->buff); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += segment->p - segment->buff; segment = segment->next; } offset_end = zip->written_bytes; /* If central dir info is too large, write Zip64 end-of-cd */ if (offset_end - offset_start > ZIP_4GB_MAX || offset_start > ZIP_4GB_MAX || zip->central_directory_entries > 0xffffUL || (zip->flags & ZIP_FLAG_FORCE_ZIP64)) { /* Zip64 end-of-cd record */ memset(buff, 0, 56); memcpy(buff, "PK\006\006", 4); archive_le64enc(buff + 4, 44); archive_le16enc(buff + 12, 45); archive_le16enc(buff + 14, 45); /* This is disk 0 of 0. */ archive_le64enc(buff + 24, zip->central_directory_entries); archive_le64enc(buff + 32, zip->central_directory_entries); archive_le64enc(buff + 40, offset_end - offset_start); archive_le64enc(buff + 48, offset_start); ret = __archive_write_output(a, buff, 56); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 56; /* Zip64 end-of-cd locator record. */ memset(buff, 0, 20); memcpy(buff, "PK\006\007", 4); archive_le32enc(buff + 4, 0); archive_le64enc(buff + 8, offset_end); archive_le32enc(buff + 16, 1); ret = __archive_write_output(a, buff, 20); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 20; } /* Format and write end of central directory. */ memset(buff, 0, sizeof(buff)); memcpy(buff, "PK\005\006", 4); archive_le16enc(buff + 8, (uint16_t)zipmin(0xffffU, zip->central_directory_entries)); archive_le16enc(buff + 10, (uint16_t)zipmin(0xffffU, zip->central_directory_entries)); archive_le32enc(buff + 12, (uint32_t)zipmin(ZIP_4GB_MAX, (offset_end - offset_start))); archive_le32enc(buff + 16, (uint32_t)zipmin(ZIP_4GB_MAX, offset_start)); ret = __archive_write_output(a, buff, 22); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 22; return (ARCHIVE_OK); } static int archive_write_zip_free(struct archive_write *a) { struct zip *zip; struct cd_segment *segment; zip = a->format_data; while (zip->central_directory != NULL) { segment = zip->central_directory; zip->central_directory = segment->next; free(segment->buff); free(segment); } free(zip->buf); archive_entry_free(zip->entry); if (zip->cctx_valid) archive_encrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); /* TODO: Free opt_sconv, sconv_default */ free(zip); a->format_data = NULL; return (ARCHIVE_OK); } /* Convert into MSDOS-style date/time. */ static unsigned int dos_time(const time_t unix_time) { struct tm *t; unsigned int dt; #if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) struct tm tmbuf; #endif #if defined(HAVE__LOCALTIME64_S) errno_t terr; __time64_t tmptime; #endif /* This will not preserve time when creating/extracting the archive * on two systems with different time zones. */ #if defined(HAVE_LOCALTIME_R) t = localtime_r(&unix_time, &tmbuf); #elif defined(HAVE__LOCALTIME64_S) tmptime = unix_time; terr = _localtime64_s(&tmbuf, &tmptime); if (terr) t = NULL; else t = &tmbuf; #else t = localtime(&unix_time); #endif /* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */ if (t->tm_year < 1980 - 1900) /* Set minimum date/time '1980-01-01 00:00:00'. */ dt = 0x00210000U; else if (t->tm_year > 2107 - 1900) /* Set maximum date/time '2107-12-31 23:59:58'. */ dt = 0xff9fbf7dU; else { dt = 0; dt += ((t->tm_year - 80) & 0x7f) << 9; dt += ((t->tm_mon + 1) & 0x0f) << 5; dt += (t->tm_mday & 0x1f); dt <<= 16; dt += (t->tm_hour & 0x1f) << 11; dt += (t->tm_min & 0x3f) << 5; dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */ } return dt; } static size_t path_length(struct archive_entry *entry) { mode_t type; const char *path; size_t len; type = archive_entry_filetype(entry); path = archive_entry_pathname(entry); if (path == NULL) return (0); len = strlen(path); if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/')) ++len; /* Space for the trailing / */ return len; } static int write_path(struct archive_entry *entry, struct archive_write *archive) { int ret; const char *path; mode_t type; size_t written_bytes; path = archive_entry_pathname(entry); type = archive_entry_filetype(entry); written_bytes = 0; if (path == NULL) return (ARCHIVE_FATAL); ret = __archive_write_output(archive, path, strlen(path)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); written_bytes += strlen(path); /* Folders are recognized by a trailing slash. */ if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) { ret = __archive_write_output(archive, "/", 1); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); written_bytes += 1; } return ((int)written_bytes); } static void copy_path(struct archive_entry *entry, unsigned char *p) { const char *path; size_t pathlen; mode_t type; path = archive_entry_pathname(entry); pathlen = strlen(path); type = archive_entry_filetype(entry); memcpy(p, path, pathlen); /* Folders are recognized by a trailing slash. */ if ((type == AE_IFDIR) && (path[pathlen - 1] != '/')) p[pathlen] = '/'; } static struct archive_string_conv * get_sconv(struct archive_write *a, struct zip *zip) { if (zip->opt_sconv != NULL) return (zip->opt_sconv); if (!zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_write(&(a->archive)); zip->init_default_conversion = 1; } return (zip->sconv_default); } /* Traditional PKWARE Decryption functions. */ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; #define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; t = (ctx->keys[1] >> 24) & 0xff; ctx->keys[2] = CRC32(ctx->keys[2], t); #undef CRC32 } static uint8_t trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; } static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { unsigned i, max; max = (unsigned)((in_len < out_len)? in_len: out_len); for (i = 0; i < max; i++) { uint8_t t = in[i]; out[i] = t ^ trad_enc_decrypt_byte(ctx); trad_enc_update_keys(ctx, t); } return i; } static int trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len) { ctx->keys[0] = 305419896L; ctx->keys[1] = 591751049L; ctx->keys[2] = 878082192L; for (;pw_len; --pw_len) trad_enc_update_keys(ctx, *pw++); return 0; } static int is_traditional_pkware_encryption_supported(void) { uint8_t key[TRAD_HEADER_SIZE]; if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) return (0); return (1); } static int init_traditional_pkware_encryption(struct archive_write *a) { struct zip *zip = a->format_data; const char *passphrase; uint8_t key[TRAD_HEADER_SIZE]; uint8_t key_encrypted[TRAD_HEADER_SIZE]; int ret; passphrase = __archive_write_get_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Encryption needs passphrase"); return ARCHIVE_FAILED; } if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't generate random number for encryption"); return ARCHIVE_FATAL; } trad_enc_init(&zip->tctx, passphrase, strlen(passphrase)); /* Set the last key code which will be used as a check code * for verifying passphrase in decryption. */ key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat; trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE, key_encrypted, TRAD_HEADER_SIZE); /* Write encrypted keys in the top of the file content. */ ret = __archive_write_output(a, key_encrypted, TRAD_HEADER_SIZE); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += TRAD_HEADER_SIZE; zip->entry_compressed_written += TRAD_HEADER_SIZE; return (ret); } static int init_winzip_aes_encryption(struct archive_write *a) { struct zip *zip = a->format_data; const char *passphrase; size_t key_len, salt_len; uint8_t salt[16 + 2]; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; int ret; passphrase = __archive_write_get_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Encryption needs passphrase"); return (ARCHIVE_FAILED); } if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128) { salt_len = 8; key_len = 16; } else { /* AES 256 */ salt_len = 16; key_len = 32; } if (archive_random(salt, salt_len) != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't generate random number for encryption"); return (ARCHIVE_FATAL); } archive_pbkdf2_sha1(passphrase, strlen(passphrase), salt, salt_len, 1000, derived_key, key_len * 2 + 2); ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); if (ret != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of crypto library"); return (ARCHIVE_FAILED); } ret = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); if (ret != 0) { archive_encrypto_aes_ctr_release(&zip->cctx); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize HMAC-SHA1"); return (ARCHIVE_FAILED); } /* Set a password verification value after the 'salt'. */ salt[salt_len] = derived_key[key_len * 2]; salt[salt_len + 1] = derived_key[key_len * 2 + 1]; /* Write encrypted keys in the top of the file content. */ ret = __archive_write_output(a, salt, salt_len + 2); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += salt_len + 2; zip->entry_compressed_written += salt_len + 2; return (ARCHIVE_OK); } static int is_winzip_aes_encryption_supported(int encryption) { size_t key_len, salt_len; uint8_t salt[16 + 2]; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; archive_crypto_ctx cctx; archive_hmac_sha1_ctx hctx; int ret; if (encryption == ENCRYPTION_WINZIP_AES128) { salt_len = 8; key_len = 16; } else { /* AES 256 */ salt_len = 16; key_len = 32; } if (archive_random(salt, salt_len) != ARCHIVE_OK) return (0); ret = archive_pbkdf2_sha1("p", 1, salt, salt_len, 1000, derived_key, key_len * 2 + 2); if (ret != 0) return (0); ret = archive_encrypto_aes_ctr_init(&cctx, derived_key, key_len); if (ret != 0) return (0); ret = archive_hmac_sha1_init(&hctx, derived_key + key_len, key_len); archive_encrypto_aes_ctr_release(&cctx); if (ret != 0) return (0); archive_hmac_sha1_cleanup(&hctx); return (1); } Index: stable/11/contrib/libarchive/libarchive/test/test_archive_read_next_header_empty.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_archive_read_next_header_empty.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_archive_read_next_header_empty.c (revision 368707) @@ -1,111 +1,125 @@ /*- * Copyright (c) 2003-2011 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); static void test_empty_file1(void) { struct archive* a = archive_read_new(); /* Try opening an empty file with the raw handler. */ assertEqualInt(ARCHIVE_OK, archive_read_support_format_raw(a)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); /* Raw handler doesn't support empty files. */ assertEqualInt(ARCHIVE_FATAL, archive_read_open_filename(a, "emptyfile", 0)); assert(NULL != archive_error_string(a)); archive_read_free(a); } static void -test_empty_file2(void) +test_empty_file2_check(struct archive* a) { - struct archive* a = archive_read_new(); struct archive_entry* e; - - /* Try opening an empty file with raw and empty handlers. */ - assertEqualInt(ARCHIVE_OK, archive_read_support_format_raw(a)); - assertEqualInt(ARCHIVE_OK, archive_read_support_format_empty(a)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); assertEqualInt(ARCHIVE_OK, archive_read_open_filename(a, "emptyfile", 0)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &e)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); archive_read_free(a); +} + +static void +test_empty_file2(void) +{ + struct archive* a = archive_read_new(); + + /* Try opening an empty file with raw and empty handlers. */ + assertEqualInt(ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualInt(ARCHIVE_OK, archive_read_support_format_empty(a)); + test_empty_file2_check(a); + + a = archive_read_new(); + assertEqualInt(ARCHIVE_OK, archive_read_support_format_by_code(a, ARCHIVE_FORMAT_EMPTY)); + test_empty_file2_check(a); + + a = archive_read_new(); + assertEqualInt(ARCHIVE_OK, archive_read_set_format(a, ARCHIVE_FORMAT_EMPTY)); + test_empty_file2_check(a); } static void test_empty_tarfile(void) { struct archive* a = archive_read_new(); struct archive_entry* e; /* Try opening an empty file with raw and empty handlers. */ assertEqualInt(ARCHIVE_OK, archive_read_support_format_tar(a)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); assertEqualInt(ARCHIVE_OK, archive_read_open_filename(a, "empty.tar", 0)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &e)); assertEqualInt(0, archive_errno(a)); assertEqualString(NULL, archive_error_string(a)); archive_read_free(a); } /* 512 zero bytes. */ static char nulls[512]; DEFINE_TEST(test_archive_read_next_header_empty) { FILE *f; /* Create an empty file. */ f = fopen("emptyfile", "wb"); fclose(f); /* Create a file with 512 zero bytes. */ f = fopen("empty.tar", "wb"); assertEqualInt(512, fwrite(nulls, 1, 512, f)); fclose(f); test_empty_file1(); test_empty_file2(); test_empty_tarfile(); } Index: stable/11/contrib/libarchive/libarchive/test/test_archive_read_support.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_archive_read_support.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_archive_read_support.c (revision 368707) @@ -1,98 +1,147 @@ /*- * Copyright (c) 2011 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); /* * Verify that the various archive_read_support_* functions * return appropriate errors when invoked on the wrong kind of * archive handle. */ typedef struct archive *constructor(void); typedef int enabler(struct archive *); typedef int destructor(struct archive *); +static int format_code = 0; +static int format_code_enabler(struct archive *a) +{ + return archive_read_support_format_by_code(a, format_code); +} + +static int format_code_setter(struct archive *a) +{ + return archive_read_set_format(a, format_code); +} + static void test_success(constructor new_, enabler enable_, destructor free_) { struct archive *a = new_(); int result = enable_(a); if (result == ARCHIVE_WARN) { assert(NULL != archive_error_string(a)); assertEqualIntA(a, -1, archive_errno(a)); } else { assertEqualIntA(a, ARCHIVE_OK, result); assert(NULL == archive_error_string(a)); assertEqualIntA(a, 0, archive_errno(a)); } free_(a); } static void test_failure(constructor new_, enabler enable_, destructor free_) { struct archive *a = new_(); assertEqualIntA(a, ARCHIVE_FATAL, enable_(a)); assert(NULL != archive_error_string(a)); assertEqualIntA(a, -1, archive_errno(a)); free_(a); } static void test_filter_or_format(enabler enable) { test_success(archive_read_new, enable, archive_read_free); test_failure(archive_write_new, enable, archive_write_free); test_failure(archive_read_disk_new, enable, archive_read_free); test_failure(archive_write_disk_new, enable, archive_write_free); } DEFINE_TEST(test_archive_read_support) { test_filter_or_format(archive_read_support_format_7zip); test_filter_or_format(archive_read_support_format_all); test_filter_or_format(archive_read_support_format_ar); test_filter_or_format(archive_read_support_format_cab); test_filter_or_format(archive_read_support_format_cpio); test_filter_or_format(archive_read_support_format_empty); test_filter_or_format(archive_read_support_format_iso9660); test_filter_or_format(archive_read_support_format_lha); test_filter_or_format(archive_read_support_format_mtree); test_filter_or_format(archive_read_support_format_tar); test_filter_or_format(archive_read_support_format_xar); test_filter_or_format(archive_read_support_format_zip); + + int format_codes[] = { + ARCHIVE_FORMAT_CPIO, + ARCHIVE_FORMAT_CPIO_POSIX, + ARCHIVE_FORMAT_CPIO_BIN_LE, + ARCHIVE_FORMAT_CPIO_BIN_BE, + ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, + ARCHIVE_FORMAT_CPIO_SVR4_CRC, + ARCHIVE_FORMAT_CPIO_AFIO_LARGE, + ARCHIVE_FORMAT_TAR, + ARCHIVE_FORMAT_TAR_USTAR, + ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, + ARCHIVE_FORMAT_TAR_PAX_RESTRICTED, + ARCHIVE_FORMAT_TAR_GNUTAR, + ARCHIVE_FORMAT_ISO9660, + ARCHIVE_FORMAT_ISO9660_ROCKRIDGE, + ARCHIVE_FORMAT_ZIP, + ARCHIVE_FORMAT_EMPTY, + ARCHIVE_FORMAT_AR, + ARCHIVE_FORMAT_AR_GNU, + ARCHIVE_FORMAT_AR_BSD, + ARCHIVE_FORMAT_MTREE, + ARCHIVE_FORMAT_RAW, + ARCHIVE_FORMAT_XAR, + ARCHIVE_FORMAT_LHA, + ARCHIVE_FORMAT_CAB, + ARCHIVE_FORMAT_RAR, + ARCHIVE_FORMAT_7ZIP, + ARCHIVE_FORMAT_WARC, + ARCHIVE_FORMAT_RAR_V5, + }; + unsigned int i; + + for (i = 0; i < sizeof(format_codes) / sizeof(int); i++) { + format_code = format_codes[i]; + test_filter_or_format(format_code_enabler); + test_filter_or_format(format_code_setter); + } test_filter_or_format(archive_read_support_filter_all); test_filter_or_format(archive_read_support_filter_bzip2); test_filter_or_format(archive_read_support_filter_compress); test_filter_or_format(archive_read_support_filter_gzip); test_filter_or_format(archive_read_support_filter_lzip); test_filter_or_format(archive_read_support_filter_lzma); test_filter_or_format(archive_read_support_filter_none); test_filter_or_format(archive_read_support_filter_rpm); test_filter_or_format(archive_read_support_filter_uu); test_filter_or_format(archive_read_support_filter_xz); } Index: stable/11/contrib/libarchive/libarchive/test/test_archive_string_conversion.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_archive_string_conversion.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_archive_string_conversion.c (revision 368707) @@ -1,810 +1,885 @@ /*- * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #include #define __LIBARCHIVE_TEST #include "archive_string.h" /* Execute the following to rebuild the data for this program: tail -n +36 test_archive_string_conversion.c | /bin/sh # # This requires http://unicode.org/Public/6.0.0/ucd/NormalizationTest.txt # if="NormalizationTest.txt" if [ ! -f ${if} ]; then echo "Not found: \"${if}\"" exit 0 fi of=test_archive_string_conversion.txt.Z echo "\$FreeBSD\$" > ${of}.uu awk -F ';' '$0 ~/^[0-9A-F]+/ {printf "%s;%s\n", $2, $3}' ${if} | compress | uuencode ${of} >> ${of}.uu exit 1 */ static int unicode_to_utf8(char *p, uint32_t uc) { char *_p = p; /* Translate code point to UTF8 */ if (uc <= 0x7f) { *p++ = (char)uc; } else if (uc <= 0x7ff) { *p++ = 0xc0 | ((uc >> 6) & 0x1f); *p++ = 0x80 | (uc & 0x3f); } else if (uc <= 0xffff) { *p++ = 0xe0 | ((uc >> 12) & 0x0f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); } else { *p++ = 0xf0 | ((uc >> 18) & 0x07); *p++ = 0x80 | ((uc >> 12) & 0x3f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); } return ((int)(p - _p)); } static void archive_be16enc(void *pp, uint16_t u) { unsigned char *p = (unsigned char *)pp; p[0] = (u >> 8) & 0xff; p[1] = u & 0xff; } static int unicode_to_utf16be(char *p, uint32_t uc) { char *utf16 = p; if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ uc -= 0x10000; archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00); return (4); } else { archive_be16enc(utf16, uc); return (2); } } static void archive_le16enc(void *pp, uint16_t u) { unsigned char *p = (unsigned char *)pp; p[0] = u & 0xff; p[1] = (u >> 8) & 0xff; } static size_t unicode_to_utf16le(char *p, uint32_t uc) { char *utf16 = p; if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ uc -= 0x10000; archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00); return (4); } else { archive_le16enc(utf16, uc); return (2); } } static int wc_size(void) { return (sizeof(wchar_t)); } static int unicode_to_wc(wchar_t *wp, uint32_t uc) { if (wc_size() == 4) { *wp = (wchar_t)uc; return (1); } if (uc > 0xffff) { /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ uc -= 0x10000; *wp++ = (wchar_t)(((uc >> 10) & 0x3ff) + 0xD800); *wp = (wchar_t)((uc & 0x3ff) + 0xDC00); return (2); } else { *wp = (wchar_t)uc; return (1); } } /* * Note: U+2000 - U+2FFF, U+F900 - U+FAFF and U+2F800 - U+2FAFF are not * converted to NFD on Mac OS. * see also http://developer.apple.com/library/mac/#qa/qa2001/qa1173.html */ static int scan_unicode_pattern(char *out, wchar_t *wout, char *u16be, char *u16le, const char *pattern, int mac_nfd) { unsigned uc = 0; const char *p = pattern; char *op = out; wchar_t *owp = wout; char *op16be = u16be; char *op16le = u16le; int ret = 0; for (;;) { if (*p >= '0' && *p <= '9') uc = (uc << 4) + (*p - '0'); else if (*p >= 'A' && *p <= 'F') uc = (uc << 4) + (*p - 'A' + 0x0a); else { if (mac_nfd && op == out) { /* * These are not converted to NFD on Mac OS. * U+2000 - U+2FFF * U+F900 - U+FAFF * U+2F800 - U+2FAFF */ switch (uc) { case 0x2194: case 0x219A: case 0x219B: case 0x21AE: case 0x21CD: case 0x21CE: case 0x21CF: case 0x2204: case 0x2209: case 0x220C: case 0x2224: case 0x2226: case 0x2241: case 0x2244: case 0x2247: case 0x2249: case 0x2260: case 0x2262: case 0x226D: case 0x226E: case 0x226F: case 0x2270: case 0x2271: case 0x2274: case 0x2275: case 0x2276: case 0x2278: case 0x2279: case 0x227A: case 0x227B: case 0x2280: case 0x2281: case 0x2284: case 0x2285: case 0x2288: case 0x2289: case 0x22AC: case 0x22AD: case 0x22AE: case 0x22AF: case 0x22E0: case 0x22E1: case 0x22E2: case 0x22E3: case 0x22EA: case 0x22EB: case 0x22EC: case 0x22ED: /* * Those code points are not converted to * NFD on Mac OS. I do not know the reason * because it is undocumented. * NFC NFD * 1109A ==> 11099 110BA * 1109C ==> 1109B 110BA * 110AB ==> 110A5 110BA */ case 0x1109A: case 0x1109C: case 0x110AB: ret = 1; break; } } op16be += unicode_to_utf16be(op16be, uc); op16le += unicode_to_utf16le(op16le, uc); owp += unicode_to_wc(owp, uc); op += unicode_to_utf8(op, uc); if (!*p) { *op16be++ = 0; *op16be = 0; *op16le++ = 0; *op16le = 0; *owp = L'\0'; *op = '\0'; break; } uc = 0; } p++; } return (ret); } static int is_wc_unicode(void) { #if defined(_WIN32) && !defined(__CYGWIN__) return (1); #else return (0); #endif } /* * A conversion test that we correctly normalize UTF-8 and UTF-16BE characters. * On Mac OS, the characters to be Form D. * On other platforms, the characters to be Form C. */ static void test_archive_string_normalization_nfc(const char *testdata) { struct archive *a, *a2; struct archive_string utf8; struct archive_mstring mstr; struct archive_string_conv *f_sconv8, *t_sconv8; struct archive_string_conv *f_sconv16be, *f_sconv16le; FILE *fp; char buff[512]; int line = 0; int locale_is_utf8, wc_is_unicode; int sconv_opt = SCONV_SET_OPT_NORMALIZATION_C; locale_is_utf8 = (NULL != setlocale(LC_ALL, "en_US.UTF-8")); wc_is_unicode = is_wc_unicode(); /* If it doesn't exist, just warn and return. */ if (!locale_is_utf8 && !wc_is_unicode) { skipping("A test of string normalization for NFC requires " "a suitable locale; en_US.UTF-8 not available on this " "system"); return; } archive_string_init(&utf8); memset(&mstr, 0, sizeof(mstr)); /* * Create string conversion objects. */ assert((a = archive_read_new()) != NULL); assertA(NULL != (f_sconv8 = archive_string_conversion_from_charset(a, "UTF-8", 0))); assertA(NULL != (f_sconv16be = archive_string_conversion_from_charset(a, "UTF-16BE", 0))); assertA(NULL != (f_sconv16le = archive_string_conversion_from_charset(a, "UTF-16LE", 0))); assert((a2 = archive_write_new()) != NULL); assertA(NULL != (t_sconv8 = archive_string_conversion_to_charset(a2, "UTF-8", 0))); if (f_sconv8 == NULL || f_sconv16be == NULL || f_sconv16le == NULL || t_sconv8 == NULL) { /* We cannot continue this test. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } archive_string_conversion_set_opt(f_sconv8, sconv_opt); archive_string_conversion_set_opt(f_sconv16be, sconv_opt); archive_string_conversion_set_opt(f_sconv16le, sconv_opt); archive_string_conversion_set_opt(t_sconv8, sconv_opt); /* Open a test pattern file. */ assert((fp = fopen(testdata, "r")) != NULL); /* * Read test data. * Test data format: * ';' '\n' * Unicode pattern format: * [0-9A-F]{4,5}([ ][0-9A-F]{4,5}){0,} */ while (fgets(buff, sizeof(buff), fp) != NULL) { char nfc[80], nfd[80]; char utf8_nfc[80], utf8_nfd[80]; char utf16be_nfc[80], utf16be_nfd[80]; char utf16le_nfc[80], utf16le_nfd[80]; wchar_t wc_nfc[40], wc_nfd[40]; char *e, *p; const wchar_t *wp; const char *mp; size_t mplen; line++; if (buff[0] == '#') continue; p = strchr(buff, ';'); if (p == NULL) continue; *p++ = '\0'; /* Copy an NFC pattern */ strncpy(nfc, buff, sizeof(nfc)-1); nfc[sizeof(nfc)-1] = '\0'; e = p; p = strchr(p, '\n'); if (p == NULL) continue; *p = '\0'; /* Copy an NFD pattern */ strncpy(nfd, e, sizeof(nfd)-1); nfd[sizeof(nfd)-1] = '\0'; /* * Get an NFC patterns. */ scan_unicode_pattern(utf8_nfc, wc_nfc, utf16be_nfc, utf16le_nfc, nfc, 0); /* * Get an NFD patterns. */ scan_unicode_pattern(utf8_nfd, wc_nfd, utf16be_nfd, utf16le_nfd, nfd, 0); if (locale_is_utf8) { /* * Normalize an NFD string for import. */ assertEqualInt(0, archive_strcpy_l( &utf8, utf8_nfd, f_sconv8)); failure("NFD(%s) should be converted to NFC(%s):%d", nfd, nfc, line); assertEqualUTF8String(utf8_nfc, utf8.s); /* * Normalize an NFC string for import. */ assertEqualInt(0, archive_strcpy_l( &utf8, utf8_nfc, f_sconv8)); failure("NFC(%s) should not be any changed:%d", nfc, line); assertEqualUTF8String(utf8_nfc, utf8.s); /* * Copy an NFC string for export. */ assertEqualInt(0, archive_strcpy_l( &utf8, utf8_nfc, t_sconv8)); failure("NFC(%s) should not be any changed:%d", nfc, line); assertEqualUTF8String(utf8_nfc, utf8.s); /* * Normalize an NFD string in UTF-16BE for import. */ assertEqualInt(0, archive_strncpy_l( &utf8, utf16be_nfd, 100000, f_sconv16be)); failure("NFD(%s) should be converted to NFC(%s):%d", nfd, nfc, line); assertEqualUTF8String(utf8_nfc, utf8.s); /* * Normalize an NFD string in UTF-16LE for import. */ assertEqualInt(0, archive_strncpy_l( &utf8, utf16le_nfd, 100000, f_sconv16le)); failure("NFD(%s) should be converted to NFC(%s):%d", nfd, nfc, line); assertEqualUTF8String(utf8_nfc, utf8.s); } /* * Test for archive_mstring interface. * In specific, Windows platform UTF-16BE is directly * converted to/from wide-character to avoid the effect of * current locale since windows platform cannot make * locale UTF-8. */ if (locale_is_utf8 || wc_is_unicode) { /* * Normalize an NFD string in UTF-8 for import. */ assertEqualInt(0, archive_mstring_copy_mbs_len_l( &mstr, utf8_nfd, 100000, f_sconv8)); assertEqualInt(0, archive_mstring_get_wcs(a, &mstr, &wp)); failure("UTF-8 NFD(%s) should be converted " "to WCS NFC(%s):%d", nfd, nfc, line); assertEqualWString(wc_nfc, wp); /* * Normalize an NFD string in UTF-16BE for import. */ assertEqualInt(0, archive_mstring_copy_mbs_len_l( &mstr, utf16be_nfd, 100000, f_sconv16be)); assertEqualInt(0, archive_mstring_get_wcs(a, &mstr, &wp)); failure("UTF-8 NFD(%s) should be converted " "to WCS NFC(%s):%d", nfd, nfc, line); assertEqualWString(wc_nfc, wp); /* * Normalize an NFD string in UTF-16LE for import. */ assertEqualInt(0, archive_mstring_copy_mbs_len_l( &mstr, utf16le_nfd, 100000, f_sconv16le)); assertEqualInt(0, archive_mstring_get_wcs(a, &mstr, &wp)); failure("UTF-8 NFD(%s) should be converted " "to WCS NFC(%s):%d", nfd, nfc, line); assertEqualWString(wc_nfc, wp); /* * Copy an NFC wide-string for export. */ assertEqualInt(0, archive_mstring_copy_wcs(&mstr, wc_nfc)); assertEqualInt(0, archive_mstring_get_mbs_l( - &mstr, &mp, &mplen, t_sconv8)); + a, &mstr, &mp, &mplen, t_sconv8)); failure("WCS NFC(%s) should be UTF-8 NFC:%d" ,nfc, line); assertEqualUTF8String(utf8_nfc, mp); } } archive_string_free(&utf8); archive_mstring_clean(&mstr); fclose(fp); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a2)); } static void test_archive_string_normalization_mac_nfd(const char *testdata) { struct archive *a, *a2; struct archive_string utf8; struct archive_mstring mstr; struct archive_string_conv *f_sconv8, *t_sconv8; struct archive_string_conv *f_sconv16be, *f_sconv16le; FILE *fp; char buff[512]; int line = 0; int locale_is_utf8, wc_is_unicode; int sconv_opt = SCONV_SET_OPT_NORMALIZATION_D; locale_is_utf8 = (NULL != setlocale(LC_ALL, "en_US.UTF-8")); wc_is_unicode = is_wc_unicode(); /* If it doesn't exist, just warn and return. */ if (!locale_is_utf8 && !wc_is_unicode) { skipping("A test of string normalization for NFD requires " "a suitable locale; en_US.UTF-8 not available on this " "system"); return; } archive_string_init(&utf8); memset(&mstr, 0, sizeof(mstr)); /* * Create string conversion objects. */ assert((a = archive_read_new()) != NULL); assertA(NULL != (f_sconv8 = archive_string_conversion_from_charset(a, "UTF-8", 0))); assertA(NULL != (f_sconv16be = archive_string_conversion_from_charset(a, "UTF-16BE", 0))); assertA(NULL != (f_sconv16le = archive_string_conversion_from_charset(a, "UTF-16LE", 0))); assert((a2 = archive_write_new()) != NULL); assertA(NULL != (t_sconv8 = archive_string_conversion_to_charset(a2, "UTF-8", 0))); if (f_sconv8 == NULL || f_sconv16be == NULL || f_sconv16le == NULL || t_sconv8 == NULL) { /* We cannot continue this test. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } archive_string_conversion_set_opt(f_sconv8, sconv_opt); archive_string_conversion_set_opt(f_sconv16be, sconv_opt); archive_string_conversion_set_opt(f_sconv16le, sconv_opt); archive_string_conversion_set_opt(t_sconv8, sconv_opt); /* Open a test pattern file. */ assert((fp = fopen(testdata, "r")) != NULL); /* * Read test data. * Test data format: * ';' '\n' * Unicode pattern format: * [0-9A-F]{4,5}([ ][0-9A-F]{4,5}){0,} */ while (fgets(buff, sizeof(buff), fp) != NULL) { char nfc[80], nfd[80]; char utf8_nfc[80], utf8_nfd[80]; char utf16be_nfc[80], utf16be_nfd[80]; char utf16le_nfc[80], utf16le_nfd[80]; wchar_t wc_nfc[40], wc_nfd[40]; char *e, *p; const wchar_t *wp; const char *mp; size_t mplen; int should_be_nfc; line++; if (buff[0] == '#') continue; p = strchr(buff, ';'); if (p == NULL) continue; *p++ = '\0'; /* Copy an NFC pattern */ strncpy(nfc, buff, sizeof(nfc)-1); nfc[sizeof(nfc)-1] = '\0'; e = p; p = strchr(p, '\n'); if (p == NULL) continue; *p = '\0'; /* Copy an NFD pattern */ strncpy(nfd, e, sizeof(nfd)-1); nfd[sizeof(nfd)-1] = '\0'; /* * Get an NFC patterns. */ should_be_nfc = scan_unicode_pattern(utf8_nfc, wc_nfc, utf16be_nfc, utf16le_nfc, nfc, 1); /* * Get an NFD patterns. */ scan_unicode_pattern(utf8_nfd, wc_nfd, utf16be_nfd, utf16le_nfd, nfd, 0); if (locale_is_utf8) { /* * Normalize an NFC string for import. */ assertEqualInt(0, archive_strcpy_l( &utf8, utf8_nfc, f_sconv8)); if (should_be_nfc) { failure("NFC(%s) should not be converted to" " NFD(%s):%d", nfc, nfd, line); assertEqualUTF8String(utf8_nfc, utf8.s); } else { failure("NFC(%s) should be converted to" " NFD(%s):%d", nfc, nfd, line); assertEqualUTF8String(utf8_nfd, utf8.s); } /* * Normalize an NFD string for import. */ assertEqualInt(0, archive_strcpy_l( &utf8, utf8_nfd, f_sconv8)); failure("NFD(%s) should not be any changed:%d", nfd, line); assertEqualUTF8String(utf8_nfd, utf8.s); /* * Copy an NFD string for export. */ assertEqualInt(0, archive_strcpy_l( &utf8, utf8_nfd, t_sconv8)); failure("NFD(%s) should not be any changed:%d", nfd, line); assertEqualUTF8String(utf8_nfd, utf8.s); /* * Normalize an NFC string in UTF-16BE for import. */ assertEqualInt(0, archive_strncpy_l( &utf8, utf16be_nfc, 100000, f_sconv16be)); if (should_be_nfc) { failure("NFC(%s) should not be converted to" " NFD(%s):%d", nfc, nfd, line); assertEqualUTF8String(utf8_nfc, utf8.s); } else { failure("NFC(%s) should be converted to" " NFD(%s):%d", nfc, nfd, line); assertEqualUTF8String(utf8_nfd, utf8.s); } /* * Normalize an NFC string in UTF-16LE for import. */ assertEqualInt(0, archive_strncpy_l( &utf8, utf16le_nfc, 100000, f_sconv16le)); if (should_be_nfc) { failure("NFC(%s) should not be converted to" " NFD(%s):%d", nfc, nfd, line); assertEqualUTF8String(utf8_nfc, utf8.s); } else { failure("NFC(%s) should be converted to" " NFD(%s):%d", nfc, nfd, line); assertEqualUTF8String(utf8_nfd, utf8.s); } } /* * Test for archive_mstring interface. * In specific, Windows platform UTF-16BE is directly * converted to/from wide-character to avoid the effect of * current locale since windows platform cannot make * locale UTF-8. */ if (locale_is_utf8 || wc_is_unicode) { /* * Normalize an NFD string in UTF-8 for import. */ assertEqualInt(0, archive_mstring_copy_mbs_len_l( &mstr, utf8_nfc, 100000, f_sconv8)); assertEqualInt(0, archive_mstring_get_wcs(a, &mstr, &wp)); if (should_be_nfc) { failure("UTF-8 NFC(%s) should not be converted " "to WCS NFD(%s):%d", nfc, nfd, line); assertEqualWString(wc_nfc, wp); } else { failure("UTF-8 NFC(%s) should be converted " "to WCS NFD(%s):%d", nfc, nfd, line); assertEqualWString(wc_nfd, wp); } /* * Normalize an NFD string in UTF-16BE for import. */ assertEqualInt(0, archive_mstring_copy_mbs_len_l( &mstr, utf16be_nfc, 100000, f_sconv16be)); assertEqualInt(0, archive_mstring_get_wcs(a, &mstr, &wp)); if (should_be_nfc) { failure("UTF-16BE NFC(%s) should not be " "converted to WCS NFD(%s):%d", nfc, nfd, line); assertEqualWString(wc_nfc, wp); } else { failure("UTF-16BE NFC(%s) should be converted " "to WCS NFD(%s):%d", nfc, nfd, line); assertEqualWString(wc_nfd, wp); } /* * Normalize an NFD string in UTF-16LE for import. */ assertEqualInt(0, archive_mstring_copy_mbs_len_l( &mstr, utf16le_nfc, 100000, f_sconv16le)); assertEqualInt(0, archive_mstring_get_wcs(a, &mstr, &wp)); if (should_be_nfc) { failure("UTF-16LE NFC(%s) should not be " "converted to WCS NFD(%s):%d", nfc, nfd, line); assertEqualWString(wc_nfc, wp); } else { failure("UTF-16LE NFC(%s) should be converted " "to WCS NFD(%s):%d", nfc, nfd, line); assertEqualWString(wc_nfd, wp); } /* * Copy an NFD wide-string for export. */ assertEqualInt(0, archive_mstring_copy_wcs( &mstr, wc_nfd)); assertEqualInt(0, archive_mstring_get_mbs_l( - &mstr, &mp, &mplen, t_sconv8)); + a, &mstr, &mp, &mplen, t_sconv8)); failure("WCS NFD(%s) should be UTF-8 NFD:%d" ,nfd, line); assertEqualUTF8String(utf8_nfd, mp); } } archive_string_free(&utf8); archive_mstring_clean(&mstr); fclose(fp); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a2)); } static void test_archive_string_canonicalization(void) { struct archive *a; struct archive_string_conv *sconv; setlocale(LC_ALL, "en_US.UTF-8"); assert((a = archive_read_new()) != NULL); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "UTF-8", 1))); failure("Charset name should be UTF-8"); assertEqualString("UTF-8", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "UTF8", 1))); failure("Charset name should be UTF-8"); assertEqualString("UTF-8", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "utf8", 1))); failure("Charset name should be UTF-8"); assertEqualString("UTF-8", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "UTF-16BE", 1))); failure("Charset name should be UTF-16BE"); assertEqualString("UTF-16BE", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "UTF16BE", 1))); failure("Charset name should be UTF-16BE"); assertEqualString("UTF-16BE", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "utf16be", 1))); failure("Charset name should be UTF-16BE"); assertEqualString("UTF-16BE", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "UTF-16LE", 1))); failure("Charset name should be UTF-16LE"); assertEqualString("UTF-16LE", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "UTF16LE", 1))); failure("Charset name should be UTF-16LE"); assertEqualString("UTF-16LE", archive_string_conversion_charset_name(sconv)); assertA(NULL != (sconv = archive_string_conversion_to_charset(a, "utf16le", 1))); failure("Charset name should be UTF-16LE"); assertEqualString("UTF-16LE", archive_string_conversion_charset_name(sconv)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +static void +check_string(struct archive *a, struct archive_mstring *mstr, struct archive_string_conv *sc, + const char *exp, const wchar_t *wexp) +{ + /* Do all the tests on a copy so that we can have a clear initial state every time */ + struct archive_mstring mstr2; + const char *p = NULL; + const wchar_t *wp = NULL; + size_t len = 0; + + memset(&mstr2, 0, sizeof(mstr2)); + + archive_mstring_copy(&mstr2, mstr); + assertEqualInt(0, archive_mstring_get_mbs(a, &mstr2, &p)); + assertEqualString(exp, p); + p = NULL; + + archive_mstring_copy(&mstr2, mstr); + assertEqualInt(0, archive_mstring_get_utf8(a, &mstr2, &p)); + assertEqualString(exp, p); + p = NULL; + + archive_mstring_copy(&mstr2, mstr); + assertEqualInt(0, archive_mstring_get_wcs(a, &mstr2, &wp)); + assertEqualWString(wexp, wp); + wp = NULL; + + archive_mstring_copy(&mstr2, mstr); + assertEqualInt(0, archive_mstring_get_mbs_l(a, &mstr2, &p, &len, sc)); + assertEqualString(exp, p); + assertEqualInt(len, strlen(exp)); + p = NULL; + len = 0; + + archive_mstring_clean(&mstr2); +} + +/* + * Make sure no matter what the input encoding is, the string can be + * converted too all the output encodings. + */ +static void +test_archive_string_set_get(void) +{ + struct archive *a; + struct archive_mstring mstr; + struct archive_string_conv *sc; + + setlocale(LC_ALL, "en_US.UTF-8"); + + assert((a = archive_read_new()) != NULL); + memset(&mstr, 0, sizeof(mstr)); + + assertA(NULL != (sc = + archive_string_conversion_to_charset(a, "UTF-8", 1))); + failure("Charset name should be UTF-8"); + assertEqualString("UTF-8", + archive_string_conversion_charset_name(sc)); + + assertEqualInt(0, archive_mstring_copy_mbs(&mstr, "AAA")); + check_string(a, &mstr, sc, "AAA", L"AAA"); + assertEqualInt(4, archive_mstring_copy_utf8(&mstr, "BBBB")); + check_string(a, &mstr, sc, "BBBB", L"BBBB"); + assertEqualInt(0, archive_mstring_copy_wcs(&mstr, L"CCC12")); + check_string(a, &mstr, sc, "CCC12", L"CCC12"); + assertEqualInt(0, archive_mstring_copy_mbs_len_l(&mstr, "DDDD-l", 6, sc)); + check_string(a, &mstr, sc, "DDDD-l", L"DDDD-l"); + assertEqualInt(0, archive_mstring_update_utf8(a, &mstr, "EEEEE---H")); + check_string(a, &mstr, sc, "EEEEE---H", L"EEEEE---H"); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + +} + DEFINE_TEST(test_archive_string_conversion) { static const char reffile[] = "test_archive_string_conversion.txt.Z"; static const char testdata[] = "testdata.txt"; struct archive *a; struct archive_entry *ae; char buff[512]; ssize_t size; FILE *fp; /* * Extract a test pattern file. */ extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 512)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assert((fp = fopen(testdata, "w")) != NULL); while ((size = archive_read_data(a, buff, 512)) > 0) assertEqualInt(size, fwrite(buff, 1, size, fp)); assertEqualInt(0, fclose(fp)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); test_archive_string_normalization_nfc(testdata); test_archive_string_normalization_mac_nfd(testdata); test_archive_string_canonicalization(); + test_archive_string_set_get(); } Index: stable/11/contrib/libarchive/libarchive/test/test_entry.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_entry.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_entry.c (revision 368707) @@ -1,891 +1,974 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #include #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif /* * Most of these tests are system-independent, though a few depend on * features of the local system. Such tests are conditionalized on * the platform name. On unsupported platforms, only the * system-independent features will be tested. * * No, I don't want to use config.h in the test files because I want * the tests to also serve as a check on the correctness of config.h. * A mis-configured library build should cause tests to fail. */ DEFINE_TEST(test_entry) { char buff[128]; wchar_t wbuff[128]; struct stat st; struct archive_entry *e, *e2; const struct stat *pst; unsigned long set, clear; /* For fflag testing. */ int type, permset, tag, qual; /* For ACL testing. */ const char *name; /* For ACL testing. */ const char *xname; /* For xattr tests. */ const void *xval; /* For xattr tests. */ size_t xsize; /* For xattr tests. */ wchar_t wc; long l; int i; assert((e = archive_entry_new()) != NULL); /* * Verify that the AE_IF* defines match S_IF* defines * on this platform. See comments in archive_entry.h. */ #ifdef S_IFREG assertEqualInt(S_IFREG, AE_IFREG); #endif #ifdef S_IFLNK assertEqualInt(S_IFLNK, AE_IFLNK); #endif #ifdef S_IFSOCK assertEqualInt(S_IFSOCK, AE_IFSOCK); #endif #ifdef S_IFCHR assertEqualInt(S_IFCHR, AE_IFCHR); #endif /* Work around MinGW, which defines S_IFBLK wrong. */ /* sourceforge.net/tracker/?func=detail&atid=102435&aid=1942809&group_id=2435 */ #if defined(S_IFBLK) && !defined(_WIN32) assertEqualInt(S_IFBLK, AE_IFBLK); #endif #ifdef S_IFDIR assertEqualInt(S_IFDIR, AE_IFDIR); #endif #ifdef S_IFIFO assertEqualInt(S_IFIFO, AE_IFIFO); #endif /* * Basic set/read tests for all fields. * We should be able to set any field and read * back the same value. * * For methods that "copy" a string, we should be able * to overwrite the original passed-in string without * changing the value in the 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_set_atime(e, 13580, 1000000001L); assertEqualInt(archive_entry_atime(e), 13581); assertEqualInt(archive_entry_atime_nsec(e), 1); archive_entry_set_atime(e, 13580, -7); assertEqualInt(archive_entry_atime(e), 13579); assertEqualInt(archive_entry_atime_nsec(e), 999999993); 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)); /* birthtime */ archive_entry_set_birthtime(e, 17579, 24990); assertEqualInt(archive_entry_birthtime(e), 17579); assertEqualInt(archive_entry_birthtime_nsec(e), 24990); archive_entry_set_birthtime(e, 17580, 1234567890L); assertEqualInt(archive_entry_birthtime(e), 17581); assertEqualInt(archive_entry_birthtime_nsec(e), 234567890); archive_entry_set_birthtime(e, 17581, -24990); assertEqualInt(archive_entry_birthtime(e), 17580); assertEqualInt(archive_entry_birthtime_nsec(e), 999975010); archive_entry_unset_birthtime(e); assertEqualInt(archive_entry_birthtime(e), 0); assertEqualInt(archive_entry_birthtime_nsec(e), 0); assert(!archive_entry_birthtime_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_set_ctime(e, 13581, 2008182348L); assertEqualInt(archive_entry_ctime(e), 13583); assertEqualInt(archive_entry_ctime_nsec(e), 8182348); archive_entry_set_ctime(e, 13582, -24681); assertEqualInt(archive_entry_ctime(e), 13581); assertEqualInt(archive_entry_ctime_nsec(e), 999975319); 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)); /* dev */ assert(!archive_entry_dev_is_set(e)); archive_entry_set_dev(e, 235); assert(archive_entry_dev_is_set(e)); assertEqualInt(archive_entry_dev(e), 235); /* devmajor/devminor are tested specially below. */ /* filetype */ archive_entry_set_filetype(e, AE_IFREG); assertEqualInt(archive_entry_filetype(e), AE_IFREG); /* 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"); + assertEqualString(archive_entry_gname_utf8(e), "group"); + assertEqualWString(archive_entry_gname_w(e), L"group"); wcscpy(wbuff, L"wgroup"); archive_entry_copy_gname_w(e, wbuff); assertEqualWString(archive_entry_gname_w(e), L"wgroup"); memset(wbuff, 0, sizeof(wbuff)); assertEqualWString(archive_entry_gname_w(e), L"wgroup"); + assertEqualString(archive_entry_gname_utf8(e), "wgroup"); + assertEqualString(archive_entry_gname(e), "wgroup"); + archive_entry_set_gname_utf8(e, "group"); + assertEqualString(archive_entry_gname_utf8(e), "group"); + assertEqualWString(archive_entry_gname_w(e), L"group"); + assertEqualString(archive_entry_gname(e), "group"); + archive_entry_update_gname_utf8(e, "group2"); + assertEqualString(archive_entry_gname_utf8(e), "group2"); + assertEqualWString(archive_entry_gname_w(e), L"group2"); + assertEqualString(archive_entry_gname(e), "group2"); /* hardlink */ archive_entry_set_hardlink(e, "hardlinkname"); assertEqualString(archive_entry_hardlink(e), "hardlinkname"); + assertEqualString(archive_entry_hardlink_utf8(e), "hardlinkname"); + assertEqualWString(archive_entry_hardlink_w(e), L"hardlinkname"); strcpy(buff, "hardlinkname2"); archive_entry_copy_hardlink(e, buff); assertEqualString(archive_entry_hardlink(e), "hardlinkname2"); + assertEqualWString(archive_entry_hardlink_w(e), L"hardlinkname2"); + assertEqualString(archive_entry_hardlink_utf8(e), "hardlinkname2"); memset(buff, 0, sizeof(buff)); assertEqualString(archive_entry_hardlink(e), "hardlinkname2"); + assertEqualString(archive_entry_hardlink_utf8(e), "hardlinkname2"); + assertEqualWString(archive_entry_hardlink_w(e), L"hardlinkname2"); archive_entry_copy_hardlink(e, NULL); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualWString(archive_entry_hardlink_w(e), NULL); + assertEqualString(archive_entry_hardlink_utf8(e), NULL); wcscpy(wbuff, L"whardlink"); archive_entry_copy_hardlink_w(e, wbuff); assertEqualWString(archive_entry_hardlink_w(e), L"whardlink"); + assertEqualString(archive_entry_hardlink_utf8(e), "whardlink"); + assertEqualString(archive_entry_hardlink(e), "whardlink"); memset(wbuff, 0, sizeof(wbuff)); assertEqualWString(archive_entry_hardlink_w(e), L"whardlink"); archive_entry_copy_hardlink_w(e, NULL); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualWString(archive_entry_hardlink_w(e), NULL); + archive_entry_set_hardlink_utf8(e, "hardlinkname"); + assertEqualString(archive_entry_hardlink_utf8(e), "hardlinkname"); + assertEqualWString(archive_entry_hardlink_w(e), L"hardlinkname"); + assertEqualString(archive_entry_hardlink(e), "hardlinkname"); + archive_entry_update_hardlink_utf8(e, "hardlinkname2"); + assertEqualString(archive_entry_hardlink_utf8(e), "hardlinkname2"); + assertEqualWString(archive_entry_hardlink_w(e), L"hardlinkname2"); + assertEqualString(archive_entry_hardlink(e), "hardlinkname2"); /* ino */ assert(!archive_entry_ino_is_set(e)); archive_entry_set_ino(e, 8593); assert(archive_entry_ino_is_set(e)); assertEqualInt(archive_entry_ino(e), 8593); assertEqualInt(archive_entry_ino64(e), 8593); archive_entry_set_ino64(e, 8594); assert(archive_entry_ino_is_set(e)); assertEqualInt(archive_entry_ino(e), 8594); assertEqualInt(archive_entry_ino64(e), 8594); /* link */ archive_entry_set_hardlink(e, "hardlinkname"); archive_entry_set_symlink(e, NULL); archive_entry_set_link(e, "link"); assertEqualString(archive_entry_hardlink(e), "link"); assertEqualString(archive_entry_symlink(e), NULL); archive_entry_copy_link(e, "link2"); assertEqualString(archive_entry_hardlink(e), "link2"); assertEqualString(archive_entry_symlink(e), NULL); archive_entry_copy_link_w(e, L"link3"); assertEqualString(archive_entry_hardlink(e), "link3"); assertEqualString(archive_entry_symlink(e), NULL); archive_entry_set_hardlink(e, NULL); archive_entry_set_symlink(e, "symlink"); archive_entry_set_link(e, "link"); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualString(archive_entry_symlink(e), "link"); archive_entry_copy_link(e, "link2"); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualString(archive_entry_symlink(e), "link2"); archive_entry_copy_link_w(e, L"link3"); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualString(archive_entry_symlink(e), "link3"); /* Arbitrarily override symlink if both hardlink and symlink set. */ archive_entry_set_hardlink(e, "hardlink"); archive_entry_set_symlink(e, "symlink"); archive_entry_set_link(e, "link"); assertEqualString(archive_entry_hardlink(e), "hardlink"); assertEqualString(archive_entry_symlink(e), "link"); /* 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_set_mtime(e, 13582, 1358297468); assertEqualInt(archive_entry_mtime(e), 13583); assertEqualInt(archive_entry_mtime_nsec(e), 358297468); archive_entry_set_mtime(e, 13583, -24682); assertEqualInt(archive_entry_mtime(e), 13582); assertEqualInt(archive_entry_mtime_nsec(e), 999975318); 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)); /* nlink */ archive_entry_set_nlink(e, 736); assertEqualInt(archive_entry_nlink(e), 736); /* pathname */ archive_entry_set_pathname(e, "path"); assertEqualString(archive_entry_pathname(e), "path"); + assertEqualString(archive_entry_pathname_utf8(e), "path"); + assertEqualWString(archive_entry_pathname_w(e), L"path"); archive_entry_set_pathname(e, "path"); assertEqualString(archive_entry_pathname(e), "path"); + assertEqualWString(archive_entry_pathname_w(e), L"path"); + assertEqualString(archive_entry_pathname_utf8(e), "path"); strcpy(buff, "path2"); archive_entry_copy_pathname(e, buff); assertEqualString(archive_entry_pathname(e), "path2"); + assertEqualWString(archive_entry_pathname_w(e), L"path2"); + assertEqualString(archive_entry_pathname_utf8(e), "path2"); memset(buff, 0, sizeof(buff)); assertEqualString(archive_entry_pathname(e), "path2"); + assertEqualString(archive_entry_pathname_utf8(e), "path2"); + assertEqualWString(archive_entry_pathname_w(e), L"path2"); wcscpy(wbuff, L"wpath"); archive_entry_copy_pathname_w(e, wbuff); assertEqualWString(archive_entry_pathname_w(e), L"wpath"); + assertEqualString(archive_entry_pathname_utf8(e), "wpath"); + assertEqualString(archive_entry_pathname(e), "wpath"); memset(wbuff, 0, sizeof(wbuff)); assertEqualWString(archive_entry_pathname_w(e), L"wpath"); + assertEqualString(archive_entry_pathname(e), "wpath"); + assertEqualString(archive_entry_pathname_utf8(e), "wpath"); + archive_entry_set_pathname_utf8(e, "path"); + assertEqualWString(archive_entry_pathname_w(e), L"path"); + assertEqualString(archive_entry_pathname(e), "path"); + assertEqualString(archive_entry_pathname_utf8(e), "path"); + archive_entry_update_pathname_utf8(e, "path2"); + assertEqualWString(archive_entry_pathname_w(e), L"path2"); + assertEqualString(archive_entry_pathname(e), "path2"); + assertEqualString(archive_entry_pathname_utf8(e), "path2"); /* rdev */ archive_entry_set_rdev(e, 532); assertEqualInt(archive_entry_rdev(e), 532); /* 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)); /* sourcepath */ archive_entry_copy_sourcepath(e, "path1"); assertEqualString(archive_entry_sourcepath(e), "path1"); /* symlink */ archive_entry_set_symlink(e, "symlinkname"); assertEqualString(archive_entry_symlink(e), "symlinkname"); + assertEqualString(archive_entry_symlink_utf8(e), "symlinkname"); + assertEqualWString(archive_entry_symlink_w(e), L"symlinkname"); strcpy(buff, "symlinkname2"); archive_entry_copy_symlink(e, buff); assertEqualString(archive_entry_symlink(e), "symlinkname2"); + assertEqualWString(archive_entry_symlink_w(e), L"symlinkname2"); + assertEqualString(archive_entry_symlink_utf8(e), "symlinkname2"); memset(buff, 0, sizeof(buff)); assertEqualString(archive_entry_symlink(e), "symlinkname2"); + assertEqualString(archive_entry_symlink_utf8(e), "symlinkname2"); + assertEqualWString(archive_entry_symlink_w(e), L"symlinkname2"); archive_entry_copy_symlink_w(e, NULL); assertEqualWString(archive_entry_symlink_w(e), NULL); assertEqualString(archive_entry_symlink(e), NULL); + assertEqualString(archive_entry_symlink_utf8(e), NULL); archive_entry_copy_symlink_w(e, L"wsymlink"); assertEqualWString(archive_entry_symlink_w(e), L"wsymlink"); + assertEqualString(archive_entry_symlink_utf8(e), "wsymlink"); + assertEqualString(archive_entry_symlink(e), "wsymlink"); archive_entry_copy_symlink(e, NULL); assertEqualWString(archive_entry_symlink_w(e), NULL); assertEqualString(archive_entry_symlink(e), NULL); + assertEqualString(archive_entry_symlink_utf8(e), NULL); + archive_entry_set_symlink_utf8(e, "symlinkname"); + assertEqualWString(archive_entry_symlink_w(e), L"symlinkname"); + assertEqualString(archive_entry_symlink(e), "symlinkname"); + assertEqualString(archive_entry_symlink_utf8(e), "symlinkname"); + archive_entry_update_symlink_utf8(e, "symlinkname2"); + assertEqualWString(archive_entry_symlink_w(e), L"symlinkname2"); + assertEqualString(archive_entry_symlink(e), "symlinkname2"); + assertEqualString(archive_entry_symlink_utf8(e), "symlinkname2"); /* 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"); + assertEqualString(archive_entry_uname_utf8(e), "user"); + assertEqualWString(archive_entry_uname_w(e), L"user"); wcscpy(wbuff, L"wuser"); - archive_entry_copy_gname_w(e, wbuff); - assertEqualWString(archive_entry_gname_w(e), L"wuser"); + archive_entry_copy_uname_w(e, wbuff); + assertEqualWString(archive_entry_uname_w(e), L"wuser"); memset(wbuff, 0, sizeof(wbuff)); - assertEqualWString(archive_entry_gname_w(e), L"wuser"); + assertEqualWString(archive_entry_uname_w(e), L"wuser"); + assertEqualString(archive_entry_uname_utf8(e), "wuser"); + assertEqualString(archive_entry_uname(e), "wuser"); + archive_entry_set_uname_utf8(e, "user"); + assertEqualString(archive_entry_uname_utf8(e), "user"); + assertEqualWString(archive_entry_uname_w(e), L"user"); + assertEqualString(archive_entry_uname(e), "user"); + archive_entry_set_uname_utf8(e, "user"); + assertEqualWString(archive_entry_uname_w(e), L"user"); + assertEqualString(archive_entry_uname(e), "user"); + assertEqualString(archive_entry_uname_utf8(e), "user"); + archive_entry_update_uname_utf8(e, "user2"); + assertEqualWString(archive_entry_uname_w(e), L"user2"); + assertEqualString(archive_entry_uname(e), "user2"); + assertEqualString(archive_entry_uname_utf8(e), "user2"); /* Test fflags interface. */ archive_entry_set_fflags(e, 0x55, 0xAA); archive_entry_fflags(e, &set, &clear); failure("Testing set/get of fflags data."); assertEqualInt(set, 0x55); failure("Testing set/get of fflags data."); assertEqualInt(clear, 0xAA); #ifdef __FreeBSD__ /* Converting fflags bitmap to string is currently system-dependent. */ /* TODO: Make this system-independent. */ assertEqualString(archive_entry_fflags_text(e), "uappnd,nouchg,nodump,noopaque,uunlnk,nosystem"); #endif #if defined(__FreeBSD__) || defined(__APPLE__) /* Test archive_entry_copy_fflags_text_w() */ archive_entry_copy_fflags_text_w(e, L" ,nouappnd, nouchg, dump,hidden"); archive_entry_fflags(e, &set, &clear); assertEqualInt(UF_HIDDEN, set); assertEqualInt(UF_NODUMP | UF_IMMUTABLE | UF_APPEND, clear); /* Test archive_entry_copy_fflags_text() */ archive_entry_copy_fflags_text(e, " ,nouappnd, nouchg, dump,hidden"); archive_entry_fflags(e, &set, &clear); assertEqualInt(UF_HIDDEN, set); assertEqualInt(UF_NODUMP | UF_IMMUTABLE | UF_APPEND, clear); #elif defined(_WIN32) && !defined(CYGWIN) archive_entry_copy_fflags_text_w(e, L"rdonly,hidden,nosystem"); archive_entry_fflags(e, &set, &clear); assertEqualInt(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, set); assertEqualInt(FILE_ATTRIBUTE_SYSTEM, clear); archive_entry_copy_fflags_text(e, "rdonly,hidden,nosystem"); archive_entry_fflags(e, &set, &clear); assertEqualInt(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, set); assertEqualInt(FILE_ATTRIBUTE_SYSTEM, clear); #elif defined FS_IOC_GETFLAGS /* Linux */ archive_entry_copy_fflags_text_w(e, L"sappnd,schg,dump,noundel"); archive_entry_fflags(e, &set, &clear); assertEqualInt(FS_APPEND_FL | FS_IMMUTABLE_FL, set); assertEqualInt(FS_NODUMP_FL | FS_UNRM_FL, clear); archive_entry_copy_fflags_text(e, "sappnd,schg,dump,noundel"); archive_entry_fflags(e, &set, &clear); assertEqualInt(FS_APPEND_FL | FS_IMMUTABLE_FL, set); assertEqualInt(FS_NODUMP_FL | FS_UNRM_FL, clear); #endif /* See test_acl_basic.c for tests of ACL set/get consistency. */ /* Test xattrs set/get consistency. */ archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12); assertEqualInt(1, archive_entry_xattr_reset(e)); assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize)); assertEqualString(xname, "xattr1"); assertEqualString(xval, "xattrvalue1"); assertEqualInt((int)xsize, 12); assertEqualInt(1, archive_entry_xattr_count(e)); assertEqualInt(ARCHIVE_WARN, archive_entry_xattr_next(e, &xname, &xval, &xsize)); assertEqualString(xname, NULL); assertEqualString(xval, NULL); assertEqualInt((int)xsize, 0); archive_entry_xattr_clear(e); assertEqualInt(0, archive_entry_xattr_reset(e)); assertEqualInt(ARCHIVE_WARN, archive_entry_xattr_next(e, &xname, &xval, &xsize)); assertEqualString(xname, NULL); assertEqualString(xval, NULL); assertEqualInt((int)xsize, 0); archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12); assertEqualInt(1, archive_entry_xattr_reset(e)); archive_entry_xattr_add_entry(e, "xattr2", "xattrvalue2", 12); assertEqualInt(2, archive_entry_xattr_reset(e)); assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize)); assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize)); assertEqualInt(ARCHIVE_WARN, archive_entry_xattr_next(e, &xname, &xval, &xsize)); assertEqualString(xname, NULL); assertEqualString(xval, NULL); assertEqualInt((int)xsize, 0); /* * Test clone() implementation. */ /* Set values in 'e' */ archive_entry_clear(e); archive_entry_set_atime(e, 13579, 24680); archive_entry_set_birthtime(e, 13779, 24990); archive_entry_set_ctime(e, 13580, 24681); archive_entry_set_dev(e, 235); archive_entry_set_fflags(e, 0x55, 0xAA); archive_entry_set_gid(e, 204); archive_entry_set_gname(e, "group"); archive_entry_set_hardlink(e, "hardlinkname"); archive_entry_set_ino(e, 8593); archive_entry_set_mode(e, 0123456); archive_entry_set_mtime(e, 13581, 24682); archive_entry_set_nlink(e, 736); archive_entry_set_pathname(e, "path"); archive_entry_set_rdev(e, 532); archive_entry_set_size(e, 987654321); archive_entry_copy_sourcepath(e, "source"); archive_entry_set_symlink(e, "symlinkname"); archive_entry_set_uid(e, 83); archive_entry_set_uname(e, "user"); /* Add an ACL entry. */ archive_entry_acl_add_entry(e, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77"); /* Add an extended attribute. */ archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue", 11); /* Make a clone. */ e2 = archive_entry_clone(e); /* Clone should have same contents. */ assertEqualInt(archive_entry_atime(e2), 13579); assertEqualInt(archive_entry_atime_nsec(e2), 24680); assertEqualInt(archive_entry_birthtime(e2), 13779); assertEqualInt(archive_entry_birthtime_nsec(e2), 24990); assertEqualInt(archive_entry_ctime(e2), 13580); assertEqualInt(archive_entry_ctime_nsec(e2), 24681); assertEqualInt(archive_entry_dev(e2), 235); archive_entry_fflags(e, &set, &clear); assertEqualInt(clear, 0xAA); assertEqualInt(set, 0x55); assertEqualInt(archive_entry_gid(e2), 204); assertEqualString(archive_entry_gname(e2), "group"); assertEqualString(archive_entry_hardlink(e2), "hardlinkname"); assertEqualInt(archive_entry_ino(e2), 8593); assertEqualInt(archive_entry_mode(e2), 0123456); assertEqualInt(archive_entry_mtime(e2), 13581); assertEqualInt(archive_entry_mtime_nsec(e2), 24682); assertEqualInt(archive_entry_nlink(e2), 736); assertEqualString(archive_entry_pathname(e2), "path"); assertEqualInt(archive_entry_rdev(e2), 532); assertEqualInt(archive_entry_size(e2), 987654321); assertEqualString(archive_entry_sourcepath(e2), "source"); assertEqualString(archive_entry_symlink(e2), "symlinkname"); assertEqualInt(archive_entry_uid(e2), 83); assertEqualString(archive_entry_uname(e2), "user"); /* Verify ACL was copied. */ assertEqualInt(4, archive_entry_acl_reset(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); /* First three are standard permission bits. */ assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, 4); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER_OBJ); assertEqualInt(qual, -1); assertEqualString(name, NULL); assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, 5); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_GROUP_OBJ); assertEqualInt(qual, -1); assertEqualString(name, NULL); assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, 6); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_OTHER); assertEqualInt(qual, -1); assertEqualString(name, NULL); /* Fourth is custom one. */ assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, ARCHIVE_ENTRY_ACL_READ); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER); assertEqualInt(qual, 77); assertEqualString(name, "user77"); /* Verify xattr was copied. */ assertEqualInt(1, archive_entry_xattr_reset(e2)); assertEqualInt(0, archive_entry_xattr_next(e2, &xname, &xval, &xsize)); assertEqualString(xname, "xattr1"); assertEqualString(xval, "xattrvalue"); assertEqualInt((int)xsize, 11); assertEqualInt(ARCHIVE_WARN, archive_entry_xattr_next(e2, &xname, &xval, &xsize)); assertEqualString(xname, NULL); assertEqualString(xval, NULL); assertEqualInt((int)xsize, 0); /* Change the original */ archive_entry_set_atime(e, 13580, 24690); archive_entry_set_birthtime(e, 13980, 24999); archive_entry_set_ctime(e, 13590, 24691); archive_entry_set_dev(e, 245); archive_entry_set_fflags(e, 0x85, 0xDA); archive_entry_set_filetype(e, AE_IFLNK); archive_entry_set_gid(e, 214); archive_entry_set_gname(e, "grouper"); archive_entry_set_hardlink(e, "hardlinkpath"); archive_entry_set_ino(e, 8763); archive_entry_set_mode(e, 0123654); archive_entry_set_mtime(e, 18351, 28642); archive_entry_set_nlink(e, 73); archive_entry_set_pathname(e, "pathest"); archive_entry_set_rdev(e, 132); archive_entry_set_size(e, 987456321); archive_entry_copy_sourcepath(e, "source2"); archive_entry_set_symlink(e, "symlinkpath"); archive_entry_set_uid(e, 93); archive_entry_set_uname(e, "username"); archive_entry_acl_clear(e); archive_entry_xattr_clear(e); /* Clone should still have same contents. */ assertEqualInt(archive_entry_atime(e2), 13579); assertEqualInt(archive_entry_atime_nsec(e2), 24680); assertEqualInt(archive_entry_birthtime(e2), 13779); assertEqualInt(archive_entry_birthtime_nsec(e2), 24990); assertEqualInt(archive_entry_ctime(e2), 13580); assertEqualInt(archive_entry_ctime_nsec(e2), 24681); assertEqualInt(archive_entry_dev(e2), 235); archive_entry_fflags(e2, &set, &clear); assertEqualInt(clear, 0xAA); assertEqualInt(set, 0x55); assertEqualInt(archive_entry_gid(e2), 204); assertEqualString(archive_entry_gname(e2), "group"); assertEqualString(archive_entry_hardlink(e2), "hardlinkname"); assertEqualInt(archive_entry_ino(e2), 8593); assertEqualInt(archive_entry_mode(e2), 0123456); assertEqualInt(archive_entry_mtime(e2), 13581); assertEqualInt(archive_entry_mtime_nsec(e2), 24682); assertEqualInt(archive_entry_nlink(e2), 736); assertEqualString(archive_entry_pathname(e2), "path"); assertEqualInt(archive_entry_rdev(e2), 532); assertEqualInt(archive_entry_size(e2), 987654321); assertEqualString(archive_entry_sourcepath(e2), "source"); assertEqualString(archive_entry_symlink(e2), "symlinkname"); assertEqualInt(archive_entry_uid(e2), 83); assertEqualString(archive_entry_uname(e2), "user"); /* Verify ACL was unchanged. */ assertEqualInt(4, archive_entry_acl_reset(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); /* First three are standard permission bits. */ assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, 4); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER_OBJ); assertEqualInt(qual, -1); assertEqualString(name, NULL); assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, 5); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_GROUP_OBJ); assertEqualInt(qual, -1); assertEqualString(name, NULL); assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, 6); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_OTHER); assertEqualInt(qual, -1); assertEqualString(name, NULL); /* Fourth is custom one. */ assertEqualInt(0, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); assertEqualInt(permset, ARCHIVE_ENTRY_ACL_READ); assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER); assertEqualInt(qual, 77); assertEqualString(name, "user77"); assertEqualInt(1, archive_entry_acl_next(e2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, &type, &permset, &tag, &qual, &name)); assertEqualInt(type, 0); assertEqualInt(permset, 0); assertEqualInt(tag, 0); assertEqualInt(qual, -1); assertEqualString(name, NULL); /* Verify xattr was unchanged. */ assertEqualInt(1, archive_entry_xattr_reset(e2)); /* Release clone. */ archive_entry_free(e2); /* * Test clear() implementation. */ archive_entry_clear(e); assertEqualInt(archive_entry_atime(e), 0); assertEqualInt(archive_entry_atime_nsec(e), 0); assertEqualInt(archive_entry_birthtime(e), 0); assertEqualInt(archive_entry_birthtime_nsec(e), 0); assertEqualInt(archive_entry_ctime(e), 0); assertEqualInt(archive_entry_ctime_nsec(e), 0); assertEqualInt(archive_entry_dev(e), 0); archive_entry_fflags(e, &set, &clear); assertEqualInt(clear, 0); assertEqualInt(set, 0); assertEqualInt(archive_entry_filetype(e), 0); assertEqualInt(archive_entry_gid(e), 0); assertEqualString(archive_entry_gname(e), NULL); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualInt(archive_entry_ino(e), 0); assertEqualInt(archive_entry_mode(e), 0); assertEqualInt(archive_entry_mtime(e), 0); assertEqualInt(archive_entry_mtime_nsec(e), 0); assertEqualInt(archive_entry_nlink(e), 0); assertEqualString(archive_entry_pathname(e), NULL); assertEqualInt(archive_entry_rdev(e), 0); assertEqualInt(archive_entry_size(e), 0); assertEqualString(archive_entry_symlink(e), NULL); assertEqualInt(archive_entry_uid(e), 0); assertEqualString(archive_entry_uname(e), NULL); /* ACLs should be cleared. */ assertEqualInt(archive_entry_acl_count(e, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), 0); assertEqualInt(archive_entry_acl_count(e, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT), 0); /* Extended attributes should be cleared. */ assertEqualInt(archive_entry_xattr_count(e), 0); /* * Test archive_entry_copy_stat(). */ memset(&st, 0, sizeof(st)); /* Set all of the standard 'struct stat' fields. */ st.st_atime = 456789; st.st_ctime = 345678; st.st_dev = 123; st.st_gid = 34; st.st_ino = 234; st.st_mode = 077777; st.st_mtime = 234567; st.st_nlink = 345; st.st_size = 123456789; st.st_uid = 23; #ifdef __FreeBSD__ /* On FreeBSD, high-res timestamp data should come through. */ st.st_atimespec.tv_nsec = 6543210; st.st_ctimespec.tv_nsec = 5432109; st.st_mtimespec.tv_nsec = 3210987; st.st_birthtimespec.tv_nsec = 7459386; #endif /* Copy them into the entry. */ archive_entry_copy_stat(e, &st); /* Read each one back separately and compare. */ assertEqualInt(archive_entry_atime(e), 456789); assertEqualInt(archive_entry_ctime(e), 345678); assertEqualInt(archive_entry_dev(e), 123); assertEqualInt(archive_entry_gid(e), 34); assertEqualInt(archive_entry_ino(e), 234); assertEqualInt(archive_entry_mode(e), 077777); assertEqualInt(archive_entry_mtime(e), 234567); assertEqualInt(archive_entry_nlink(e), 345); assertEqualInt(archive_entry_size(e), 123456789); assertEqualInt(archive_entry_uid(e), 23); #if __FreeBSD__ /* On FreeBSD, high-res timestamp data should come through. */ assertEqualInt(archive_entry_atime_nsec(e), 6543210); assertEqualInt(archive_entry_ctime_nsec(e), 5432109); assertEqualInt(archive_entry_mtime_nsec(e), 3210987); assertEqualInt(archive_entry_birthtime_nsec(e), 7459386); #endif /* * Test archive_entry_stat(). */ /* First, clear out any existing stat data. */ memset(&st, 0, sizeof(st)); archive_entry_copy_stat(e, &st); /* Set a bunch of fields individually. */ archive_entry_set_atime(e, 456789, 321); archive_entry_set_ctime(e, 345678, 432); archive_entry_set_dev(e, 123); archive_entry_set_gid(e, 34); archive_entry_set_ino(e, 234); archive_entry_set_mode(e, 012345); archive_entry_set_mode(e, 012345); archive_entry_set_mtime(e, 234567, 543); archive_entry_set_nlink(e, 345); archive_entry_set_size(e, 123456789); archive_entry_set_uid(e, 23); /* Retrieve a stat structure. */ assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; /* Check that the values match. */ assertEqualInt(pst->st_atime, 456789); assertEqualInt(pst->st_ctime, 345678); assertEqualInt(pst->st_dev, 123); assertEqualInt(pst->st_gid, 34); assertEqualInt(pst->st_ino, 234); assertEqualInt(pst->st_mode, 012345); assertEqualInt(pst->st_mtime, 234567); assertEqualInt(pst->st_nlink, 345); assertEqualInt(pst->st_size, 123456789); assertEqualInt(pst->st_uid, 23); #ifdef __FreeBSD__ /* On FreeBSD, high-res timestamp data should come through. */ assertEqualInt(pst->st_atimespec.tv_nsec, 321); assertEqualInt(pst->st_ctimespec.tv_nsec, 432); assertEqualInt(pst->st_mtimespec.tv_nsec, 543); #endif /* Changing any one value should update struct stat. */ archive_entry_set_atime(e, 456788, 0); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_atime, 456788); archive_entry_set_ctime(e, 345677, 431); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_ctime, 345677); archive_entry_set_dev(e, 122); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_dev, 122); archive_entry_set_gid(e, 33); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_gid, 33); archive_entry_set_ino(e, 233); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_ino, 233); archive_entry_set_mode(e, 012344); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_mode, 012344); archive_entry_set_mtime(e, 234566, 542); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_mtime, 234566); archive_entry_set_nlink(e, 344); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_nlink, 344); archive_entry_set_size(e, 123456788); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_size, 123456788); archive_entry_set_uid(e, 22); assert((pst = archive_entry_stat(e)) != NULL); if (pst == NULL) return; assertEqualInt(pst->st_uid, 22); /* We don't need to check high-res fields here. */ /* * Test dev/major/minor interfaces. Setting 'dev' or 'rdev' * should change the corresponding major/minor values, and * vice versa. * * The test here is system-specific because it assumes that * makedev(), major(), and minor() are defined in sys/stat.h. * I'm not too worried about it, though, because the code is * simple. If it works on FreeBSD, it's unlikely to be broken * anywhere else. Note: The functionality is present on every * platform even if these tests only run some places; * libarchive's more extensive configuration logic should find * the necessary definitions on every platform. */ #if __FreeBSD__ archive_entry_set_dev(e, 0x12345678); assertEqualInt(archive_entry_devmajor(e), major(0x12345678)); assertEqualInt(archive_entry_devminor(e), minor(0x12345678)); assertEqualInt(archive_entry_dev(e), 0x12345678); archive_entry_set_devmajor(e, 0xfe); archive_entry_set_devminor(e, 0xdcba98); assertEqualInt(archive_entry_devmajor(e), 0xfe); assertEqualInt(archive_entry_devminor(e), 0xdcba98); assertEqualInt(archive_entry_dev(e), makedev(0xfe, 0xdcba98)); archive_entry_set_rdev(e, 0x12345678); assertEqualInt(archive_entry_rdevmajor(e), major(0x12345678)); assertEqualInt(archive_entry_rdevminor(e), minor(0x12345678)); assertEqualInt(archive_entry_rdev(e), 0x12345678); archive_entry_set_rdevmajor(e, 0xfe); archive_entry_set_rdevminor(e, 0xdcba98); assertEqualInt(archive_entry_rdevmajor(e), 0xfe); assertEqualInt(archive_entry_rdevminor(e), 0xdcba98); assertEqualInt(archive_entry_rdev(e), makedev(0xfe, 0xdcba98)); #endif /* * Exercise the character-conversion logic, if we can. */ if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { skipping("Can't exercise charset-conversion logic without" " a suitable locale."); } else { /* A filename that cannot be converted to wide characters. */ archive_entry_copy_pathname(e, "abc\314\214mno\374xyz"); failure("Converting invalid chars to Unicode should fail."); assert(NULL == archive_entry_pathname_w(e)); /* failure("Converting invalid chars to UTF-8 should fail."); assert(NULL == archive_entry_pathname_utf8(e)); */ /* A group name that cannot be converted. */ archive_entry_copy_gname(e, "abc\314\214mno\374xyz"); failure("Converting invalid chars to Unicode should fail."); assert(NULL == archive_entry_gname_w(e)); /* A user name that cannot be converted. */ archive_entry_copy_uname(e, "abc\314\214mno\374xyz"); failure("Converting invalid chars to Unicode should fail."); assert(NULL == archive_entry_uname_w(e)); /* A hardlink target that cannot be converted. */ archive_entry_copy_hardlink(e, "abc\314\214mno\374xyz"); failure("Converting invalid chars to Unicode should fail."); assert(NULL == archive_entry_hardlink_w(e)); /* A symlink target that cannot be converted. */ archive_entry_copy_symlink(e, "abc\314\214mno\374xyz"); failure("Converting invalid chars to Unicode should fail."); assert(NULL == archive_entry_symlink_w(e)); } l = 0x12345678L; wc = (wchar_t)l; /* Wide character too big for UTF-8. */ if (NULL == setlocale(LC_ALL, "C") || (long)wc != l) { skipping("Testing charset conversion failure requires 32-bit wchar_t and support for \"C\" locale."); } else { /* * Build the string L"xxx\U12345678yyy\u5678zzz" without * using wcscpy or C99 \u#### syntax. */ name = "xxxAyyyBzzz"; for (i = 0; i < (int)strlen(name); ++i) wbuff[i] = name[i]; wbuff[3] = (wchar_t)0x12345678; wbuff[7] = (wchar_t)0x5678; /* A Unicode filename that cannot be converted to UTF-8. */ archive_entry_copy_pathname_w(e, wbuff); failure("Converting wide characters from Unicode should fail."); assertEqualString(NULL, archive_entry_pathname(e)); } /* Release the experimental entry. */ archive_entry_free(e); } Index: stable/11/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c (revision 368707) @@ -1,1860 +1,1862 @@ /*- * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #include #if defined(_WIN32) && !defined(__CYGWIN__) # if !defined(__BORLANDC__) # define getcwd _getcwd # endif #endif /* * Test if the current filesystem is mounted with noatime option. */ static int atimeIsUpdated(void) { const char *fn = "fs_noatime"; struct stat st; #if defined(_WIN32) && !defined(CYGWIN) char *buff = NULL; char *ptr; int r; r = systemf("fsutil behavior query disableLastAccess > query_atime"); if (r == 0) { buff = slurpfile(NULL, "query_atime"); if (buff != NULL) { ptr = buff; while(*ptr != '\0' && !isdigit(*ptr)) { ptr++; } if (*ptr == '0') { free(buff); return(1); } else if (*ptr == '1' || *ptr == '2') { free(buff); return(0); } free(buff); } } #endif if (!assertMakeFile(fn, 0666, "a")) return (0); if (!assertUtimes(fn, 1, 0, 1, 0)) return (0); /* Test the file contents in order to update its atime. */ if (!assertTextFileContents("a", fn)) return (0); if (stat(fn, &st) != 0) return (0); /* Is atime updated? */ if (st.st_atime > 1) return (1); return (0); } static void test_basic(void) { struct archive *a; struct archive_entry *ae; const void *p; char *initial_cwd, *cwd; size_t size; int64_t offset; int file_count; #if defined(_WIN32) && !defined(__CYGWIN__) wchar_t *wcwd, *wp, *fullpath; #endif assertMakeDir("dir1", 0755); assertMakeFile("dir1/file1", 0644, "0123456789"); assertMakeFile("dir1/file2", 0644, "hello world"); assertMakeDir("dir1/sub1", 0755); assertMakeFile("dir1/sub1/file1", 0644, "0123456789"); assertMakeDir("dir1/sub2", 0755); assertMakeFile("dir1/sub2/file1", 0644, "0123456789"); assertMakeFile("dir1/sub2/file2", 0644, "0123456789"); assertMakeDir("dir1/sub2/sub1", 0755); assertMakeDir("dir1/sub2/sub2", 0755); assertMakeDir("dir1/sub2/sub3", 0755); assertMakeFile("dir1/sub2/sub3/file", 0644, "xyz"); file_count = 12; assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1")); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (strcmp(archive_entry_pathname(ae), "dir1/sub2/sub3/file") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 3); assertEqualInt((int)offset, 0); assertEqualMem(p, "xyz", 3); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 3); assertEqualInt(0, archive_read_disk_can_descend(a)); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test that call archive_read_disk_open_w, wchar_t version. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, L"dir1")); file_count = 12; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (wcscmp(archive_entry_pathname_w(ae), L"dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); assertEqualInt(0, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(1, archive_read_disk_can_descend(a)); } else if (wcscmp(archive_entry_pathname_w(ae), L"dir1/sub2/sub3/file") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 3); assertEqualInt((int)offset, 0); assertEqualMem(p, "xyz", 3); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 3); assertEqualInt(0, archive_read_disk_can_descend(a)); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test that call archive_read_disk_open with a regular file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1/file1")); /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); #if defined(_WIN32) && !defined(__CYGWIN__) /* * Test for wildcard '*' or '?' */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1/*1")); /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* dir1/sub1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(1, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/sub1"); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); /* dir1/sub1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualString(archive_entry_pathname(ae), "dir1/sub1/file1"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test for a full-path beginning with "//?/" */ wcwd = _wgetcwd(NULL, 0); fullpath = malloc(sizeof(wchar_t) * (wcslen(wcwd) + 32)); wcscpy(fullpath, L"//?/"); wcscat(fullpath, wcwd); wcscat(fullpath, L"/dir1/file1"); free(wcwd); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, fullpath)); while ((wcwd = wcschr(fullpath, L'\\')) != NULL) *wcwd = L'/'; /* dir1/file1 */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); free(fullpath); /* * Test for wild card '*' or '?' with "//?/" prefix. */ wcwd = _wgetcwd(NULL, 0); fullpath = malloc(sizeof(wchar_t) * (wcslen(wcwd) + 32)); wcscpy(fullpath, L"//?/"); wcscat(fullpath, wcwd); wcscat(fullpath, L"/dir1/*1"); free(wcwd); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open_w(a, fullpath)); while ((wcwd = wcschr(fullpath, L'\\')) != NULL) *wcwd = L'/'; /* dir1/file1 */ wp = wcsrchr(fullpath, L'/'); wcscpy(wp+1, L"file1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* dir1/sub1 */ wcscpy(wp+1, L"sub1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(1, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); /* dir1/sub1/file1 */ wcscpy(wp+1, L"sub1/file1"); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); assertEqualInt(0, archive_read_disk_can_descend(a)); assertEqualWString(archive_entry_pathname_w(ae), fullpath); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); free(fullpath); #endif /* * We should be on the initial directory where we performed * archive_read_disk_new() after we perform archive_read_free() * even if we broke off the directory traversals. */ /* Save current working directory. */ #ifdef PATH_MAX initial_cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else initial_cwd = getcwd(NULL, 0); #endif assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "dir1")); /* Step in a deep directory. */ file_count = 12; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "dir1/sub1/file1") == 0) /* * We are on an another directory at this time. */ break; if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* We should be on the initial working directory. */ failure( "Current working directory does not return to the initial" "directory"); #ifdef PATH_MAX cwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else cwd = getcwd(NULL, 0); #endif assertEqualString(initial_cwd, cwd); free(initial_cwd); free(cwd); archive_entry_free(ae); } static void test_symlink_hybrid(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("h", 0755); assertChdir("h"); assertMakeDir("d1", 0755); assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); assertMakeSymlink("d1/link1", "file1", 0); assertMakeSymlink("d1/linkX", "fileX", 0); assertMakeSymlink("link2", "d1/file2", 0); assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_hybrid(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "h/ld1")); file_count = 5; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "h/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Specified file is a directory and it has symbolic files. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "h")); file_count = 9; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "h") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "h/d1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/d1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "h/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/d1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/d1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/link2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "h/linkY") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_symlink_logical(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("l", 0755); assertChdir("l"); assertMakeDir("d1", 0755); assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); assertMakeSymlink("d1/link1", "file1", 0); assertMakeSymlink("d1/linkX", "fileX", 0); assertMakeSymlink("link2", "d1/file2", 0); assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Note: this test uses archive_read_next_header() instead of archive_read_next_header2() */ assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_logical(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l/ld1")); file_count = 5; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); if (strcmp(archive_entry_pathname(ae), "l/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Specified file is a directory and it has symbolic files. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l")); file_count = 13; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); if (strcmp(archive_entry_pathname(ae), "l") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/d1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/d1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/file2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/link1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/ld1/linkX") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l/link2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d1/file2", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } else if (strcmp(archive_entry_pathname(ae), "l/linkY") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_symlink_logical_loop(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canSymlink()) { skipping("Can't test symlinks on this filesystem"); return; } /* * Create a sample archive. */ assertMakeDir("l2", 0755); assertChdir("l2"); assertMakeDir("d1", 0755); assertMakeDir("d1/d2", 0755); assertMakeDir("d1/d2/d3", 0755); assertMakeDir("d2", 0755); assertMakeFile("d2/file1", 0644, "d2/file1"); assertMakeSymlink("d1/d2/ld1", "../../d1", 1); assertMakeSymlink("d1/d2/ld2", "../../d2", 1); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_symlink_logical(a)); /* * Specified file is a symbolic link file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "l2/d1")); file_count = 6; while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "l2/d1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/d3") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "l2/d1/d2/ld2/file1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 8); assertEqualInt((int)offset, 0); assertEqualMem(p, "d2/file1", 8); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 8); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_restore_atime(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!atimeIsUpdated()) { skipping("Can't test restoring atime on this filesystem"); return; } assertMakeDir("at", 0755); assertMakeFile("at/f1", 0644, "0123456789"); assertMakeFile("at/f2", 0644, "hello world"); assertMakeFile("at/fe", 0644, NULL); assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* * Test1: Traversals without archive_read_disk_set_atime_restored(). */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* On FreeBSD (and likely other systems), atime on dirs does not change when it is read. */ /* failure("Atime should be restored"); */ /* assertFileAtimeRecent("at"); */ failure("Atime should be restored"); assertFileAtimeRecent("at/f1"); failure("Atime should be restored"); assertFileAtimeRecent("at/f2"); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test2: Traversals with archive_read_disk_set_atime_restored(). */ assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test3: Traversals with archive_read_disk_set_atime_restored() but * no data read as a listing. */ assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 4; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_atime_restored(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); } else if (strcmp(archive_entry_pathname(ae), "at/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); if (!canNodump()) { /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); skipping("Can't test atime with nodump on this filesystem"); return; } /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test4: Traversals with ARCHIVE_READDISK_RESTORE_ATIME and * ARCHIVE_READDISK_HONOR_NODUMP */ assertSetNodump("at/f1"); assertSetNodump("at/f2"); assertUtimes("at/f1", 886600, 0, 886600, 0); assertUtimes("at/f2", 886611, 0, 886611, 0); assertUtimes("at/fe", 886611, 0, 886611, 0); assertUtimes("at", 886622, 0, 886622, 0); file_count = 2; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, ARCHIVE_READDISK_RESTORE_ATIME | ARCHIVE_READDISK_HONOR_NODUMP)); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "at")); failure("Directory traversals should work as well"); while (file_count--) { assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "at") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "at/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_entry_filetype(ae) == AE_IFDIR) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There must be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("at", 886622, 0); failure("Atime should be restored"); assertFileAtime("at/f1", 886600, 0); failure("Atime should be restored"); assertFileAtime("at/f2", 886611, 0); failure("The atime of a empty file should not be changed"); assertFileAtime("at/fe", 886611, 0); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static int metadata_filter(struct archive *a, void *data, struct archive_entry *ae) { (void)data; /* UNUSED */ failure("CTime should be set"); assertEqualInt(8, archive_entry_ctime_is_set(ae)); failure("MTime should be set"); assertEqualInt(16, archive_entry_mtime_is_set(ae)); if (archive_entry_mtime(ae) < 886611) return (0); if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ failure("archive_read_disk_can_descend should work" " in metadata filter"); assertEqualIntA(a, 1, archive_read_disk_can_descend(a)); failure("archive_read_disk_descend should work" " in metadata filter"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } return (1); } static void test_callbacks(void) { struct archive *a; struct archive *m; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; assertMakeDir("cb", 0755); assertMakeFile("cb/f1", 0644, "0123456789"); assertMakeFile("cb/f2", 0644, "hello world"); assertMakeFile("cb/fe", 0644, NULL); assertUtimes("cb/f1", 886600, 0, 886600, 0); assertUtimes("cb/f2", 886611, 0, 886611, 0); assertUtimes("cb/fe", 886611, 0, 886611, 0); assertUtimes("cb", 886622, 0, 886622, 0); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); if (a == NULL) { archive_entry_free(ae); return; } assert((m = archive_match_new()) != NULL); if (m == NULL) { archive_entry_free(ae); archive_read_free(a); archive_match_free(m); return; } /* * Test1: Traversals with a name filter. */ file_count = 3; assertEqualIntA(m, ARCHIVE_OK, archive_match_exclude_pattern(m, "cb/f2")); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_matching(a, m, NULL, NULL)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "cb")); while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'cb/f2' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "cb/f2") != 0); if (strcmp(archive_entry_pathname(ae), "cb") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "cb/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "cb/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* Reset name filter */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_matching(a, NULL, NULL, NULL)); /* * Test2: Traversals with a metadata filter. */ assertUtimes("cb/f1", 886600, 0, 886600, 0); assertUtimes("cb/f2", 886611, 0, 886611, 0); assertUtimes("cb/fe", 886611, 0, 886611, 0); assertUtimes("cb", 886622, 0, 886622, 0); file_count = 3; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_metadata_filter_callback(a, metadata_filter, NULL)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "cb")); while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'cb/f1' should be excluded"); assert(strcmp(archive_entry_pathname(ae), "cb/f1") != 0); if (strcmp(archive_entry_pathname(ae), "cb") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "cb/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "cb/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertEqualInt(ARCHIVE_OK, archive_match_free(m)); archive_entry_free(ae); } static void test_nodump(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; if (!canNodump()) { skipping("Can't test nodump on this filesystem"); return; } assertMakeDir("nd", 0755); assertMakeFile("nd/f1", 0644, "0123456789"); assertMakeFile("nd/f2", 0644, "hello world"); assertMakeFile("nd/fe", 0644, NULL); assertSetNodump("nd/f2"); assertUtimes("nd/f1", 886600, 0, 886600, 0); assertUtimes("nd/f2", 886611, 0, 886611, 0); assertUtimes("nd/fe", 886611, 0, 886611, 0); assertUtimes("nd", 886622, 0, 886622, 0); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* * Test1: Traversals without ARCHIVE_READDISK_HONOR_NODUMP */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd")); file_count = 4; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "nd") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "nd/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "nd/f2") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 11); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 11); assertEqualInt((int)offset, 0); assertEqualMem(p, "hello world", 11); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 11); } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test2: Traversals with ARCHIVE_READDISK_HONOR_NODUMP */ assertUtimes("nd/f1", 886600, 0, 886600, 0); assertUtimes("nd/f2", 886611, 0, 886611, 0); assertUtimes("nd/fe", 886611, 0, 886611, 0); assertUtimes("nd", 886622, 0, 886622, 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_set_behavior(a, ARCHIVE_READDISK_RESTORE_ATIME | ARCHIVE_READDISK_HONOR_NODUMP)); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd")); file_count = 3; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); failure("File 'nd/f2' should be exclueded"); assert(strcmp(archive_entry_pathname(ae), "nd/f2") != 0); if (strcmp(archive_entry_pathname(ae), "nd") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); } else if (strcmp(archive_entry_pathname(ae), "nd/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); } else if (strcmp(archive_entry_pathname(ae), "nd/fe") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 0); } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } /* There is no entry. */ failure("There should be no entry"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); failure("Atime should be restored"); assertFileAtime("nd/f2", 886611, 0); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } static void test_parent(void) { struct archive *a; struct archive_entry *ae; const void *p; size_t size; int64_t offset; int file_count; int match_count; int r; assertMakeDir("lock", 0311); assertMakeDir("lock/dir1", 0755); assertMakeFile("lock/dir1/f1", 0644, "0123456789"); assertMakeDir("lock/lock2", 0311); assertMakeDir("lock/lock2/dir1", 0755); assertMakeFile("lock/lock2/dir1/f1", 0644, "0123456789"); assert((ae = archive_entry_new()) != NULL); assert((a = archive_read_disk_new()) != NULL); /* * Test1: Traverse lock/dir1 as . */ assertChdir("lock/dir1"); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); file_count = 2; match_count = 0; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), ".") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); ++match_count; } else if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); ++match_count; } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } failure("Did not match expected filenames"); assertEqualInt(match_count, 2); /* * There is no entry. This will however fail if the directory traverse * tries to ascend past the initial directory, since it lacks permission * to do so. */ failure("There should be no entry and no error"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertChdir("../.."); /* * Test2: Traverse lock/dir1 directly */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1")); file_count = 2; match_count = 0; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "lock/dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); ++match_count; } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); ++match_count; } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } failure("Did not match expected filenames"); assertEqualInt(match_count, 2); /* * There is no entry. This will however fail if the directory traverse * tries to ascend past the initial directory, since it lacks permission * to do so. */ failure("There should be no entry and no error"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test3: Traverse lock/dir1/. */ failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1/.")); file_count = 2; match_count = 0; while (file_count--) { archive_entry_clear(ae); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "lock/dir1/.") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); ++match_count; } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/./f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); ++match_count; } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } failure("Did not match expected filenames"); assertEqualInt(match_count, 2); /* * There is no entry. This will however fail if the directory traverse * tries to ascend past the initial directory, since it lacks permission * to do so. */ failure("There should be no entry and no error"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); /* * Test4: Traverse lock/lock2/dir1 from inside lock. * * This test is expected to fail on platforms with no O_EXEC or * equivalent (e.g. O_PATH on Linux or O_SEARCH on SunOS), because * the current traversal code can't handle the case where it can't * obtain an open fd for the initial current directory. We need to * check that condition here, because if O_EXEC _does_ exist, we don't * want to overlook any failure. */ assertChdir("lock"); failure("Directory traversals should work as well"); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock2/dir1")); archive_entry_clear(ae); r = archive_read_next_header2(a, ae); if (r == ARCHIVE_FAILED) { #if defined(O_PATH) || defined(O_SEARCH) || \ (defined(__FreeBSD__) && defined(O_EXEC)) assertEqualIntA(a, ARCHIVE_OK, r); #endif /* Close the disk object. */ archive_read_close(a); } else { file_count = 2; match_count = 0; while (file_count--) { if (file_count == 0) assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); if (strcmp(archive_entry_pathname(ae), "lock2/dir1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); ++match_count; } else if (strcmp(archive_entry_pathname(ae), "lock2/dir1/f1") == 0) { assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_size(ae), 10); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 10); assertEqualInt((int)offset, 0); assertEqualMem(p, "0123456789", 10); assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &p, &size, &offset)); assertEqualInt((int)size, 0); assertEqualInt((int)offset, 10); ++match_count; } if (archive_read_disk_can_descend(a)) { /* Descend into the current object */ assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_descend(a)); } } failure("Did not match expected filenames"); assertEqualInt(match_count, 2); /* * There is no entry. This will however fail if the directory * traverse tries to ascend past the initial directory, since * it lacks permission to do so. */ failure("There should be no entry and no error"); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); /* Close the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); } assertChdir(".."); + assertChmod("lock", 0755); + assertChmod("lock/lock2", 0755); /* Destroy the disk object. */ assertEqualInt(ARCHIVE_OK, archive_read_free(a)); archive_entry_free(ae); } DEFINE_TEST(test_read_disk_directory_traversals) { /* Basic test. */ test_basic(); /* Test hybrid mode; follow symlink initially, then not. */ test_symlink_hybrid(); /* Test logical mode; follow all symlinks. */ test_symlink_logical(); /* Test logical mode; prevent loop in symlinks. */ test_symlink_logical_loop(); /* Test to restore atime. */ test_restore_atime(); /* Test callbacks. */ test_callbacks(); /* Test nodump. */ test_nodump(); /* Test parent overshoot. */ test_parent(); } Index: stable/11/contrib/libarchive/libarchive/test/test_read_format_mtree.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_read_format_mtree.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_read_format_mtree.c (revision 368707) @@ -1,744 +1,816 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); static void test_read_format_mtree1(void) { const char reffile[] = "test_read_format_mtree.mtree"; char buff[16]; struct archive_entry *ae; struct archive *a; FILE *f; /* Compute max 64-bit signed twos-complement value * without relying on overflow. This assumes that long long * is at least 64 bits. */ static const long long max_int64 = ((((long long)1) << 62) - 1) + (((long long)1) << 62); time_t min_time; volatile time_t t; extract_reference_file(reffile); /* * An access error occurred on some platform when mtree * format handling open a directory. It is for through * the routine which open a directory that we create * "dir" and "dir2" directories. */ assertMakeDir("dir", 0775); assertMakeDir("dir2", 0775); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 11)); /* * Read "file", whose data is available on disk. */ f = fopen("file", "wb"); assert(f != NULL); assertEqualInt(3, fwrite("hi\n", 1, 3, f)); fclose(f); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE); assertEqualString(archive_entry_pathname(ae), "file"); assertEqualInt(archive_entry_uid(ae), 18); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123); assertEqualInt(archive_entry_size(ae), 3); assertEqualInt(3, archive_read_data(a, buff, 3)); assertEqualMem(buff, "hi\n", 3); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir"); assertEqualInt(AE_IFDIR, archive_entry_filetype(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir/file with space"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "file with space"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3a"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3a/indir3a"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/fullindir2"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/indir2"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3b"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3b/indir3b"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3b/filename\\with_esc\b\t\fapes"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "notindir"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/emptyfile"); assertEqualInt(archive_entry_size(ae), 0); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/smallfile"); assertEqualInt(archive_entry_size(ae), 1); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); /* TODO: Mtree reader should probably return ARCHIVE_WARN for this. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/toosmallfile"); assertEqualInt(archive_entry_size(ae), -1); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/bigfile"); assertEqualInt(archive_entry_size(ae), max_int64); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/toobigfile"); /* Size in mtree is max_int64 + 1; should return max_int64. */ assertEqualInt(archive_entry_size(ae), max_int64); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/veryoldfile"); /* The value in the file is MIN_INT64_T, but time_t may be narrower. */ /* Verify min_time is the smallest possible time_t. */ min_time = archive_entry_mtime(ae); assert(min_time <= 0); /* Simply asserting min_time - 1 > 0 breaks with some compiler optimizations. */ t = (time_t)((uintmax_t)min_time - 1); assert(t > 0); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); /* toooldfile is 1 sec older, which should overflow and get returned * with the same value. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/toooldfile"); assertEqualInt(archive_entry_mtime(ae), min_time); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); + /* md5digest */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/md5file"); + assertEqualMem(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_MD5), + "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e", + 16); + + /* rmd160digest */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/rmd160file"); + assertEqualMem(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_RMD160), + "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90" + "\xaf\xd8\x07\x09", 20); + + /* sha1digest */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/sha1file"); + assertEqualMem(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_SHA1), + "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90" + "\xaf\xd8\x07\x09", 20); + + /* sha256digest */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/sha256file"); + assertEqualMem(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_SHA256), + "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24" + "\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55", + 32); + + /* sha384digest */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/sha384file"); + assertEqualMem(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_SHA384), + "\x38\xb0\x60\xa7\x51\xac\x96\x38\x4c\xd9\x32\x7e\xb1\xb1\xe3\x6a" + "\x21\xfd\xb7\x11\x14\xbe\x07\x43\x4c\x0c\xc7\xbf\x63\xf6\xe1\xda" + "\x27\x4e\xde\xbf\xe7\x6f\x65\xfb\xd5\x1a\xd2\xf1\x48\x98\xb9\x5b", + 48); + + /* sha512digest */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/sha512file"); + assertEqualMem(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_SHA512), + "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07" + "\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce" + "\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f" + "\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e", + 64); + + /* md5 digest is too short */ + assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/md5tooshort"); + assertMemoryFilledWith(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_MD5), + 16, 0x00); + + /* md5 digest is too long */ + assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/md5toolong"); + assertMemoryFilledWith(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_MD5), + 16, 0x00); + + /* md5 digest is uppercase hex */ + assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/md5caphex"); + assertMemoryFilledWith(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_MD5), + 16, 0x00); + + /* md5 digest is not hex */ + assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "dir2/md5nothex"); + assertMemoryFilledWith(archive_entry_digest(ae, ARCHIVE_ENTRY_DIGEST_MD5), + 16, 0x00); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); - assertEqualInt(20, archive_file_count(a)); + assertEqualInt(30, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_read_format_mtree2(void) { static char archive[] = "#mtree\n" "d type=dir content=.\n"; struct archive_entry *ae; struct archive *a; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, archive, sizeof(archive))); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE); assertEqualString(archive_entry_pathname(ae), "d"); assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Reported to libarchive.googlecode.com as Issue 121. */ static void test_read_format_mtree3(void) { static char archive[] = "#mtree\n" "a type=file contents=file\n" "b type=link link=a\n" "c type=file contents=file\n"; struct archive_entry *ae; struct archive *a; assertMakeDir("mtree3", 0777); assertChdir("mtree3"); assertMakeFile("file", 0644, "file contents"); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, archive, sizeof(archive))); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "a"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "b"); assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "c"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(3, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assertChdir(".."); } DEFINE_TEST(test_read_format_mtree) { test_read_format_mtree1(); test_read_format_mtree2(); test_read_format_mtree3(); } DEFINE_TEST(test_read_format_mtree_filenames_only) { static char archive[] = "/set type=file mode=0644\n" "./a\n" "./b\n" "./c\n" "./d\n" "./e\n" "./f mode=0444\n"; struct archive_entry *ae; struct archive *a; assertMakeFile("file", 0644, "file contents"); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, archive, sizeof(archive))); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./a"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./b"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./c"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./d"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./e"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./f"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0444); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_mtree_nochange) { static char archive[] = "#mtree\n" "./a type=file mode=0644 time=123\n" "./b type=file mode=0644 time=234\n" "./c type=file mode=0644 time=345\n"; static char archive2[] = "#mtree\n" "./a type=file mode=0644 time=123 nochange\n" "./b type=file mode=0644 time=234\n" "./c type=file mode=0644 time=345 nochange\n"; struct archive_entry *ae; struct archive *a; assertMakeFile("a", 0640, "12345"); assertMakeFile("b", 0664, "123456"); assertMakeFile("c", 0755, "1234567"); /* * Test 1. Read a mtree archive without `nochange' keyword. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, archive, sizeof(archive))); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./a"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_mtime(ae), 123); assertEqualInt(archive_entry_size(ae), 5); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./b"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_mtime(ae), 234); assertEqualInt(archive_entry_size(ae), 6); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./c"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_mtime(ae), 345); assertEqualInt(archive_entry_size(ae), 7); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(3, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* * Test 2. Read a mtree archive with `nochange' keyword. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, archive2, sizeof(archive2))); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./a"); #if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0640); #endif assert(archive_entry_mtime(ae) != 123); assertEqualInt(archive_entry_size(ae), 5); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./b"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_mtime(ae), 234); assertEqualInt(archive_entry_size(ae), 6); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./c"); #if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0755); #endif assert(archive_entry_mtime(ae) != 345); assertEqualInt(archive_entry_size(ae), 7); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(3, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_mtree_nomagic_v1_form) { const char reffile[] = "test_read_format_mtree_nomagic.mtree"; char buff[16]; struct archive_entry *ae; struct archive *a; FILE *f; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 11)); /* * Read "file", whose data is available on disk. */ f = fopen("file", "wb"); assert(f != NULL); assertEqualInt(3, fwrite("hi\n", 1, 3, f)); fclose(f); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE); assertEqualString(archive_entry_pathname(ae), "file"); assertEqualInt(archive_entry_uid(ae), 18); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123); assertEqualInt(archive_entry_size(ae), 3); assertEqualInt(3, archive_read_data(a, buff, 3)); assertEqualMem(buff, "hi\n", 3); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir"); assertEqualInt(AE_IFDIR, archive_entry_filetype(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir/file with space"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "file with space"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3a"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3a/indir3a"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/fullindir2"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/indir2"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3b"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "dir2/dir3b/indir3b"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "notindir"); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(12, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Test for a format that NetBSD mtree -C generates. */ DEFINE_TEST(test_read_format_mtree_nomagic_v2_form) { const char reffile[] = "test_read_format_mtree_nomagic2.mtree"; char buff[16]; struct archive_entry *ae; struct archive *a; FILE *f; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 11)); /* * Read "file", whose data is available on disk. */ f = fopen("file", "wb"); assert(f != NULL); assertEqualInt(3, fwrite("hi\n", 1, 3, f)); fclose(f); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE); assertEqualString(archive_entry_pathname(ae), "./file"); assertEqualInt(archive_entry_uid(ae), 18); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123); assertEqualInt(archive_entry_size(ae), 3); assertEqualInt(3, archive_read_data(a, buff, 3)); assertEqualMem(buff, "hi\n", 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir"); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir/file with space"); assertEqualInt(archive_entry_uid(ae), 18); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./file with space"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir2"); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir2/dir3a"); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Test for a format that NetBSD mtree -D generates. */ DEFINE_TEST(test_read_format_mtree_nomagic_v2_netbsd_form) { const char reffile[] = "test_read_format_mtree_nomagic3.mtree"; char buff[16]; struct archive_entry *ae; struct archive *a; FILE *f; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 11)); /* * Read "file", whose data is available on disk. */ f = fopen("file", "wb"); assert(f != NULL); assertEqualInt(3, fwrite("hi\n", 1, 3, f)); fclose(f); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE); assertEqualString(archive_entry_pathname(ae), "./file"); assertEqualInt(archive_entry_uid(ae), 18); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123); assertEqualInt(archive_entry_size(ae), 3); assertEqualInt(3, archive_read_data(a, buff, 3)); assertEqualMem(buff, "hi\n", 3); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir"); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir/file with space"); assertEqualInt(archive_entry_uid(ae), 18); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./file with space"); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir2"); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(archive_entry_pathname(ae), "./dir2/dir3a"); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * We should get a warning if the contents file doesn't exist. */ DEFINE_TEST(test_read_format_mtree_nonexistent_contents_file) { static char archive[] = "#mtree\n" "a type=file contents=nonexistent_file\n"; struct archive_entry *ae; struct archive *a; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_set_options(a, "mtree:checkfs")); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, archive, sizeof(archive))); assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae)); assert(strlen(archive_error_string(a)) > 0); assertEqualString(archive_entry_pathname(ae), "a"); assertEqualInt(archive_entry_filetype(ae), AE_IFREG); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_file_count(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Check mtree file with non-printable ascii characters */ DEFINE_TEST(test_read_format_mtree_noprint) { const char reffile[] = "test_read_format_mtree_noprint.mtree"; struct archive_entry *ae; struct archive *a; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 11)); assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); assertEqualString("Can't parse line 3", archive_error_string(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } Index: stable/11/contrib/libarchive/libarchive/test/test_read_format_mtree.mtree.uu =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_read_format_mtree.mtree.uu (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_read_format_mtree.mtree.uu (revision 368707) @@ -1,21 +1,41 @@ $FreeBSD$ begin 644 test_read_format_mtree.mtree M(VUT7!E/61I<@H@9FEL95PP-#!W:71H7#`T,'-P86-E('1Y<&4]9FEL M92!U:60],3@*("XN"F9I;&5<,#0P=VET:%PP-#!S<&%C92!T>7!E/69I;&4* M9&ER,B!T>7!E/61I<@H@9&ER,V$@='EP93UD:7(*("!I;F1I7!E/61I<@H@(&EN9&ER,V(@ M='EP93UF:6QE"B`@9FEL96YA;65<7'=I=&A<"E]E7!E/69I M;&4@F4]+3$*9&ER,B]B:6=F:6QE('1Y M<&4]9FEL92!S:7IE/3DR,C,S-S(P,S8X-30W-S4X,#<*9&ER,B]T;V]B:6=F M:6QE('1Y<&4]9FEL92!S:7IE/3DR,C,S-S(P,S8X-30W-S4X,#@*9&ER,B]V M97)Y;VQD9FEL92!T>7!E/69I;&4@=&EM93TM.3(R,S,W,C`S-C@U-#7!E/69I;&4@"!T>7!E/69I;&4@ +K;60U9&EG97-T/7$T,60X8V0Y.&8P,&(R,#1E.3@P,#DY.&5C9C@T,C=E"@`` ` end Index: stable/11/contrib/libarchive/libarchive/test/test_read_format_raw.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_read_format_raw.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_read_format_raw.c (revision 368707) @@ -1,151 +1,150 @@ /*- * Copyright (c) 2007 Kai Wang * Copyright (c) 2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); DEFINE_TEST(test_read_format_raw) { char buff[512]; struct archive_entry *ae; struct archive *a; const char *reffile1 = "test_read_format_raw.data"; const char *reffile2 = "test_read_format_raw.data.Z"; const char *reffile3 = "test_read_format_raw.bufr"; #ifdef HAVE_ZLIB_H const char *reffile4 = "test_read_format_raw.data.gz"; #endif /* First, try pulling data out of an uninterpretable file. */ extract_reference_file(reffile1); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile1, 512)); /* First (and only!) Entry */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("data", archive_entry_pathname(ae)); /* Most fields should be unset (unknown) */ assert(!archive_entry_size_is_set(ae)); assert(!archive_entry_atime_is_set(ae)); assert(!archive_entry_ctime_is_set(ae)); assert(!archive_entry_mtime_is_set(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualInt(4, archive_read_data(a, buff, 32)); assertEqualMem(buff, "foo\n", 4); /* Test EOF */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* Second, try the same with a compressed file. */ extract_reference_file(reffile2); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_by_code(a, ARCHIVE_FORMAT_RAW)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile2, 1)); /* First (and only!) Entry */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("data", archive_entry_pathname(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); /* Most fields should be unset (unknown) */ assert(!archive_entry_size_is_set(ae)); assert(!archive_entry_atime_is_set(ae)); assert(!archive_entry_ctime_is_set(ae)); assert(!archive_entry_mtime_is_set(ae)); assertEqualInt(4, archive_read_data(a, buff, 32)); assertEqualMem(buff, "foo\n", 4); /* Test EOF */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* Third, try with file which fooled us in past - appeared to be tar. */ extract_reference_file(reffile3); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_set_format(a, ARCHIVE_FORMAT_RAW)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile3, 1)); /* First (and only!) Entry */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("data", archive_entry_pathname(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); /* Most fields should be unset (unknown) */ assert(!archive_entry_size_is_set(ae)); assert(!archive_entry_atime_is_set(ae)); assert(!archive_entry_ctime_is_set(ae)); assert(!archive_entry_mtime_is_set(ae)); /* Test EOF */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); #ifdef HAVE_ZLIB_H /* Fourth, try with gzip which has metadata. */ extract_reference_file(reffile4); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile4, 1)); /* First (and only!) Entry */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("test-file-name.data", archive_entry_pathname(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assert(archive_entry_mtime_is_set(ae)); assertEqualIntA(a, archive_entry_mtime(ae), 0x5cbafd25); /* Most fields should be unset (unknown) */ assert(!archive_entry_size_is_set(ae)); assert(!archive_entry_atime_is_set(ae)); assert(!archive_entry_ctime_is_set(ae)); /* Test EOF */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); #endif } Index: stable/11/contrib/libarchive/libarchive/test/test_read_format_zip.c =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_read_format_zip.c (revision 368706) +++ stable/11/contrib/libarchive/libarchive/test/test_read_format_zip.c (revision 368707) @@ -1,968 +1,1000 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD$"); #define __LIBARCHIVE_BUILD #include static int extract_one(struct archive* a, struct archive_entry* ae, uint32_t crc) { la_ssize_t fsize, bytes_read; uint8_t* buf; int ret = 1; uint32_t computed_crc; fsize = (la_ssize_t) archive_entry_size(ae); buf = malloc(fsize); if(buf == NULL) return 1; bytes_read = archive_read_data(a, buf, fsize); if(bytes_read != fsize) { assertEqualInt(bytes_read, fsize); goto fn_exit; } computed_crc = crc32(0, buf, fsize); assertEqualInt(computed_crc, crc); ret = 0; fn_exit: free(buf); return ret; } static int extract_one_using_blocks(struct archive* a, int block_size, uint32_t crc) { uint8_t* buf; int ret = 1; uint32_t computed_crc = 0; la_ssize_t bytes_read; buf = malloc(block_size); if(buf == NULL) return 1; while(1) { bytes_read = archive_read_data(a, buf, block_size); if(bytes_read == ARCHIVE_RETRY) continue; else if(bytes_read == 0) break; else if(bytes_read < 0) { /* If we're here, it means the decompressor has failed * to properly decode test file. */ assertA(0); ret = 1; goto fn_exit; } else { /* ok */ } computed_crc = crc32(computed_crc, buf, bytes_read); } assertEqualInt(computed_crc, crc); ret = 0; fn_exit: free(buf); return ret; } /* * 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 */ static void verify_basic(struct archive *a, int seek_checks) { struct archive_entry *ae; char *buff[128]; const void *pv; size_t s; int64_t o; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 1.0 (uncompressed)", archive_format_name(a)); assertEqualString("dir/", archive_entry_pathname(ae)); assertEqualInt(1179604249, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); if (seek_checks) assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_EOF, archive_read_data_block(a, &pv, &s, &o)); assertEqualInt((int)s, 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1179604289, archive_entry_mtime(ae)); if (seek_checks) assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(18, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); failure("archive_read_data() returns number of bytes read"); if (archive_zlib_version() != NULL) { assertEqualInt(18, archive_read_data(a, buff, 19)); assertEqualMem(buff, "hello\nhello\nhello\n", 18); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (8: deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(1179605932, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); if (seek_checks) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); } assert(archive_entry_size_is_set(ae)); assertEqualInt(18, archive_entry_size(ae)); if (archive_zlib_version() != NULL) { failure("file2 has a bad CRC, so read should fail and not change buff"); memset(buff, 'a', 19); assertEqualInt(ARCHIVE_WARN, archive_read_data(a, buff, 19)); assertEqualMem(buff, "aaaaaaaaaaaaaaaaaaa", 19); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (8: deflation)"); assert(archive_errno(a) != 0); } assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); /* Verify the number of files read. */ failure("the archive file has three files"); assertEqualInt(3, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_ZIP, archive_format(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_basic(void) { const char *refname = "test_read_format_zip.zip"; struct archive *a; char *p; size_t s; extract_reference_file(refname); /* Verify with seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); verify_basic(a, 1); /* Verify with streaming reader. */ p = slurpfile(&s, "%s", refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 31)); verify_basic(a, 0); free(p); } /* * Read Info-ZIP New Unix Extra Field 0x7875 "ux". * Currently stores Unix UID/GID up to 32 bits. */ static void verify_info_zip_ux(struct archive *a, int seek_checks) { struct archive_entry *ae; char *buff[128]; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1300668680, archive_entry_mtime(ae)); assertEqualInt(18, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); if (seek_checks) assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); failure("zip reader should read Info-ZIP New Unix Extra Field"); assertEqualInt(1001, archive_entry_uid(ae)); assertEqualInt(1001, archive_entry_gid(ae)); if (archive_zlib_version() != NULL) { failure("archive_read_data() returns number of bytes read"); assertEqualInt(18, archive_read_data(a, buff, 19)); assertEqualMem(buff, "hello\nhello\nhello\n", 18); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (8: deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify the number of files read. */ failure("the archive file has just one file"); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_ZIP, archive_format(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_info_zip_ux(void) { const char *refname = "test_read_format_zip_ux.zip"; struct archive *a; char *p; size_t s; extract_reference_file(refname); /* Verify with seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); verify_info_zip_ux(a, 1); /* Verify with streaming reader. */ p = slurpfile(&s, "%s", refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 108)); verify_info_zip_ux(a, 0); free(p); } /* * Verify that test_read_extract correctly works with * Zip entries that use length-at-end. */ static void verify_extract_length_at_end(struct archive *a, int seek_checks) { struct archive_entry *ae; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualString("hello.txt", archive_entry_pathname(ae)); if (seek_checks) { assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); assert(archive_entry_size_is_set(ae)); assertEqualInt(6, archive_entry_size(ae)); } else { assert(!archive_entry_size_is_set(ae)); assertEqualInt(0, archive_entry_size(ae)); } if (archive_zlib_version() != NULL) { assertEqualIntA(a, ARCHIVE_OK, archive_read_extract(a, ae, 0)); assertFileContents("hello\x0A", 6, "hello.txt"); } else { assertEqualIntA(a, ARCHIVE_FAILED, archive_read_extract(a, ae, 0)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (8: deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } static void test_extract_length_at_end(void) { const char *refname = "test_read_format_zip_length_at_end.zip"; char *p; size_t s; struct archive *a; extract_reference_file(refname); /* Verify extraction with seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); verify_extract_length_at_end(a, 1); /* Verify extraction with streaming reader. */ p = slurpfile(&s, "%s", refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 108)); verify_extract_length_at_end(a, 0); free(p); } static void test_symlink(void) { const char *refname = "test_read_format_zip_symlink.zip"; char *p; size_t s; struct archive *a; struct archive_entry *ae; extract_reference_file(refname); p = slurpfile(&s, "%s", refname); /* Symlinks can only be extracted with the seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("file", archive_entry_pathname(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("symlink", archive_entry_pathname(ae)); assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualString("file", archive_entry_symlink(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); free(p); } DEFINE_TEST(test_read_format_zip) { test_basic(); test_info_zip_ux(); test_extract_length_at_end(); test_symlink(); } DEFINE_TEST(test_read_format_zip_ppmd_one_file) { const char *refname = "test_read_format_zip_ppmd8.zipx"; struct archive *a; struct archive_entry *ae; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_ppmd_one_file_blockread) { const char *refname = "test_read_format_zip_ppmd8.zipx"; struct archive *a; struct archive_entry *ae; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_ppmd_multi) { const char *refname = "test_read_format_zip_ppmd8_multi.zipx"; struct archive *a; struct archive_entry *ae; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("smartd.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("ts.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_ppmd_multi_blockread) { const char *refname = "test_read_format_zip_ppmd8_multi.zipx"; struct archive *a; struct archive_entry *ae; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("smartd.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("ts.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_lzma_one_file) { const char *refname = "test_read_format_zip_lzma.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_lzma_one_file_blockread) { const char *refname = "test_read_format_zip_lzma.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_lzma_multi) { const char *refname = "test_read_format_zip_lzma_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("smartd.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("ts.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_lzma_multi_blockread) { const char *refname = "test_read_format_zip_lzma_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("smartd.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("ts.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_bzip2_one_file) { const char *refname = "test_read_format_zip_bzip2.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { skipping("bzip2 is not fully supported on this platform"); archive_read_close(a); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_bzip2_one_file_blockread) { const char *refname = "test_read_format_zip_bzip2.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { skipping("bzip2 is not fully supported on this platform"); archive_read_close(a); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_bzip2_multi) { const char *refname = "test_read_format_zip_bzip2_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { skipping("bzip2 is not fully supported on this platform"); archive_read_close(a); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("smartd.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("ts.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_bzip2_multi_blockread) { const char *refname = "test_read_format_zip_bzip2_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { skipping("bzip2 is not fully supported on this platform"); archive_read_close(a); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("smartd.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("ts.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_xz_multi) { const char *refname = "test_read_format_zip_xz_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); assertEqualString("bash.bashrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xF751B8C9)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); assertEqualString("pacman.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xB20B7F88)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); assertEqualString("profile", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0x2329F054)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_xz_multi_blockread) { const char *refname = "test_read_format_zip_xz_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); assertEqualString("bash.bashrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0xF751B8C9)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); assertEqualString("pacman.conf", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xB20B7F88)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); assertEqualString("profile", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0x2329F054)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_ppmd8_crash_1) { const char *refname = "test_read_format_zip_ppmd8_crash_2.zipx"; struct archive *a; struct archive_entry *ae; char buf[64]; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 100)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); /* This file shouldn't be properly decompressed, because it's invalid. * However, unpacker should return an error during unpacking. Without the * proper fix, the unpacker was entering an unlimited loop. */ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_bz2_hang_on_invalid) { const char *refname = "test_read_format_zip_bz2_hang.zip"; struct archive *a; struct archive_entry *ae; char buf[8]; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { skipping("bzip2 is not fully supported on this platform"); archive_read_close(a); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); /* The file `refname` is invalid in this case, so this call should fail. * But it shouldn't crash. */ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 64)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_ppmd8_crash_2) { const char *refname = "test_read_format_zip_ppmd8_crash_2.zipx"; struct archive *a; struct archive_entry *ae; char buf[64]; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); /* The file `refname` is invalid in this case, so this call should fail. * But it shouldn't crash. */ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 64)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_lzma_alone_leak) { const char *refname = "test_read_format_zip_lzma_alone_leak.zipx"; struct archive *a; struct archive_entry *ae; char buf[64]; /* OSSFuzz #14470 sample file. */ extract_reference_file(refname); assert((a = archive_read_new()) != NULL); if(ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading is not fully supported on this platform"); archive_read_close(a); return; } assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); /* Extraction of this file should fail, because the sample file is invalid. * But it shouldn't crash. */ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_data(a, buf, sizeof(buf))); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); /* Extraction of this file should fail, because the sample file is invalid. * But it shouldn't crash. */ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, sizeof(buf))); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); /* This testcase shouldn't produce any memory leaks. When running test * suite under Valgrind or ASan, the test runner won't return with * exit code 0 in case if a memory leak. */ } DEFINE_TEST(test_read_format_zip_lzma_stream_end) { const char *refname = "test_read_format_zip_lzma_stream_end.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_zip_lzma_stream_end_blockread) { const char *refname = "test_read_format_zip_lzma_stream_end.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { skipping("lzma reading not fully supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } extract_reference_file(refname); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); assertEqualString("vimrc", archive_entry_pathname(ae)); assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } + +DEFINE_TEST(test_read_format_zip_7z_lzma) +{ + const char *refname = "test_read_format_zip_7z_lzma.zip"; + struct archive_entry *ae; + struct archive *a; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + //read directories + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + //read symlink + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("../samples/abc_measurement_analysis_sample" + "/src/abc_measurement_analysis_sample.py", + archive_entry_symlink(ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} Index: stable/11/contrib/libarchive/libarchive/test/test_read_format_zip_7z_lzma.zip.uu =================================================================== --- stable/11/contrib/libarchive/libarchive/test/test_read_format_zip_7z_lzma.zip.uu (nonexistent) +++ stable/11/contrib/libarchive/libarchive/test/test_read_format_zip_7z_lzma.zip.uu (revision 368707) @@ -0,0 +1,45 @@ +begin 644 test_read_format_zip_7z_lzma.zip +M4$L#!`H#`````.$#)%$````````````````,````7VENA79\GCDS+I*=O.=!+"5(),_1O%5^1102P,$"@,`````]0,D +M40```````````````"P```!?:6YS=&%L;&1I7-I00]!0!=````!``1B$)'BB,SPW````7VEN7-I7-I7-I7-I /* Windows requires this before sys/stat.h */ #include #if HAVE_DIRENT_H #include #endif #ifdef HAVE_DIRECT_H #include #define dirent direct #endif #include #include #ifdef HAVE_IO_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_RICHACL_H #include #endif #ifdef HAVE_WINDOWS_H #define NOCRYPT #include #include #endif /* * System-specific tweaks. We really want to minimize these * as much as possible, since they make it harder to understand * the mainline code. */ /* Windows (including Visual Studio and MinGW but not Cygwin) */ #if defined(_WIN32) && !defined(__CYGWIN__) #if !defined(__BORLANDC__) #undef chdir #define chdir _chdir #define strdup _strdup #endif #endif /* Visual Studio */ #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf sprintf_s #endif #if defined(__BORLANDC__) #pragma warn -8068 /* Constant out of range in comparison. */ #endif #if defined(__GNUC__) && (__GNUC__ > 2 || \ (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)) # ifdef __MINGW_PRINTF_FORMAT # define __LA_PRINTF_FORMAT __MINGW_PRINTF_FORMAT # else # define __LA_PRINTF_FORMAT __printf__ # endif # define __LA_PRINTFLIKE(f,a) __attribute__((__format__(__LA_PRINTF_FORMAT, f, a))) #else # define __LA_PRINTFLIKE(f,a) #endif /* Haiku OS and QNX */ #if defined(__HAIKU__) || defined(__QNXNTO__) /* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ #include #endif /* Get a real definition for __FBSDID if we can */ #if HAVE_SYS_CDEFS_H #include #endif /* If not, define it so as to avoid dangling semicolons. */ #ifndef __FBSDID #define __FBSDID(a) struct _undefined_hack #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef __LIBARCHIVE_TEST_COMMON #define __LIBARCHIVE_TEST_COMMON #endif #include "archive_platform_acl.h" #define ARCHIVE_TEST_ACL_TYPE_POSIX1E 1 #define ARCHIVE_TEST_ACL_TYPE_NFS4 2 #include "archive_platform_xattr.h" /* * Redefine DEFINE_TEST for use in defining the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); void name(void) /* An implementation of the standard assert() macro */ #define assert(e) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) /* chdir() and error if it fails */ #define assertChdir(path) \ assertion_chdir(__FILE__, __LINE__, path) +/* change file/directory permissions and errors if it fails */ +#define assertChmod(pathname, mode) \ + assertion_chmod(__FILE__, __LINE__, pathname, mode) /* Assert two files have the same file flags */ #define assertEqualFflags(patha, pathb) \ assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 0) /* Assert two integers are the same. Reports value of each one if not. */ #define assertEqualInt(v1,v2) \ assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* Assert two strings are the same. Reports value of each one if not. */ #define assertEqualString(v1,v2) \ assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0) #define assertEqualUTF8String(v1,v2) \ assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1) /* As above, but v1 and v2 are wchar_t * */ #define assertEqualWString(v1,v2) \ assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* As above, but raw blocks of bytes. */ #define assertEqualMem(v1, v2, l) \ assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) /* Assert that memory is full of a specified byte */ #define assertMemoryFilledWith(v1, l, b) \ assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL) /* Assert two files are the same. */ #define assertEqualFile(f1, f2) \ assertion_equal_file(__FILE__, __LINE__, (f1), (f2)) /* Assert that a file is empty. */ #define assertEmptyFile(pathname) \ assertion_empty_file(__FILE__, __LINE__, (pathname)) /* Assert that a file is not empty. */ #define assertNonEmptyFile(pathname) \ assertion_non_empty_file(__FILE__, __LINE__, (pathname)) #define assertFileAtime(pathname, sec, nsec) \ assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) #define assertFileAtimeRecent(pathname) \ assertion_file_atime_recent(__FILE__, __LINE__, pathname) #define assertFileBirthtime(pathname, sec, nsec) \ assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) #define assertFileBirthtimeRecent(pathname) \ assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) /* Assert that a file exists; supports printf-style arguments. */ #define assertFileExists(pathname) \ assertion_file_exists(__FILE__, __LINE__, pathname) /* Assert that a file exists. */ #define assertFileNotExists(pathname) \ assertion_file_not_exists(__FILE__, __LINE__, pathname) /* Assert that file contents match a string. */ #define assertFileContents(data, data_size, pathname) \ assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname) /* Verify that a file does not contain invalid strings */ #define assertFileContainsNoInvalidStrings(pathname, strings) \ assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings) #define assertFileMtime(pathname, sec, nsec) \ assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) #define assertFileMtimeRecent(pathname) \ assertion_file_mtime_recent(__FILE__, __LINE__, pathname) #define assertFileNLinks(pathname, nlinks) \ assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) #define assertFileSize(pathname, size) \ assertion_file_size(__FILE__, __LINE__, pathname, size) #define assertFileMode(pathname, mode) \ assertion_file_mode(__FILE__, __LINE__, pathname, mode) #define assertTextFileContents(text, pathname) \ assertion_text_file_contents(__FILE__, __LINE__, text, pathname) #define assertFileContainsLinesAnyOrder(pathname, lines) \ assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) #define assertIsDir(pathname, mode) \ assertion_is_dir(__FILE__, __LINE__, pathname, mode) #define assertIsHardlink(path1, path2) \ assertion_is_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsNotHardlink(path1, path2) \ assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsReg(pathname, mode) \ assertion_is_reg(__FILE__, __LINE__, pathname, mode) #define assertIsSymlink(pathname, contents, isdir) \ assertion_is_symlink(__FILE__, __LINE__, pathname, contents, isdir) /* Create a directory, report error if it fails. */ #define assertMakeDir(dirname, mode) \ assertion_make_dir(__FILE__, __LINE__, dirname, mode) #define assertMakeFile(path, mode, contents) \ assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents) #define assertMakeBinFile(path, mode, csize, contents) \ assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) #define assertMakeHardlink(newfile, oldfile) \ assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) #define assertMakeSymlink(newfile, linkto, targetIsDir) \ assertion_make_symlink(__FILE__, __LINE__, newfile, linkto, targetIsDir) #define assertSetNodump(path) \ assertion_set_nodump(__FILE__, __LINE__, path) #define assertUmask(mask) \ assertion_umask(__FILE__, __LINE__, mask) /* Assert that two files have unequal file flags */ #define assertUnequalFflags(patha, pathb) \ assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 1) #define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec) \ assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec) #ifndef PROGRAM #define assertEntrySetAcls(entry, acls, count) \ assertion_entry_set_acls(__FILE__, __LINE__, entry, acls, count) #define assertEntryCompareAcls(entry, acls, count, type, mode) \ assertion_entry_compare_acls(__FILE__, __LINE__, entry, acls, count, type, mode) #endif /* * This would be simple with C99 variadic macros, but I don't want to * require that. Instead, I insert a function call before each * skipping() call to pass the file and line information down. Crude, * but effective. */ #define skipping \ skipping_setup(__FILE__, __LINE__);test_skipping /* Function declarations. These are defined in test_utility.c. */ void failure(const char *fmt, ...) __LA_PRINTFLIKE(1, 2); int assertion_assert(const char *, int, int, const char *, void *); int assertion_chdir(const char *, int, const char *); +int assertion_chmod(const char *, int, const char *, int); int assertion_compare_fflags(const char *, int, const char *, const char *, int); int assertion_empty_file(const char *, int, const char *); int assertion_equal_file(const char *, int, const char *, const char *); int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *); int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int); int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); int assertion_file_atime(const char *, int, const char *, long, long); int assertion_file_atime_recent(const char *, int, const char *); int assertion_file_birthtime(const char *, int, const char *, long, long); int assertion_file_birthtime_recent(const char *, int, const char *); int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **); int assertion_file_contents(const char *, int, const void *, int, const char *); int assertion_file_exists(const char *, int, const char *); int assertion_file_mode(const char *, int, const char *, int); int assertion_file_mtime(const char *, int, const char *, long, long); int assertion_file_mtime_recent(const char *, int, const char *); int assertion_file_nlinks(const char *, int, const char *, int); int assertion_file_not_exists(const char *, int, const char *); int assertion_file_size(const char *, int, const char *, long); int assertion_is_dir(const char *, int, const char *, int); int assertion_is_hardlink(const char *, int, const char *, const char *); int assertion_is_not_hardlink(const char *, int, const char *, const char *); int assertion_is_reg(const char *, int, const char *, int); int assertion_is_symlink(const char *, int, const char *, const char *, int); int assertion_make_dir(const char *, int, const char *, int); int assertion_make_file(const char *, int, const char *, int, int, const void *); int assertion_make_hardlink(const char *, int, const char *newpath, const char *); int assertion_make_symlink(const char *, int, const char *newpath, const char *, int); int assertion_non_empty_file(const char *, int, const char *); int assertion_set_nodump(const char *, int, const char *); int assertion_text_file_contents(const char *, int, const char *buff, const char *f); int assertion_umask(const char *, int, int); int assertion_utimes(const char *, int, const char *, long, long, long, long ); int assertion_version(const char*, int, const char *, const char *); void skipping_setup(const char *, int); void test_skipping(const char *fmt, ...) __LA_PRINTFLIKE(1, 2); /* Like sprintf, then system() */ int systemf(const char *fmt, ...) __LA_PRINTFLIKE(1, 2); /* Delay until time() returns a value after this. */ void sleepUntilAfter(time_t); /* Return true if this platform can create symlinks. */ int canSymlink(void); /* Return true if this platform can run the "bzip2" program. */ int canBzip2(void); /* Return true if this platform can run the "grzip" program. */ int canGrzip(void); /* Return true if this platform can run the "gzip" program. */ int canGzip(void); /* Return true if this platform can run the specified command. */ int canRunCommand(const char *); /* Return true if this platform can run the "lrzip" program. */ int canLrzip(void); /* Return true if this platform can run the "lz4" program. */ int canLz4(void); /* Return true if this platform can run the "zstd" program. */ int canZstd(void); /* Return true if this platform can run the "lzip" program. */ int canLzip(void); /* Return true if this platform can run the "lzma" program. */ int canLzma(void); /* Return true if this platform can run the "lzop" program. */ int canLzop(void); /* Return true if this platform can run the "xz" program. */ int canXz(void); /* Return true if this filesystem can handle nodump flags. */ int canNodump(void); /* Set test ACLs */ int setTestAcl(const char *path); /* Get extended attribute */ void *getXattr(const char *, const char *, size_t *); /* Set extended attribute */ int setXattr(const char *, const char *, const void *, size_t); /* Return true if the file has large i-node number(>0xffffffff). */ int is_LargeInode(const char *); #if ARCHIVE_ACL_SUNOS /* Fetch ACLs on Solaris using acl() or facl() */ void *sunacl_get(int cmd, int *aclcnt, int fd, const char *path); #endif /* Suck file into string allocated via malloc(). Call free() when done. */ /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */ char *slurpfile(size_t *, const char *fmt, ...) __LA_PRINTFLIKE(2, 3); /* Dump block of bytes to a file. */ void dumpfile(const char *filename, void *, size_t); /* Extracts named reference file to the current directory. */ void extract_reference_file(const char *); /* Copies named reference file to the current directory. */ void copy_reference_file(const char *); /* Extracts a list of files to the current directory. * List must be NULL terminated. */ void extract_reference_files(const char **); /* Subtract umask from mode */ mode_t umasked(mode_t expected_mode); /* Path to working directory for current test */ extern const char *testworkdir; #ifndef PROGRAM /* * Special interfaces for libarchive test harness. */ #include "archive.h" #include "archive_entry.h" /* ACL structure */ struct archive_test_acl_t { int type; /* Type of ACL */ int permset; /* Permissions for this class of users. */ int tag; /* Owner, User, Owning group, group, other, etc. */ int qual; /* GID or UID of user/group, depending on tag. */ const char *name; /* Name of user/group, depending on tag. */ }; /* Set ACLs */ int assertion_entry_set_acls(const char *, int, struct archive_entry *, struct archive_test_acl_t *, int); /* Compare ACLs */ int assertion_entry_compare_acls(const char *, int, struct archive_entry *, struct archive_test_acl_t *, int, int, int); /* Special customized read-from-memory interface. */ int read_open_memory(struct archive *, const void *, size_t, size_t); /* _minimal version exercises a slightly different set of libarchive APIs. */ int read_open_memory_minimal(struct archive *, const void *, size_t, size_t); /* _seek version produces a seekable file. */ int read_open_memory_seek(struct archive *, const void *, size_t, size_t); /* Versions of above that accept an archive argument for additional info. */ #define assertA(e) assertion_assert(__FILE__, __LINE__, (e), #e, (a)) #define assertEqualIntA(a,v1,v2) \ assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a)) #define assertEqualStringA(a,v1,v2) \ assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a), 0) #else /* defined(PROGRAM) */ /* * Special interfaces for program test harness. */ /* Pathname of exe to be tested. */ extern const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ extern const char *testprog; void assertVersion(const char *prog, const char *base); #endif /* defined(PROGRAM) */ #ifdef USE_DMALLOC #include #endif #endif /* TEST_COMMON_H */ Index: stable/11/contrib/libarchive/test_utils/test_main.c =================================================================== --- stable/11/contrib/libarchive/test_utils/test_main.c (revision 368706) +++ stable/11/contrib/libarchive/test_utils/test_main.c (revision 368707) @@ -1,4103 +1,4116 @@ /* * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" #include "test_utils.h" #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LINUX_FS_H #include #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include #ifdef HAVE_SIGNAL_H #endif #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #if HAVE_SYS_XATTR_H #include #elif HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_RICHACL_H #include #endif #if HAVE_MEMBERSHIP_H #include #endif /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif mode_t umasked(mode_t expected_mode) { mode_t mode = umask(0); umask(mode); return expected_mode & ~mode; } /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; } DUMMYUNIONNAME; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int targetIsDir) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); DWORD attrs; static int set; int ret, tmpflags, llen, tlen; int flags = 0; char *src, *tgt, *p; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } if (f == NULL) return (0); tlen = strlen(target); llen = strlen(linkname); if (tlen == 0 || llen == 0) return (0); tgt = malloc((tlen + 1) * sizeof(char)); if (tgt == NULL) return (0); src = malloc((llen + 1) * sizeof(char)); if (src == NULL) { free(tgt); return (0); } /* * Translate slashes to backslashes */ p = src; while(*linkname != '\0') { if (*linkname == '/') *p = '\\'; else *p = *linkname; linkname++; p++; } *p = '\0'; p = tgt; while(*target != '\0') { if (*target == '/') *p = '\\'; else *p = *target; target++; p++; } *p = '\0'; /* * Each test has to specify if a file or a directory symlink * should be created. */ if (targetIsDir) { #if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; #else flags |= 0x1; #endif } #if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) tmpflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; #else tmpflags = flags | 0x2; #endif /* * Windows won't overwrite existing links */ attrs = GetFileAttributesA(linkname); if (attrs != INVALID_FILE_ATTRIBUTES) { if (attrs & FILE_ATTRIBUTE_DIRECTORY) RemoveDirectoryA(linkname); else DeleteFileA(linkname); } ret = (*f)(src, tgt, tmpflags); /* * Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE * is not understood */ if (!ret) ret = (*f)(src, tgt, flags); free(src); free(tgt); return (ret); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ // Silence unused-parameter compiler warnings. (void)expression; (void)function; (void)file; (void)line; (void)pReserved; } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void __LA_PRINTFLIKE(1, 0) vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void __LA_PRINTFLIKE(1, 2) logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void __LA_PRINTFLIKE(3, 4) failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } +/* change file/directory permissions and errors if it fails */ +int +assertion_chmod(const char *file, int line, const char *pathname, int mode) +{ + assertion_count(file, line); + if (chmod(pathname, mode) == 0) + return (1); + failure_start(file, line, "chmod(\"%s\", %4.o)", pathname, mode); + failure_finish(NULL); + return (0); + +} + /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalid sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not legal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %zu bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n"); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); free(buff); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); free(buff); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Verify that a text file does not contains the specified strings */ int assertion_file_contains_no_invalid_strings(const char *file, int line, const char *pathname, const char *strings[]) { char *buff; int i; buff = slurpfile(NULL, "%s", pathname); if (buff == NULL) { failure_start(file, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } for (i = 0; strings[i] != NULL; ++i) { if (strstr(buff, strings[i]) != NULL) { failure_start(file, line, "Invalid string in %s: %s", pathname, strings[i]); failure_finish(NULL); free(buff); return(0); } } free(buff); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; /* FreeBSD filesystems that don't support birthtime * (e.g., UFS1) always return -1 here. */ if (filet == -1) { return (1); } filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %ld.%09ld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mode of 'pathname'. */ int assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) { int mode; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) failure_start(file, line, "assertFileMode not yet implemented for Windows"); (void)mode; /* UNUSED */ (void)r; /* UNUSED */ (void)pathname; /* UNUSED */ (void)expected_mode; /* UNUSED */ #else { struct stat st; r = lstat(pathname, &st); mode = (int)(st.st_mode & 0777); } if (r == 0 && mode == expected_mode) return (1); failure_start(file, line, "File %s has mode %o, expected %o", pathname, mode, expected_mode); #endif failure_finish(NULL); return (0); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %jd links, expected %d", pathname, (intmax_t)bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %jd links, expected %d", pathname, (intmax_t)st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* * Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. * * On platforms with directory symlinks, set isdir to 0 to test for a file * symlink and to 1 to test for a directory symlink. On other platforms * the variable is ignored. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents, int isdir) { #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE h; DWORD inbytes; REPARSE_DATA_BUFFER *buf; BY_HANDLE_FILE_INFORMATION st; size_t len, len2; wchar_t *linknamew, *contentsw; const char *p; char *s, *pn; int ret = 0; BYTE *indata; const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT; /* Replace slashes with backslashes in pathname */ pn = malloc((strlen(pathname) + 1) * sizeof(char)); p = pathname; s = pn; while(*p != '\0') { if(*p == '/') *s = '\\'; else *s = *p; p++; s++; } *s = '\0'; h = CreateFileA(pn, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); free(pn); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } ret = GetFileInformationByHandle(h, &st); if (ret == 0) { failure_start(file, line, "Can't stat: %s", pathname); failure_finish(NULL); } else if ((st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { failure_start(file, line, "Not a symlink: %s", pathname); failure_finish(NULL); ret = 0; } if (isdir && ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)) { failure_start(file, line, "Not a directory symlink: %s", pathname); failure_finish(NULL); ret = 0; } if (!isdir && ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { failure_start(file, line, "Not a file symlink: %s", pathname); failure_finish(NULL); ret = 0; } if (ret == 0) { CloseHandle(h); return (0); } indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, 1024, &inbytes, NULL); CloseHandle(h); if (ret == 0) { free(indata); failure_start(file, line, "Could not retrieve symlink target: %s", pathname); failure_finish(NULL); return (0); } buf = (REPARSE_DATA_BUFFER *) indata; if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) { free(indata); /* File is not a symbolic link */ failure_start(file, line, "Not a symlink: %s", pathname); failure_finish(NULL); return (0); } if (contents == NULL) { free(indata); return (1); } len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength; linknamew = malloc(len + sizeof(wchar_t)); if (linknamew == NULL) { free(indata); return (0); } memcpy(linknamew, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer) [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len); free(indata); linknamew[len / sizeof(wchar_t)] = L'\0'; contentsw = malloc(len + sizeof(wchar_t)); if (contentsw == NULL) { free(linknamew); return (0); } len2 = mbsrtowcs(contentsw, &contents, (len + sizeof(wchar_t) / sizeof(wchar_t)), NULL); if (len2 > 0 && wcscmp(linknamew, contentsw) != 0) ret = 1; free(linknamew); free(contentsw); return (ret); #else char buff[300]; struct stat st; ssize_t linklen; int r; (void)isdir; /* UNUSED */ assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff) - 1); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents, int isdir) { if (is_symlink(file, line, path, contents, isdir)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) { if (0 == chmod(dirname, mode)) { assertion_file_mode(file, line, dirname, mode); return (1); } } #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (0 != chmod(path, mode)) { failure_start(file, line, "Could not chmod %s", path); failure_finish(NULL); close(fd); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); close(fd); return (0); } } close(fd); assertion_file_mode(file, line, path, mode); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* * Create a symlink and report any failures. * * Windows symlinks need to know if the target is a directory. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto, int targetIsDir) { #if defined(_WIN32) && !defined(__CYGWIN__) assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK (void)targetIsDir; /* UNUSED */ assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #else (void)targetIsDir; /* UNUSED */ #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Compare file flags */ int assertion_compare_fflags(const char *file, int line, const char *patha, const char *pathb, int nomatch) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) struct stat sa, sb; assertion_count(file, line); if (stat(patha, &sa) < 0) return (0); if (stat(pathb, &sb) < 0) return (0); if (!nomatch && sa.st_flags != sb.st_flags) { failure_start(file, line, "File flags should be identical: " "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb, sb.st_flags); failure_finish(NULL); return (0); } if (nomatch && sa.st_flags == sb.st_flags) { failure_start(file, line, "File flags should be different: " "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb, sb.st_flags); failure_finish(NULL); return (0); } #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) int fd, r, flagsa, flagsb; assertion_count(file, line); fd = open(patha, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", patha); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flagsa); close(fd); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", patha); failure_finish(NULL); return (0); } fd = open(pathb, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathb); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flagsb); close(fd); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathb); failure_finish(NULL); return (0); } if (!nomatch && flagsa != flagsb) { failure_start(file, line, "File flags should be identical: " "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb); failure_finish(NULL); return (0); } if (nomatch && flagsa == flagsb) { failure_start(file, line, "File flags should be different: " "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb); failure_finish(NULL); return (0); } #else (void)patha; /* UNUSED */ (void)pathb; /* UNUSED */ (void)nomatch; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* Set nodump, report failures. */ int assertion_set_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } #ifdef FS_NODUMP_FL flags |= FS_NODUMP_FL; #else flags |= EXT2_NODUMP_FL; #endif r = ioctl(fd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } #ifdef PROGRAM static void assert_version_id(char **qq, size_t *ss) { char *q = *qq; size_t s = *ss; /* Version number is a series of digits and periods. */ while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { ++q; --s; } if (q[0] == 'd' && q[1] == 'e' && q[2] == 'v') { q += 3; s -= 3; } /* Skip a single trailing a,b,c, or d. */ if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') ++q; /* Version number terminated by space. */ failure("No space after version: ``%s''", q); assert(s > 1); failure("No space after version: ``%s''", q); assert(*q == ' '); ++q; --s; *qq = q; *ss = s; } /* * Check program version */ void assertVersion(const char *prog, const char *base) { int r; char *p, *q; size_t s; size_t prog_len = strlen(base); r = systemf("%s --version >version.stdout 2>version.stderr", prog); if (r != 0) r = systemf("%s -W version >version.stdout 2>version.stderr", prog); failure("Unable to run either %s --version or %s -W version", prog, prog); if (!assert(r == 0)) return; /* --version should generate nothing to stdout. */ assertEmptyFile("version.stderr"); /* Verify format of version message. */ q = p = slurpfile(&s, "version.stdout"); /* Version message should start with name of program, then space. */ assert(s > prog_len + 1); failure("Version must start with '%s': ``%s''", base, p); if (!assertEqualMem(q, base, prog_len)) { free(p); return; } q += prog_len; s -= prog_len; assert(*q == ' '); q++; s--; assert_version_id(&q, &s); /* Separator. */ failure("No `-' between program name and versions: ``%s''", p); assertEqualMem(q, "- ", 2); q += 2; s -= 2; failure("Not long enough for libarchive version: ``%s''", p); assert(s > 11); failure("Libarchive version must start with `libarchive': ``%s''", p); assertEqualMem(q, "libarchive ", 11); q += 11; s -= 11; assert_version_id(&q, &s); /* Skip arbitrary third-party version numbers. */ while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum((unsigned char)*q))) { ++q; --s; } /* All terminated by end-of-line. */ assert(s >= 1); /* Skip an optional CR character (e.g., Windows) */ failure("Version output must end with \\n or \\r\\n"); if (*q == '\r') { ++q; --s; } assertEqualMem(q, "\n", 1); free(p); } #endif /* PROGRAM */ /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0", 0); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0", 0); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the zstd program? */ int canZstd(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("zstd --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz --help %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ int canNodump(void) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) \ && defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) return (0); #ifdef FS_NODUMP_FL flags |= FS_NODUMP_FL; #else flags |= EXT2_NODUMP_FL; #endif r = ioctl(fd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) return (0); close(fd); #ifdef FS_NODUMP_FL if (flags & FS_NODUMP_FL) #else if (flags & EXT2_NODUMP_FL) #endif return (1); #endif return (0); } /* Get extended attribute value from a path */ void * getXattr(const char *path, const char *name, size_t *sizep) { void *value = NULL; #if ARCHIVE_XATTR_SUPPORT ssize_t size; #if ARCHIVE_XATTR_LINUX size = lgetxattr(path, name, NULL, 0); #elif ARCHIVE_XATTR_DARWIN size = getxattr(path, name, NULL, 0, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX size = lgetea(path, name, NULL, 0); #elif ARCHIVE_XATTR_FREEBSD size = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name + 5, NULL, 0); #endif if (size >= 0) { value = malloc(size); #if ARCHIVE_XATTR_LINUX size = lgetxattr(path, name, value, size); #elif ARCHIVE_XATTR_DARWIN size = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX size = lgetea(path, name, value, size); #elif ARCHIVE_XATTR_FREEBSD size = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name + 5, value, size); #endif if (size < 0) { free(value); value = NULL; } } if (size < 0) *sizep = 0; else *sizep = (size_t)size; #else /* !ARCHIVE_XATTR_SUPPORT */ (void)path; /* UNUSED */ (void)name; /* UNUSED */ *sizep = 0; #endif /* !ARCHIVE_XATTR_SUPPORT */ return (value); } /* * Set extended attribute on a path * Returns 0 on error, 1 on success */ int setXattr(const char *path, const char *name, const void *value, size_t size) { #if ARCHIVE_XATTR_SUPPORT #if ARCHIVE_XATTR_LINUX if (lsetxattr(path, name, value, size, 0) == 0) #elif ARCHIVE_XATTR_DARWIN if (setxattr(path, name, value, size, 0, XATTR_NOFOLLOW) == 0) #elif ARCHIVE_XATTR_AIX if (lsetea(path, name, value, size, 0) == 0) #elif ARCHIVE_XATTR_FREEBSD if (extattr_set_link(path, EXTATTR_NAMESPACE_USER, name + 5, value, size) > -1) #else if (0) #endif return (1); #else /* !ARCHIVE_XATTR_SUPPORT */ (void)path; /* UNUSED */ (void)name; /* UNUSED */ (void)value; /* UNUSED */ (void)size; /* UNUSED */ #endif /* !ARCHIVE_XATTR_SUPPORT */ return (0); } #if ARCHIVE_ACL_SUNOS /* Fetch ACLs on Solaris using acl() or facl() */ void * sunacl_get(int cmd, int *aclcnt, int fd, const char *path) { int cnt, cntcmd; size_t size; void *aclp; if (cmd == GETACL) { cntcmd = GETACLCNT; size = sizeof(aclent_t); } #if ARCHIVE_ACL_SUNOS_NFS4 else if (cmd == ACE_GETACL) { cntcmd = ACE_GETACLCNT; size = sizeof(ace_t); } #endif else { errno = EINVAL; *aclcnt = -1; return (NULL); } aclp = NULL; cnt = -2; while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) { if (path != NULL) cnt = acl(path, cntcmd, 0, NULL); else cnt = facl(fd, cntcmd, 0, NULL); if (cnt > 0) { if (aclp == NULL) aclp = malloc(cnt * size); else aclp = realloc(NULL, cnt * size); if (aclp != NULL) { if (path != NULL) cnt = acl(path, cmd, cnt, aclp); else cnt = facl(fd, cmd, cnt, aclp); } } else { free(aclp); aclp = NULL; break; } } *aclcnt = cnt; return (aclp); } #endif /* ARCHIVE_ACL_SUNOS */ /* * Set test ACLs on a path * Return values: * 0: error setting ACLs * ARCHIVE_TEST_ACL_TYPE_POSIX1E: POSIX.1E ACLs have been set * ARCHIVE_TEST_ACL_TYPE_NFS4: NFSv4 or extended ACLs have been set */ int setTestAcl(const char *path) { #if ARCHIVE_ACL_SUPPORT int r = 1; #if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN acl_t acl; #endif #if ARCHIVE_ACL_LIBRICHACL struct richacl *richacl; #endif #if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_FREEBSD const char *acltext_posix1e = "user:1:rw-," "group:15:r-x," "user::rwx," "group::rwx," "other::r-x," "mask::rwx"; #elif ARCHIVE_ACL_SUNOS /* Solaris POSIX.1e */ aclent_t aclp_posix1e[] = { { USER_OBJ, -1, 4 | 2 | 1 }, { USER, 1, 4 | 2 }, { GROUP_OBJ, -1, 4 | 2 | 1 }, { GROUP, 15, 4 | 1 }, { CLASS_OBJ, -1, 4 | 2 | 1 }, { OTHER_OBJ, -1, 4 | 2 | 1 } }; #endif #if ARCHIVE_ACL_FREEBSD /* FreeBSD NFS4 */ const char *acltext_nfs4 = "user:1:rwpaRcs::allow:1," "group:15:rxaRcs::allow:15," "owner@:rwpxaARWcCos::allow," "group@:rwpxaRcs::allow," "everyone@:rxaRcs::allow"; #elif ARCHIVE_ACL_LIBRICHACL const char *acltext_nfs4 = "owner:rwpxaARWcCoS::mask," "group:rwpxaRcS::mask," "other:rxaRcS::mask," "user:1:rwpaRcS::allow," "group:15:rxaRcS::allow," "owner@:rwpxaARWcCoS::allow," "group@:rwpxaRcS::allow," "everyone@:rxaRcS::allow"; #elif ARCHIVE_ACL_SUNOS_NFS4 /* Solaris NFS4 */ ace_t aclp_nfs4[] = { { 1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, 0, ACE_ACCESS_ALLOWED_ACE_TYPE }, { 15, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS | ACE_READ_ACL | ACE_WRITE_ACL | ACE_WRITE_OWNER | ACE_SYNCHRONIZE, ACE_OWNER, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_GROUP | ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_EVERYONE, ACE_ACCESS_ALLOWED_ACE_TYPE } }; #elif ARCHIVE_ACL_DARWIN /* Mac OS X */ acl_entry_t aclent; acl_permset_t permset; const uid_t uid = 1; uuid_t uuid; int i; const acl_perm_t acl_perms[] = { ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE, ACL_READ_ATTRIBUTES, ACL_READ_EXTATTRIBUTES, ACL_READ_SECURITY, #if HAVE_DECL_ACL_SYNCHRONIZE ACL_SYNCHRONIZE #endif }; #endif /* ARCHIVE_ACL_DARWIN */ #if ARCHIVE_ACL_FREEBSD acl = acl_from_text(acltext_nfs4); failure("acl_from_text() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); #elif ARCHIVE_ACL_LIBRICHACL richacl = richacl_from_text(acltext_nfs4, NULL, NULL); failure("richacl_from_text() error: %s", strerror(errno)); if (assert(richacl != NULL) == 0) return (0); #elif ARCHIVE_ACL_DARWIN acl = acl_init(1); failure("acl_init() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); r = acl_create_entry(&acl, &aclent); failure("acl_create_entry() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_set_tag_type(aclent, ACL_EXTENDED_ALLOW); failure("acl_set_tag_type() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_get_permset(aclent, &permset); failure("acl_get_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; for (i = 0; i < (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); i++) { r = acl_add_perm(permset, acl_perms[i]); failure("acl_add_perm() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; } r = acl_set_permset(aclent, permset); failure("acl_set_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = mbr_uid_to_uuid(uid, uuid); failure("mbr_uid_to_uuid() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_set_qualifier(aclent, uuid); failure("acl_set_qualifier() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; #endif /* ARCHIVE_ACL_DARWIN */ #if ARCHIVE_ACL_NFS4 #if ARCHIVE_ACL_FREEBSD r = acl_set_file(path, ACL_TYPE_NFS4, acl); acl_free(acl); #elif ARCHIVE_ACL_LIBRICHACL r = richacl_set_file(path, richacl); richacl_free(richacl); #elif ARCHIVE_ACL_SUNOS_NFS4 r = acl(path, ACE_SETACL, (int)(sizeof(aclp_nfs4)/sizeof(aclp_nfs4[0])), aclp_nfs4); #elif ARCHIVE_ACL_DARWIN r = acl_set_file(path, ACL_TYPE_EXTENDED, acl); acl_free(acl); #endif if (r == 0) return (ARCHIVE_TEST_ACL_TYPE_NFS4); #endif /* ARCHIVE_ACL_NFS4 */ #if ARCHIVE_ACL_POSIX1E #if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL acl = acl_from_text(acltext_posix1e); failure("acl_from_text() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); r = acl_set_file(path, ACL_TYPE_ACCESS, acl); acl_free(acl); #elif ARCHIVE_ACL_SUNOS r = acl(path, SETACL, (int)(sizeof(aclp_posix1e)/sizeof(aclp_posix1e[0])), aclp_posix1e); #endif if (r == 0) return (ARCHIVE_TEST_ACL_TYPE_POSIX1E); else return (0); #endif /* ARCHIVE_ACL_POSIX1E */ #if ARCHIVE_ACL_DARWIN testacl_free: acl_free(acl); #endif #endif /* ARCHIVE_ACL_SUPPORT */ (void)path; /* UNUSED */ return (0); } /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } #ifndef PROGRAM /* Set ACLs */ int assertion_entry_set_acls(const char *file, int line, struct archive_entry *ae, struct archive_test_acl_t *acls, int n) { int i, r, ret; assertion_count(file, line); ret = 0; archive_entry_acl_clear(ae); for (i = 0; i < n; i++) { r = archive_entry_acl_add_entry(ae, acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name); if (r != 0) { ret = 1; failure_start(file, line, "type=%#010x, " "permset=%#010x, tag=%d, qual=%d name=%s", acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name); failure_finish(NULL); } } return (ret); } static int archive_test_acl_match(struct archive_test_acl_t *acl, int type, int permset, int tag, int qual, const char *name) { if (type != acl->type) return (0); if (permset != acl->permset) return (0); if (tag != acl->tag) return (0); if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) return (1); if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (1); if (tag == ARCHIVE_ENTRY_ACL_EVERYONE) return (1); if (tag == ARCHIVE_ENTRY_ACL_OTHER) return (1); if (qual != acl->qual) return (0); if (name == NULL) { if (acl->name == NULL || acl->name[0] == '\0') return (1); return (0); } if (acl->name == NULL) { if (name[0] == '\0') return (1); return (0); } return (0 == strcmp(name, acl->name)); } /* Compare ACLs */ int assertion_entry_compare_acls(const char *file, int line, struct archive_entry *ae, struct archive_test_acl_t *acls, int cnt, int want_type, int mode) { int *marker; int i, r, n, ret; int type, permset, tag, qual; int matched; const char *name; assertion_count(file, line); ret = 0; n = 0; marker = malloc(sizeof(marker[0]) * cnt); for (i = 0; i < cnt; i++) { if ((acls[i].type & want_type) != 0) { marker[n] = i; n++; } } if (n == 0) { failure_start(file, line, "No ACL's to compare, type mask: %d", want_type); return (1); } while (0 == (r = archive_entry_acl_next(ae, want_type, &type, &permset, &tag, &qual, &name))) { for (i = 0, matched = 0; i < n && !matched; i++) { if (archive_test_acl_match(&acls[marker[i]], type, permset, tag, qual, name)) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_USER_OBJ) { if (!matched) { failure_start(file, line, "No match for " "user_obj perm"); failure_finish(NULL); ret = 1; } if ((permset << 6) != (mode & 0700)) { failure_start(file, line, "USER_OBJ permset " "(%02o) != user mode (%02o)", permset, 07 & (mode >> 6)); failure_finish(NULL); ret = 1; } } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) { if (!matched) { failure_start(file, line, "No match for " "group_obj perm"); failure_finish(NULL); ret = 1; } if ((permset << 3) != (mode & 0070)) { failure_start(file, line, "GROUP_OBJ permset " "(%02o) != group mode (%02o)", permset, 07 & (mode >> 3)); failure_finish(NULL); ret = 1; } } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_OTHER) { if (!matched) { failure_start(file, line, "No match for " "other perm"); failure_finish(NULL); ret = 1; } if ((permset << 0) != (mode & 0007)) { failure_start(file, line, "OTHER permset " "(%02o) != other mode (%02o)", permset, mode & 07); failure_finish(NULL); ret = 1; } } else if (matched != 1) { failure_start(file, line, "Could not find match for " "ACL (type=%#010x,permset=%#010x,tag=%d,qual=%d," "name=``%s'')", type, permset, tag, qual, name); failure_finish(NULL); ret = 1; } } if (r != ARCHIVE_EOF) { failure_start(file, line, "Should not exit before EOF"); failure_finish(NULL); ret = 1; } if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 && (mode_t)(mode & 0777) != (archive_entry_mode(ae) & 0777)) { failure_start(file, line, "Mode (%02o) and entry mode (%02o) " "mismatch", mode, archive_entry_mode(ae)); failure_finish(NULL); ret = 1; } if (n != 0) { failure_start(file, line, "Could not find match for ACL " "(type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s'')", acls[marker[0]].type, acls[marker[0]].permset, acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name); failure_finish(NULL); ret = 1; /* Number of ACLs not matched should == 0 */ } free(marker); return (ret); } #endif /* !defined(PROGRAM) */ /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { #ifdef PATH_MAX char workdir[PATH_MAX * 2]; #else char workdir[1024 * 2]; #endif char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { size_t tried_size, buff_size; char *buff, *tried, *pwd = NULL, *p = NULL; #ifdef PATH_MAX buff_size = PATH_MAX; #else buff_size = 8192; #endif buff = calloc(buff_size, 1); if (buff == NULL) { fprintf(stderr, "Unable to allocate memory\n"); exit(1); } /* Allocate a buffer to hold the various directories we checked. */ tried_size = buff_size * 2; tried = calloc(tried_size, 1); if (tried == NULL) { fprintf(stderr, "Unable to allocate memory\n"); exit(1); } /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); free(tried); /* Copy result into a fresh buffer to reduce memory usage. */ p = strdup(buff); free(buff); return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; #ifdef PATH_MAX char tmpdir[PATH_MAX]; #else char tmpdir[256]; #endif char *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[32]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); if ((strlen(tmp) + 1 + strlen(progname) + 1 + strlen(tmpdir_timestamp) + 1 + 3) > (sizeof(tmpdir) / sizeof(char))) { fprintf(stderr, "ERROR: Temp directory pathname too long\n"); exit(1); } snprintf(tmpdir, sizeof(tmpdir), "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: stable/11/lib/libarchive/Makefile =================================================================== --- stable/11/lib/libarchive/Makefile (revision 368706) +++ stable/11/lib/libarchive/Makefile (revision 368707) @@ -1,427 +1,428 @@ # $FreeBSD$ .include PACKAGE=lib${LIB} _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive LIB= archive LIBADD= z bz2 lzma bsdxml CFLAGS+= -DHAVE_BZLIB_H=1 -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system. # It has no real relation to the libarchive version number. SHLIB_MAJOR= 6 CFLAGS+= -DPLATFORM_CONFIG_H=\"${.CURDIR}/config_freebsd.h\" CFLAGS+= -I${.OBJDIR} .if ${MK_OPENSSL} != "no" CFLAGS+= -DWITH_OPENSSL LIBADD+= crypto .else LIBADD+= md .endif .if ${MK_ICONV} != "no" # TODO: This can be changed back to CFLAGS once iconv works correctly # with statically linked binaries. SHARED_CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST= .endif .if ${MACHINE_ARCH:Marm*} != "" || ${MACHINE_ARCH:Mmips*} != "" || \ ${MACHINE_ARCH:Msparc64*} != "" NO_WCAST_ALIGN= yes .if ${MACHINE_ARCH:M*64*} == "" CFLAGS+= -DPPMD_32BIT .endif .endif NO_WCAST_ALIGN.clang= .PATH: ${_LIBARCHIVEDIR}/libarchive # Headers to be installed in /usr/include INCS= archive.h archive_entry.h # Sources to be compiled. SRCS= archive_acl.c \ archive_blake2sp_ref.c \ archive_blake2s_ref.c \ archive_check_magic.c \ archive_cmdline.c \ archive_cryptor.c \ archive_disk_acl_freebsd.c \ archive_digest.c \ archive_entry.c \ archive_entry_copy_stat.c \ archive_entry_link_resolver.c \ archive_entry_sparse.c \ archive_entry_stat.c \ archive_entry_strmode.c \ archive_entry_xattr.c \ archive_getdate.c \ archive_hmac.c \ archive_match.c \ archive_options.c \ archive_pack_dev.c \ archive_pathmatch.c \ archive_ppmd7.c \ archive_ppmd8.c \ archive_random.c \ archive_rb.c \ archive_read.c \ archive_read_add_passphrase.c \ archive_read_append_filter.c \ archive_read_data_into_fd.c \ archive_read_disk_entry_from_file.c \ archive_read_disk_posix.c \ archive_read_disk_set_standard_lookup.c \ archive_read_extract.c \ archive_read_extract2.c \ archive_read_open_fd.c \ archive_read_open_file.c \ archive_read_open_filename.c \ archive_read_open_memory.c \ archive_read_set_format.c \ archive_read_set_options.c \ archive_read_support_filter_all.c \ archive_read_support_filter_bzip2.c \ + archive_read_support_filter_by_code.c \ archive_read_support_filter_compress.c \ archive_read_support_filter_gzip.c \ archive_read_support_filter_grzip.c \ archive_read_support_filter_lrzip.c \ archive_read_support_filter_lz4.c \ archive_read_support_filter_lzop.c \ archive_read_support_filter_none.c \ archive_read_support_filter_program.c \ archive_read_support_filter_rpm.c \ archive_read_support_filter_uu.c \ archive_read_support_filter_xz.c \ archive_read_support_filter_zstd.c \ archive_read_support_format_7zip.c \ archive_read_support_format_all.c \ archive_read_support_format_ar.c \ archive_read_support_format_by_code.c \ archive_read_support_format_cab.c \ archive_read_support_format_cpio.c \ archive_read_support_format_empty.c \ archive_read_support_format_iso9660.c \ archive_read_support_format_lha.c \ archive_read_support_format_mtree.c \ archive_read_support_format_rar.c \ archive_read_support_format_rar5.c \ archive_read_support_format_raw.c \ archive_read_support_format_tar.c \ archive_read_support_format_warc.c \ archive_read_support_format_xar.c \ archive_read_support_format_zip.c \ archive_string.c \ archive_string_sprintf.c \ archive_util.c \ archive_version_details.c \ archive_virtual.c \ archive_write.c \ archive_write_add_filter.c \ archive_write_disk_set_standard_lookup.c \ archive_write_disk_posix.c \ archive_write_open_fd.c \ archive_write_open_file.c \ archive_write_open_filename.c \ archive_write_open_memory.c \ archive_write_add_filter_b64encode.c \ archive_write_add_filter_by_name.c \ archive_write_add_filter_bzip2.c \ archive_write_add_filter_compress.c \ archive_write_add_filter_grzip.c \ archive_write_add_filter_gzip.c \ archive_write_add_filter_lrzip.c \ archive_write_add_filter_lz4.c \ archive_write_add_filter_lzop.c \ archive_write_add_filter_none.c \ archive_write_add_filter_program.c \ archive_write_add_filter_uuencode.c \ archive_write_add_filter_xz.c \ archive_write_add_filter_zstd.c \ archive_write_set_format.c \ archive_write_set_format_7zip.c \ archive_write_set_format_ar.c \ archive_write_set_format_by_name.c \ archive_write_set_format_cpio.c \ archive_write_set_format_cpio_newc.c \ archive_write_set_format_filter_by_ext.c \ archive_write_set_format_gnutar.c \ archive_write_set_format_iso9660.c \ archive_write_set_format_mtree.c \ archive_write_set_format_pax.c \ archive_write_set_format_raw.c \ archive_write_set_format_shar.c \ archive_write_set_format_ustar.c \ archive_write_set_format_v7tar.c \ archive_write_set_format_warc.c \ archive_write_set_format_xar.c \ archive_write_set_format_zip.c \ archive_write_set_passphrase.c \ archive_write_set_options.c \ filter_fork_posix.c # Man pages to be installed. MAN= archive_entry.3 \ archive_entry_acl.3 \ archive_entry_linkify.3 \ archive_entry_misc.3 \ archive_entry_paths.3 \ archive_entry_perms.3 \ archive_entry_stat.3 \ archive_entry_time.3 \ archive_read.3 \ archive_read_data.3 \ archive_read_disk.3 \ archive_read_extract.3 \ archive_read_filter.3 \ archive_read_format.3 \ archive_read_free.3 \ archive_read_header.3 \ archive_read_new.3 \ archive_read_open.3 \ archive_read_set_options.3 \ archive_util.3 \ archive_write.3 \ archive_write_blocksize.3 \ archive_write_data.3 \ archive_write_disk.3 \ archive_write_filter.3 \ archive_write_finish_entry.3 \ archive_write_format.3 \ archive_write_free.3 \ archive_write_header.3 \ archive_write_new.3 \ archive_write_open.3 \ archive_write_set_options.3 \ cpio.5 \ libarchive.3 \ libarchive_changes.3 \ libarchive_internals.3 \ libarchive-formats.5 \ tar.5 # Symlink the man pages under each function name. MLINKS+= archive_entry.3 archive_entry_clear.3 MLINKS+= archive_entry.3 archive_entry_clone.3 MLINKS+= archive_entry.3 archive_entry_free.3 MLINKS+= archive_entry.3 archive_entry_new.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_add_entry.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_add_entry_w.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_clear.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_count.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_next.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_next_w.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_reset.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_text_w.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_new.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_set_strategy.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_free.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_hardlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_link.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_link_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_pathname_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_sourcepath.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_symlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_hardlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_pathname_w.3 MLINKS+= archive_entry_paths.3 archive_entry_set_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_set_link.3 MLINKS+= archive_entry_paths.3 archive_entry_set_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_set_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_symlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_update_symlink_utf8.3 MLINKS+= archive_entry_paths.3 archive_entry_update_hardlink_utf8.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_fflags_text.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_fflags_text_w.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_gname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_uname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_fflags.3 MLINKS+= archive_entry_perms.3 archive_entry_fflags_text.3 MLINKS+= archive_entry_perms.3 archive_entry_gid.3 MLINKS+= archive_entry_perms.3 archive_entry_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_gname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_set_fflags.3 MLINKS+= archive_entry_perms.3 archive_entry_set_gid.3 MLINKS+= archive_entry_perms.3 archive_entry_set_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_perm.3 MLINKS+= archive_entry_perms.3 archive_entry_set_perm.3 MLINKS+= archive_entry_perms.3 archive_entry_set_uid.3 MLINKS+= archive_entry_perms.3 archive_entry_set_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_strmode.3 MLINKS+= archive_entry_perms.3 archive_entry_uid.3 MLINKS+= archive_entry_perms.3 archive_entry_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_uname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_update_gname_utf8.3 MLINKS+= archive_entry_perms.3 archive_entry_update_uname_utf8.3 MLINKS+= archive_entry_stat.3 archive_entry_copy_stat.3 MLINKS+= archive_entry_stat.3 archive_entry_dev.3 MLINKS+= archive_entry_stat.3 archive_entry_dev_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_devmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_devminor.3 MLINKS+= archive_entry_stat.3 archive_entry_filetype.3 MLINKS+= archive_entry_stat.3 archive_entry_ino.3 MLINKS+= archive_entry_stat.3 archive_entry_ino64.3 MLINKS+= archive_entry_stat.3 archive_entry_ino_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_mode.3 MLINKS+= archive_entry_stat.3 archive_entry_nlink.3 MLINKS+= archive_entry_stat.3 archive_entry_rdev.3 MLINKS+= archive_entry_stat.3 archive_entry_rdevmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_rdevminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_dev.3 MLINKS+= archive_entry_stat.3 archive_entry_set_devmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_devminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_filetype.3 MLINKS+= archive_entry_stat.3 archive_entry_set_ino.3 MLINKS+= archive_entry_stat.3 archive_entry_set_ino64.3 MLINKS+= archive_entry_stat.3 archive_entry_set_mode.3 MLINKS+= archive_entry_stat.3 archive_entry_set_nlink.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdev.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdevmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdevminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_size.3 MLINKS+= archive_entry_stat.3 archive_entry_size.3 MLINKS+= archive_entry_stat.3 archive_entry_size_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_unset_size.3 MLINKS+= archive_entry_time.3 archive_entry_atime.3 MLINKS+= archive_entry_time.3 archive_entry_atime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_atime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_ctime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_ctime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_mtime.3 MLINKS+= archive_entry_time.3 archive_entry_mtime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_mtime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_set_atime.3 MLINKS+= archive_entry_time.3 archive_entry_set_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_set_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_set_mtime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_atime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_mtime.3 MLINKS+= archive_read_data.3 archive_read_data_block.3 MLINKS+= archive_read_data.3 archive_read_data_into_fd.3 MLINKS+= archive_read_data.3 archive_read_data_skip.3 MLINKS+= archive_read_header.3 archive_read_next_header.3 MLINKS+= archive_read_header.3 archive_read_next_header2.3 MLINKS+= archive_read_extract.3 archive_read_extract2.3 MLINKS+= archive_read_extract.3 archive_read_extract_set_progress_callback.3 MLINKS+= archive_read_extract.3 archive_read_extract_set_skip_file.3 MLINKS+= archive_read_open.3 archive_read_open2.3 MLINKS+= archive_read_open.3 archive_read_open_FILE.3 MLINKS+= archive_read_open.3 archive_read_open_fd.3 MLINKS+= archive_read_open.3 archive_read_open_file.3 MLINKS+= archive_read_open.3 archive_read_open_filename.3 MLINKS+= archive_read_open.3 archive_read_open_memory.3 MLINKS+= archive_read_free.3 archive_read_close.3 MLINKS+= archive_read_free.3 archive_read_finish.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_all.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_bzip2.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_compress.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_gzip.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_lzma.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_none.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_xz.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_program.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_program_signature.3 MLINKS+= archive_read_format.3 archive_read_support_format_7zip.3 MLINKS+= archive_read_format.3 archive_read_support_format_all.3 MLINKS+= archive_read_format.3 archive_read_support_format_ar.3 MLINKS+= archive_read_format.3 archive_read_support_format_by_code.3 MLINKS+= archive_read_format.3 archive_read_support_format_cab.3 MLINKS+= archive_read_format.3 archive_read_support_format_cpio.3 MLINKS+= archive_read_format.3 archive_read_support_format_empty.3 MLINKS+= archive_read_format.3 archive_read_support_format_iso9660.3 MLINKS+= archive_read_format.3 archive_read_support_format_lha.3 MLINKS+= archive_read_format.3 archive_read_support_format_mtree.3 MLINKS+= archive_read_format.3 archive_read_support_format_rar.3 MLINKS+= archive_read_format.3 archive_read_support_format_raw.3 MLINKS+= archive_read_format.3 archive_read_support_format_tar.3 MLINKS+= archive_read_format.3 archive_read_support_format_xar.3 MLINKS+= archive_read_format.3 archive_read_support_format_zip.3 MLINKS+= archive_read_disk.3 archive_read_disk_entry_from_file.3 MLINKS+= archive_read_disk.3 archive_read_disk_gname.3 MLINKS+= archive_read_disk.3 archive_read_disk_new.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_gname_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_standard_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_hybrid.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_logical.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_physical.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_uname_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_uname.3 MLINKS+= archive_read_set_options.3 archive_read_set_filter_option.3 MLINKS+= archive_read_set_options.3 archive_read_set_format_option.3 MLINKS+= archive_read_set_options.3 archive_read_set_option.3 MLINKS+= archive_util.3 archive_clear_error.3 MLINKS+= archive_util.3 archive_compression.3 MLINKS+= archive_util.3 archive_compression_name.3 MLINKS+= archive_util.3 archive_copy_error.3 MLINKS+= archive_util.3 archive_errno.3 MLINKS+= archive_util.3 archive_error_string.3 MLINKS+= archive_util.3 archive_file_count.3 MLINKS+= archive_util.3 archive_filter_code.3 MLINKS+= archive_util.3 archive_filter_count.3 MLINKS+= archive_util.3 archive_filter_name.3 MLINKS+= archive_util.3 archive_format.3 MLINKS+= archive_util.3 archive_format_name.3 MLINKS+= archive_util.3 archive_position.3 MLINKS+= archive_util.3 archive_set_error.3 MLINKS+= archive_write_blocksize.3 archive_write_get_bytes_in_last_block.3 MLINKS+= archive_write_blocksize.3 archive_write_get_bytes_per_block.3 MLINKS+= archive_write_blocksize.3 archive_write_set_bytes_in_last_block.3 MLINKS+= archive_write_blocksize.3 archive_write_set_bytes_per_block.3 MLINKS+= archive_write_disk.3 archive_write_data_block.3 MLINKS+= archive_write_disk.3 archive_write_disk_new.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_group_lookup.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_options.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_skip_file.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_standard_lookup.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_user_lookup.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_bzip2.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_compress.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_gzip.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_lzip.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_lzma.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_none.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_program.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_xz.3 MLINKS+= archive_write_format.3 archive_write_set_format_cpio.3 MLINKS+= archive_write_format.3 archive_write_set_format_pax.3 MLINKS+= archive_write_format.3 archive_write_set_format_pax_restricted.3 MLINKS+= archive_write_format.3 archive_write_set_format_shar.3 MLINKS+= archive_write_format.3 archive_write_set_format_shar_dump.3 MLINKS+= archive_write_format.3 archive_write_set_format_ustar.3 MLINKS+= archive_write_free.3 archive_write_close.3 MLINKS+= archive_write_free.3 archive_write_fail.3 MLINKS+= archive_write_free.3 archive_write_finish.3 MLINKS+= archive_write_open.3 archive_write_open_FILE.3 MLINKS+= archive_write_open.3 archive_write_open_fd.3 MLINKS+= archive_write_open.3 archive_write_open_file.3 MLINKS+= archive_write_open.3 archive_write_open_filename.3 MLINKS+= archive_write_open.3 archive_write_open_memory.3 MLINKS+= archive_write_set_options.3 archive_write_set_filter_option.3 MLINKS+= archive_write_set_options.3 archive_write_set_format_option.3 MLINKS+= archive_write_set_options.3 archive_write_set_option.3 MLINKS+= libarchive.3 archive.3 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/11/lib/libarchive/config_freebsd.h =================================================================== --- stable/11/lib/libarchive/config_freebsd.h (revision 368706) +++ stable/11/lib/libarchive/config_freebsd.h (revision 368707) @@ -1,257 +1,259 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ +#define __LIBARCHIVE_CONFIG_H_INCLUDED 1 #include /* FreeBSD 5.0 and later has ACL and extattr support. */ #if __FreeBSD__ > 4 #define ARCHIVE_ACL_FREEBSD 1 #define ARCHIVE_XATTR_FREEBSD 1 #define HAVE_ACL_GET_PERM_NP 1 #define HAVE_ARC4RANDOM_BUF 1 #define HAVE_STRUCT_XVFSCONF 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 #if __FreeBSD__ > 7 /* FreeBSD 8.0 and later has NFSv4 ACL support */ #define ARCHIVE_ACL_FREEBSD_NFS4 1 #define HAVE_ACL_GET_LINK_NP 1 #define HAVE_ACL_IS_TRIVIAL_NP 1 #define HAVE_ACL_SET_LINK_NP 1 #endif /* __FreeBSD__ > 7 */ #endif /* __FreeBSD__ > 4 */ #ifdef WITH_OPENSSL #define HAVE_LIBCRYPTO 1 #define HAVE_OPENSSL_EVP_H 1 #define HAVE_OPENSSL_MD5_H 1 #define HAVE_OPENSSL_RIPEMD_H 1 #define HAVE_OPENSSL_SHA_H 1 #define HAVE_OPENSSL_SHA256_INIT 1 #define HAVE_OPENSSL_SHA384_INIT 1 #define HAVE_OPENSSL_SHA512_INIT 1 #define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1 #define HAVE_SHA256 1 #define HAVE_SHA384 1 #define HAVE_SHA512 1 #else #define HAVE_LIBMD 1 #define HAVE_MD5_H 1 #define HAVE_MD5INIT 1 #define HAVE_RIPEMD_H 1 #define HAVE_SHA_H 1 #define HAVE_SHA1 1 #define HAVE_SHA1_INIT 1 #define HAVE_SHA256 1 #define HAVE_SHA256_H 1 #define HAVE_SHA256_INIT 1 #define HAVE_SHA512 1 #define HAVE_SHA512_H 1 #define HAVE_SHA512_INIT 1 #endif #define HAVE_BSDXML_H 1 #define HAVE_BZLIB_H 1 #define HAVE_CHFLAGS 1 #define HAVE_CHOWN 1 #define HAVE_CHROOT 1 #define HAVE_CTIME_R 1 #define HAVE_CTYPE_H 1 #define HAVE_DECL_EXTATTR_NAMESPACE_USER 1 #define HAVE_DECL_INT32_MAX 1 #define HAVE_DECL_INT32_MIN 1 #define HAVE_DECL_INT64_MAX 1 #define HAVE_DECL_INT64_MIN 1 #define HAVE_DECL_INTMAX_MAX 1 #define HAVE_DECL_INTMAX_MIN 1 #define HAVE_DECL_SIZE_MAX 1 #define HAVE_DECL_SSIZE_MAX 1 #define HAVE_DECL_STRERROR_R 1 #define HAVE_DECL_UINT32_MAX 1 #define HAVE_DECL_UINT64_MAX 1 #define HAVE_DECL_UINTMAX_MAX 1 #define HAVE_DIRENT_H 1 #define HAVE_DLFCN_H 1 #define HAVE_D_MD_ORDER 1 #define HAVE_EFTYPE 1 #define HAVE_EILSEQ 1 #define HAVE_ERRNO_H 1 #define HAVE_FCHDIR 1 #define HAVE_FCHFLAGS 1 #define HAVE_FCHMOD 1 #define HAVE_FCHOWN 1 #define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FDOPENDIR 1 #define HAVE_FORK 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 #define HAVE_FSTATAT 1 #define HAVE_FSTATFS 1 #define HAVE_FSTATVFS 1 #define HAVE_FTRUNCATE 1 #define HAVE_FUTIMES 1 #define HAVE_FUTIMESAT 1 #define HAVE_GETEUID 1 #define HAVE_GETGRGID_R 1 #define HAVE_GETGRNAM_R 1 #define HAVE_GETPID 1 #define HAVE_GETPWNAM_R 1 #define HAVE_GETPWUID_R 1 #define HAVE_GETVFSBYNAME 1 #define HAVE_GMTIME_R 1 #define HAVE_GRP_H 1 #define HAVE_INTMAX_T 1 #define HAVE_INTTYPES_H 1 #define HAVE_LANGINFO_H 1 #define HAVE_LCHFLAGS 1 #define HAVE_LCHMOD 1 #define HAVE_LCHOWN 1 #define HAVE_LIBZ 1 #define HAVE_LIMITS_H 1 #define HAVE_LINK 1 #define HAVE_LOCALE_H 1 #define HAVE_LOCALTIME_R 1 #define HAVE_LONG_LONG_INT 1 #define HAVE_LSTAT 1 #define HAVE_LUTIMES 1 #define HAVE_MBRTOWC 1 #define HAVE_MEMMOVE 1 #define HAVE_MEMORY_H 1 #define HAVE_MEMSET 1 #define HAVE_MKDIR 1 #define HAVE_MKFIFO 1 #define HAVE_MKNOD 1 #define HAVE_MKSTEMP 1 #define HAVE_NL_LANGINFO 1 #define HAVE_OPENAT 1 #define HAVE_PATHS_H 1 #define HAVE_PIPE 1 #define HAVE_POLL 1 #define HAVE_POLL_H 1 #define HAVE_POSIX_SPAWNP 1 #define HAVE_PTHREAD_H 1 #define HAVE_PWD_H 1 #define HAVE_READDIR_R 1 #define HAVE_READLINK 1 #define HAVE_READLINKAT 1 #define HAVE_READPASSPHRASE 1 #define HAVE_READPASSPHRASE_H 1 #define HAVE_REGEX_H 1 #define HAVE_SELECT 1 #define HAVE_SETENV 1 #define HAVE_SETLOCALE 1 #define HAVE_SIGACTION 1 #define HAVE_SIGNAL_H 1 #define HAVE_SPAWN_H 1 #define HAVE_STATFS 1 #define HAVE_STATVFS 1 #define HAVE_STDARG_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRCHR 1 #define HAVE_STRDUP 1 #define HAVE_STRERROR 1 #define HAVE_STRERROR_R 1 #define HAVE_STRFTIME 1 #define HAVE_STRINGS_H 1 #define HAVE_STRING_H 1 +#define HAVE_STRNLEN 1 #define HAVE_STRRCHR 1 #define HAVE_STRUCT_STATFS_F_NAMEMAX 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 #define HAVE_STRUCT_STAT_ST_FLAGS 1 #define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 #define HAVE_STRUCT_TM_TM_GMTOFF 1 #define HAVE_SYMLINK 1 #define HAVE_SYS_CDEFS_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_POLL_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STATVFS_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TIME_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_SYS_UTSNAME_H 1 #define HAVE_SYS_WAIT_H 1 #define HAVE_TIMEGM 1 #define HAVE_TIME_H 1 #define HAVE_TZSET 1 #define HAVE_UINTMAX_T 1 #define HAVE_UNISTD_H 1 #define HAVE_UNLINKAT 1 #define HAVE_UNSETENV 1 #define HAVE_UNSIGNED_LONG_LONG 1 #define HAVE_UNSIGNED_LONG_LONG_INT 1 #define HAVE_UTIME 1 #define HAVE_UTIMES 1 #define HAVE_UTIME_H 1 #define HAVE_VFORK 1 #define HAVE_VPRINTF 1 #define HAVE_WCHAR_H 1 #define HAVE_WCHAR_T 1 #define HAVE_WCRTOMB 1 #define HAVE_WCSCMP 1 #define HAVE_WCSCPY 1 #define HAVE_WCSLEN 1 #define HAVE_WCTOMB 1 #define HAVE_WCTYPE_H 1 #define HAVE_WMEMCMP 1 #define HAVE_WMEMCPY 1 #define HAVE_WMEMMOVE 1 #define HAVE_ZLIB_H 1 #define TIME_WITH_SYS_TIME 1 #if __FreeBSD_version >= 1100056 #define HAVE_FUTIMENS 1 #define HAVE_UTIMENSAT 1 #endif /* FreeBSD 4 and earlier lack intmax_t/uintmax_t */ #if __FreeBSD__ < 5 #define intmax_t int64_t #define uintmax_t uint64_t #endif /* FreeBSD defines for archive_hash.h */ #ifdef WITH_OPENSSL #define ARCHIVE_CRYPTO_MD5_OPENSSL 1 #define ARCHIVE_CRYPTO_RMD160_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA1_OPENSSL #define ARCHIVE_CRYPTO_SHA256_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA384_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA512_OPENSSL 1 #else #define ARCHIVE_CRYPTO_MD5_LIBMD 1 #define ARCHIVE_CRYPTO_SHA1_LIBMD 1 #define ARCHIVE_CRYPTO_SHA256_LIBMD 1 #define ARCHIVE_CRYPTO_SHA512_LIBMD 1 #endif Index: stable/11/lib/libarchive/tests/Makefile =================================================================== --- stable/11/lib/libarchive/tests/Makefile (revision 368706) +++ stable/11/lib/libarchive/tests/Makefile (revision 368707) @@ -1,644 +1,645 @@ # $FreeBSD$ PACKAGE= tests _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test BINDIR= ${TESTSDIR} PROGS+= libarchive_test CFLAGS+= -I${.CURDIR} -I${.CURDIR:H} -I${.OBJDIR} CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive -I${_LIBARCHIVEDIR}/libarchive/test CFLAGS+= -I${_LIBARCHIVEDIR}/test_utils CFLAGS+= -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC .PATH: ${_LIBARCHIVEDIR}/libarchive/test TESTS_SRCS= \ test_acl_nfs4.c \ test_acl_pax.c \ test_acl_platform_nfs4.c \ test_acl_platform_posix1e.c \ test_acl_posix1e.c \ test_acl_text.c \ test_archive_api_feature.c \ test_archive_clear_error.c \ test_archive_cmdline.c \ test_archive_digest.c \ test_archive_getdate.c \ test_archive_match_time.c \ test_archive_match_owner.c \ test_archive_match_path.c \ test_archive_pathmatch.c \ test_archive_read_add_passphrase.c \ test_archive_read_close_twice.c \ test_archive_read_close_twice_open_fd.c \ test_archive_read_close_twice_open_filename.c \ test_archive_read_multiple_data_objects.c \ test_archive_read_next_header_empty.c \ test_archive_read_next_header_raw.c \ test_archive_read_open2.c \ test_archive_read_set_filter_option.c \ test_archive_read_set_format_option.c \ test_archive_read_set_option.c \ test_archive_read_set_options.c \ test_archive_read_support.c \ test_archive_set_error.c \ test_archive_string.c \ test_archive_string_conversion.c \ test_archive_write_add_filter_by_name.c \ test_archive_write_set_filter_option.c \ test_archive_write_set_format_by_name.c \ test_archive_write_set_format_filter_by_ext.c \ test_archive_write_set_format_option.c \ test_archive_write_set_option.c \ test_archive_write_set_options.c \ test_archive_write_set_passphrase.c \ test_bad_fd.c \ test_compat_bzip2.c \ test_compat_cpio.c \ test_compat_gtar.c \ test_compat_gzip.c \ test_compat_lz4.c \ test_compat_lzip.c \ test_compat_lzma.c \ test_compat_lzop.c \ test_compat_mac.c \ test_compat_perl_archive_tar.c \ test_compat_plexus_archiver_tar.c \ test_compat_solaris_tar_acl.c \ test_compat_solaris_pax_sparse.c \ test_compat_star_acl.c \ test_compat_tar_hardlink.c \ test_compat_uudecode.c \ test_compat_uudecode_large.c \ test_compat_xz.c \ test_compat_zip.c \ test_compat_zstd.c \ test_empty_write.c \ test_entry.c \ test_entry_strmode.c \ test_extattr_freebsd.c \ test_filter_count.c \ test_fuzz.c \ test_gnutar_filename_encoding.c \ test_link_resolver.c \ test_open_fd.c \ test_open_failure.c \ test_open_file.c \ test_open_filename.c \ test_pax_filename_encoding.c \ test_pax_xattr_header.c \ test_read_data_large.c \ test_read_disk.c \ test_read_disk_directory_traversals.c \ test_read_disk_entry_from_file.c \ test_read_extract.c \ test_read_file_nonexistent.c \ test_read_filter_compress.c \ test_read_filter_grzip.c \ test_read_filter_lrzip.c \ test_read_filter_lzop.c \ test_read_filter_lzop_multiple_parts.c \ test_read_filter_program.c \ test_read_filter_program_signature.c \ test_read_filter_uudecode.c \ test_read_format_7zip.c \ test_read_format_7zip_encryption_data.c \ test_read_format_7zip_encryption_header.c \ test_read_format_7zip_encryption_partially.c \ test_read_format_7zip_malformed.c \ test_read_format_7zip_packinfo_digests.c \ test_read_format_ar.c \ test_read_format_cab.c \ test_read_format_cab_filename.c \ test_read_format_cpio_afio.c \ test_read_format_cpio_bin.c \ test_read_format_cpio_bin_Z.c \ test_read_format_cpio_bin_be.c \ test_read_format_cpio_bin_bz2.c \ test_read_format_cpio_bin_gz.c \ test_read_format_cpio_bin_le.c \ test_read_format_cpio_bin_lzip.c \ test_read_format_cpio_bin_lzma.c \ test_read_format_cpio_bin_xz.c \ test_read_format_cpio_filename.c \ test_read_format_cpio_odc.c \ test_read_format_cpio_svr4_gzip.c \ test_read_format_cpio_svr4c_Z.c \ test_read_format_cpio_svr4_bzip2_rpm.c \ test_read_format_cpio_svr4_gzip_rpm.c \ test_read_format_empty.c \ test_read_format_gtar_filename.c \ test_read_format_gtar_gz.c \ test_read_format_gtar_lzma.c \ test_read_format_gtar_sparse.c \ test_read_format_gtar_sparse_skip_entry.c \ test_read_format_iso_Z.c \ test_read_format_iso_multi_extent.c \ test_read_format_iso_xorriso.c \ test_read_format_isorr_rr_moved.c \ test_read_format_isojoliet_bz2.c \ test_read_format_isojoliet_long.c \ test_read_format_isojoliet_rr.c \ test_read_format_isojoliet_versioned.c \ test_read_format_isorr_bz2.c \ test_read_format_isorr_ce.c \ test_read_format_isorr_new_bz2.c \ test_read_format_isozisofs_bz2.c \ test_read_format_lha.c \ test_read_format_lha_bugfix_0.c \ test_read_format_lha_filename.c \ test_read_format_lha_filename_utf16.c \ test_read_format_mtree.c \ test_read_format_mtree_crash747.c \ test_read_format_pax_bz2.c \ test_read_format_rar.c \ test_read_format_rar5.c \ test_read_format_rar_encryption_data.c \ test_read_format_rar_encryption_header.c \ test_read_format_rar_encryption_partially.c \ test_read_format_rar_invalid1.c \ test_read_format_raw.c \ test_read_format_tar.c \ test_read_format_tar_concatenated.c \ test_read_format_tar_empty_filename.c \ test_read_format_tar_empty_pax.c \ test_read_format_tar_empty_with_gnulabel.c \ test_read_format_tar_filename.c \ test_read_format_tbz.c \ test_read_format_tgz.c \ test_read_format_tlz.c \ test_read_format_txz.c \ test_read_format_tz.c \ test_read_format_ustar_filename.c \ test_read_format_warc.c \ test_read_format_xar.c \ test_read_format_zip.c \ test_read_format_zip_7075_utf8_paths.c \ test_read_format_zip_comment_stored.c \ test_read_format_zip_encryption_data.c \ test_read_format_zip_encryption_header.c \ test_read_format_zip_encryption_partially.c \ test_read_format_zip_extra_padding.c \ test_read_format_zip_filename.c \ test_read_format_zip_high_compression.c \ test_read_format_zip_jar.c \ test_read_format_zip_mac_metadata.c \ test_read_format_zip_malformed.c \ test_read_format_zip_msdos.c \ test_read_format_zip_nested.c \ test_read_format_zip_nofiletype.c \ test_read_format_zip_padded.c \ test_read_format_zip_sfx.c \ test_read_format_zip_traditional_encryption_data.c \ test_read_format_zip_winzip_aes.c \ test_read_format_zip_winzip_aes_large.c \ test_read_format_zip_with_invalid_traditional_eocd.c \ test_read_format_zip_zip64.c \ test_read_large.c \ test_read_pax_xattr_rht_security_selinux.c \ test_read_pax_xattr_schily.c \ test_read_pax_truncated.c \ test_read_position.c \ test_read_set_format.c \ test_read_too_many_filters.c \ test_read_truncated.c \ test_read_truncated_filter.c \ test_sparse_basic.c \ test_tar_filenames.c \ test_tar_large.c \ test_warn_missing_hardlink_target.c \ test_ustar_filenames.c \ test_ustar_filename_encoding.c \ test_write_disk.c \ test_write_disk_appledouble.c \ test_write_disk_failures.c \ test_write_disk_hardlink.c \ test_write_disk_hfs_compression.c \ test_write_disk_lookup.c \ test_write_disk_mac_metadata.c \ test_write_disk_no_hfs_compression.c \ test_write_disk_perms.c \ test_write_disk_secure.c \ test_write_disk_secure744.c \ test_write_disk_secure745.c \ test_write_disk_secure746.c \ test_write_disk_sparse.c \ test_write_disk_symlink.c \ test_write_disk_times.c \ test_write_filter_b64encode.c \ test_write_filter_bzip2.c \ test_write_filter_compress.c \ test_write_filter_gzip.c \ test_write_filter_gzip_timestamp.c \ test_write_filter_lrzip.c \ test_write_filter_lz4.c \ test_write_filter_lzip.c \ test_write_filter_lzma.c \ test_write_filter_lzop.c \ test_write_filter_program.c \ test_write_filter_uuencode.c \ test_write_filter_xz.c \ test_write_filter_zstd.c \ test_write_format_7zip.c \ test_write_format_7zip_empty.c \ test_write_format_7zip_large.c \ test_write_format_ar.c \ test_write_format_cpio.c \ test_write_format_cpio_empty.c \ test_write_format_cpio_newc.c \ test_write_format_cpio_odc.c \ test_write_format_gnutar.c \ test_write_format_gnutar_filenames.c \ test_write_format_iso9660.c \ test_write_format_iso9660_boot.c \ test_write_format_iso9660_empty.c \ test_write_format_iso9660_filename.c \ test_write_format_iso9660_zisofs.c \ test_write_format_mtree.c \ test_write_format_mtree_absolute_path.c \ test_write_format_mtree_classic.c \ test_write_format_mtree_classic_indent.c \ test_write_format_mtree_fflags.c \ test_write_format_mtree_no_separator.c \ test_write_format_mtree_quoted_filename.c \ test_write_format_pax.c \ test_write_format_raw.c \ test_write_format_raw_b64.c \ test_write_format_shar_empty.c \ test_write_format_tar.c \ test_write_format_tar_empty.c \ test_write_format_tar_sparse.c \ test_write_format_tar_ustar.c \ test_write_format_tar_v7tar.c \ test_write_format_warc.c \ test_write_format_warc_empty.c \ test_write_format_xar.c \ test_write_format_xar_empty.c \ test_write_format_zip.c \ test_write_format_zip_compression_store.c \ test_write_format_zip_empty.c \ test_write_format_zip_empty_zip64.c \ test_write_format_zip_file.c \ test_write_format_zip_file_zip64.c \ test_write_format_zip_large.c \ test_write_format_zip_zip64.c \ test_write_open_memory.c \ test_write_read_format_zip.c \ test_xattr_platform.c \ test_zip_filename_encoding.c # Deterministic failures: # Crashes with SIGBUS BROKEN_TESTS+= test_archive_rmd160 # Fails with `libarchive/test/test_archive_crypto.c:121: md != actualmd` BROKEN_TESTS+= test_archive_sha384 # Fails with `test_read_disk_directory_traversals.c:1094: File at has atime 886622, 1443306049 seconds ago` BROKEN_TESTS+= test_read_disk_directory_traversals # Non-deterministic failures: # (Times out?) [and] crashes BROKEN_TESTS+= test_fuzz_rar # Build the test program. SRCS.libarchive_test= \ ${TESTS_SRCS} \ read_open_memory.c \ list.h LIBADD.libarchive_test= archive .PATH: ${_LIBARCHIVEDIR}/test_utils SRCS.libarchive_test+= test_main.c \ test_utils.c # list.h is just a list of all tests, as indicated by DEFINE_TEST macro lines list.h: ${TESTS_SRCS} Makefile @(cd ${_LIBARCHIVEDIR}/libarchive/test && \ grep -E -h ^DEFINE_TEST ${.ALLSRC:N*Makefile} | \ egrep -v '${BROKEN_TESTS:tW:C/ /|/g}') > ${.TARGET}.tmp @mv ${.TARGET}.tmp ${.TARGET} CLEANTESTS+= list.h list.h.tmp ${PACKAGE}FILES+= README ${PACKAGE}FILES+= test_acl_pax_posix1e.tar.uu ${PACKAGE}FILES+= test_acl_pax_nfs4.tar.uu ${PACKAGE}FILES+= test_archive_string_conversion.txt.Z.uu ${PACKAGE}FILES+= test_compat_bzip2_1.tbz.uu ${PACKAGE}FILES+= test_compat_bzip2_2.tbz.uu ${PACKAGE}FILES+= test_compat_cpio_1.cpio.uu ${PACKAGE}FILES+= test_compat_gtar_1.tar.uu ${PACKAGE}FILES+= test_compat_gtar_2.tar.uu ${PACKAGE}FILES+= test_compat_gzip_1.tgz.uu ${PACKAGE}FILES+= test_compat_gzip_2.tgz.uu ${PACKAGE}FILES+= test_compat_lz4_1.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_2.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_3.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B4.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B4BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B4BDBX.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B5.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B5BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B6.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B6BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B7.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B7BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lzip_1.tlz.uu ${PACKAGE}FILES+= test_compat_lzip_2.tlz.uu ${PACKAGE}FILES+= test_compat_lzma_1.tlz.uu ${PACKAGE}FILES+= test_compat_lzma_2.tlz.uu ${PACKAGE}FILES+= test_compat_lzma_3.tlz.uu ${PACKAGE}FILES+= test_compat_lzop_1.tar.lzo.uu ${PACKAGE}FILES+= test_compat_lzop_2.tar.lzo.uu ${PACKAGE}FILES+= test_compat_lzop_3.tar.lzo.uu ${PACKAGE}FILES+= test_compat_mac-1.tar.Z.uu ${PACKAGE}FILES+= test_compat_mac-2.tar.Z.uu ${PACKAGE}FILES+= test_compat_perl_archive_tar.tar.uu ${PACKAGE}FILES+= test_compat_plexus_archiver_tar.tar.uu ${PACKAGE}FILES+= test_compat_solaris_pax_sparse_1.pax.Z.uu ${PACKAGE}FILES+= test_compat_solaris_pax_sparse_2.pax.Z.uu ${PACKAGE}FILES+= test_compat_solaris_tar_acl.tar.uu ${PACKAGE}FILES+= test_compat_star_acl_nfs4.tar.uu ${PACKAGE}FILES+= test_compat_star_acl_posix1e.tar.uu ${PACKAGE}FILES+= test_compat_tar_hardlink_1.tar.uu ${PACKAGE}FILES+= test_compat_uudecode_large.tar.Z.uu ${PACKAGE}FILES+= test_compat_xz_1.txz.uu ${PACKAGE}FILES+= test_compat_zip_1.zip.uu ${PACKAGE}FILES+= test_compat_zip_2.zip.uu ${PACKAGE}FILES+= test_compat_zip_3.zip.uu ${PACKAGE}FILES+= test_compat_zip_4.zip.uu ${PACKAGE}FILES+= test_compat_zip_5.zip.uu ${PACKAGE}FILES+= test_compat_zip_6.zip.uu ${PACKAGE}FILES+= test_compat_zip_7.xps.uu ${PACKAGE}FILES+= test_compat_zip_8.zip.uu ${PACKAGE}FILES+= test_compat_zstd_1.tar.zst.uu ${PACKAGE}FILES+= test_compat_zstd_2.tar.zst.uu ${PACKAGE}FILES+= test_fuzz.cab.uu ${PACKAGE}FILES+= test_fuzz.lzh.uu ${PACKAGE}FILES+= test_fuzz_1.iso.Z.uu ${PACKAGE}FILES+= test_pax_filename_encoding.tar.uu ${PACKAGE}FILES+= test_pax_xattr_header_all.tar.uu ${PACKAGE}FILES+= test_pax_xattr_header_libarchive.tar.uu ${PACKAGE}FILES+= test_pax_xattr_header_schily.tar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part1.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part2.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part3.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part4.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part5.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part6.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_single_file.part1.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_single_file.part2.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_single_file.part3.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part01.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part02.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part03.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part04.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part05.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part06.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part07.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part08.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part09.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part10.rar.uu ${PACKAGE}FILES+= test_read_filter_grzip.tar.grz.uu ${PACKAGE}FILES+= test_read_filter_lrzip.tar.lrz.uu ${PACKAGE}FILES+= test_read_filter_lzop.tar.lzo.uu ${PACKAGE}FILES+= test_read_filter_lzop_multiple_parts.tar.lzo.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_bzip2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_copy_1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_copy_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_copy_lzma.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_deflate.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma1_1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma1_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma2_1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma2_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_bzip2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_copy.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_deflate.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bzip2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_copy.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_copy_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_deflate.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_delta_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_delta_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_delta4_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_delta4_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_empty_archive.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_empty_file.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_encryption.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_encryption_header.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_encryption_partially.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma1_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma1_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_malformed.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_malformed2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_packinfo_digests.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_ppmd.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_symbolic_name.7z.uu ${PACKAGE}FILES+= test_read_format_ar.ar.uu ${PACKAGE}FILES+= test_read_format_cab_1.cab.uu ${PACKAGE}FILES+= test_read_format_cab_2.cab.uu ${PACKAGE}FILES+= test_read_format_cab_3.cab.uu ${PACKAGE}FILES+= test_read_format_cab_filename_cp932.cab.uu ${PACKAGE}FILES+= test_read_format_cpio_bin_be.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_bin_le.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_cp866.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_eucjp.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_koi8r.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_utf8_jp.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_utf8_ru.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_svr4_bzip2_rpm.rpm.uu ${PACKAGE}FILES+= test_read_format_cpio_svr4_gzip_rpm.rpm.uu ${PACKAGE}FILES+= test_read_format_gtar_filename_cp866.tar.Z.uu ${PACKAGE}FILES+= test_read_format_gtar_filename_eucjp.tar.Z.uu ${PACKAGE}FILES+= test_read_format_gtar_filename_koi8r.tar.Z.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_13.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix00.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix01.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix10.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_skip_entry.tar.Z.uu ${PACKAGE}FILES+= test_read_format_iso.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_2.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet_by_nero.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet_long.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet_rockridge.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_multi_extent.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge_ce.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge_new.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge_rr_moved.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_xorriso.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_zisofs.iso.Z.uu ${PACKAGE}FILES+= test_read_format_lha_bugfix_0.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_filename_cp932.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_filename_utf16.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header0.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header1.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header2.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header3.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_lh0.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_lh6.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_lh7.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_withjunk.lzh.uu ${PACKAGE}FILES+= test_read_format_mtree.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_crash747.mtree.bz2.uu ${PACKAGE}FILES+= test_read_format_mtree_nomagic.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_nomagic2.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_nomagic3.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_noprint.mtree.uu ${PACKAGE}FILES+= test_read_format_rar.rar.uu ${PACKAGE}FILES+= test_read_format_rar_binary_data.rar.uu ${PACKAGE}FILES+= test_read_format_rar_compress_best.rar.uu ${PACKAGE}FILES+= test_read_format_rar_compress_normal.rar.uu ${PACKAGE}FILES+= test_read_format_rar_encryption_data.rar.uu ${PACKAGE}FILES+= test_read_format_rar_encryption_header.rar.uu ${PACKAGE}FILES+= test_read_format_rar_encryption_partially.rar.uu ${PACKAGE}FILES+= test_read_format_rar_invalid1.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multi_lzss_blocks.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0001.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0002.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0003.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0004.rar.uu ${PACKAGE}FILES+= test_read_format_rar_noeof.rar.uu ${PACKAGE}FILES+= test_read_format_rar_ppmd_lzss_conversion.rar.uu ${PACKAGE}FILES+= test_read_format_rar_ppmd_use_after_free.rar.uu ${PACKAGE}FILES+= test_read_format_rar_ppmd_use_after_free2.rar.uu ${PACKAGE}FILES+= test_read_format_rar_sfx.exe.uu ${PACKAGE}FILES+= test_read_format_rar_subblock.rar.uu ${PACKAGE}FILES+= test_read_format_rar_unicode.rar.uu ${PACKAGE}FILES+= test_read_format_rar_windows.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_arm.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_arm_filter_on_window_boundary.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_blake2.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_block_size_is_too_small.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_compressed.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_different_solid_window_size.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_different_window_size.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_different_winsize_on_merge.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_distance_overflow.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_extra_field_version.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_fileattr.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_hardlink.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_invalid_dict_reference.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_leftshift1.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_leftshift2.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part01.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part02.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part03.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part04.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part05.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part06.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part07.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part08.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part01.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part02.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part03.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part04.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiple_files.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiple_files_solid.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_nonempty_dir_stream.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_owner.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_readtables_overflow.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_solid.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_stored.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_stored_manyfiles.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_symlink.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_truncated_huff.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_win32.rar.uu ${PACKAGE}FILES+= test_read_format_raw.bufr.uu ${PACKAGE}FILES+= test_read_format_raw.data.Z.uu ${PACKAGE}FILES+= test_read_format_raw.data.gz.uu ${PACKAGE}FILES+= test_read_format_raw.data.uu ${PACKAGE}FILES+= test_read_format_tar_concatenated.tar.uu ${PACKAGE}FILES+= test_read_format_tar_empty_filename.tar.uu ${PACKAGE}FILES+= test_read_format_tar_empty_with_gnulabel.tar.uu ${PACKAGE}FILES+= test_read_format_tar_empty_pax.tar.Z.uu ${PACKAGE}FILES+= test_read_format_tar_filename_koi8r.tar.Z.uu ${PACKAGE}FILES+= test_read_format_ustar_filename_cp866.tar.Z.uu ${PACKAGE}FILES+= test_read_format_ustar_filename_eucjp.tar.Z.uu ${PACKAGE}FILES+= test_read_format_ustar_filename_koi8r.tar.Z.uu ${PACKAGE}FILES+= test_read_format_warc.warc.uu ${PACKAGE}FILES+= test_read_format_zip.zip.uu ${PACKAGE}FILES+= test_read_format_zip_7075_utf8_paths.zip.uu +${PACKAGE}FILES+= test_read_format_zip_7z_lzma.zip.uu ${PACKAGE}FILES+= test_read_format_zip_bz2_hang.zip.uu ${PACKAGE}FILES+= test_read_format_zip_bzip2.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_bzip2_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_comment_stored_1.zip.uu ${PACKAGE}FILES+= test_read_format_zip_comment_stored_2.zip.uu ${PACKAGE}FILES+= test_read_format_zip_encryption_data.zip.uu ${PACKAGE}FILES+= test_read_format_zip_encryption_header.zip.uu ${PACKAGE}FILES+= test_read_format_zip_encryption_partially.zip.uu ${PACKAGE}FILES+= test_read_format_zip_extra_padding.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_cp866.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_cp932.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_koi8r.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_utf8_jp.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_utf8_ru.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_utf8_ru2.zip.uu ${PACKAGE}FILES+= test_read_format_zip_high_compression.zip.uu ${PACKAGE}FILES+= test_read_format_zip_jar.jar.uu ${PACKAGE}FILES+= test_read_format_zip_length_at_end.zip.uu ${PACKAGE}FILES+= test_read_format_zip_lzma_alone_leak.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma_stream_end.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_mac_metadata.zip.uu ${PACKAGE}FILES+= test_read_format_zip_malformed1.zip.uu ${PACKAGE}FILES+= test_read_format_zip_msdos.zip.uu ${PACKAGE}FILES+= test_read_format_zip_nested.zip.uu ${PACKAGE}FILES+= test_read_format_zip_nofiletype.zip.uu ${PACKAGE}FILES+= test_read_format_zip_padded1.zip.uu ${PACKAGE}FILES+= test_read_format_zip_padded2.zip.uu ${PACKAGE}FILES+= test_read_format_zip_padded3.zip.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8_crash_1.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8_crash_2.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_sfx.uu ${PACKAGE}FILES+= test_read_format_zip_symlink.zip.uu ${PACKAGE}FILES+= test_read_format_zip_traditional_encryption_data.zip.uu ${PACKAGE}FILES+= test_read_format_zip_ux.zip.uu ${PACKAGE}FILES+= test_read_format_zip_with_invalid_traditional_eocd.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes128.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes256.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes256_large.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes256_stored.zip.uu ${PACKAGE}FILES+= test_read_format_zip_xz_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_zip64a.zip.uu ${PACKAGE}FILES+= test_read_format_zip_zip64b.zip.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_aa.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ab.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ac.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ad.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ae.uu ${PACKAGE}FILES+= test_read_pax_xattr_rht_security_selinux.tar.uu ${PACKAGE}FILES+= test_read_pax_xattr_schily.tar.uu ${PACKAGE}FILES+= test_read_splitted_rar_aa.uu ${PACKAGE}FILES+= test_read_splitted_rar_ab.uu ${PACKAGE}FILES+= test_read_splitted_rar_ac.uu ${PACKAGE}FILES+= test_read_splitted_rar_ad.uu ${PACKAGE}FILES+= test_read_too_many_filters.gz.uu ${PACKAGE}FILES+= test_splitted_rar_seek_support_aa.uu ${PACKAGE}FILES+= test_splitted_rar_seek_support_ab.uu ${PACKAGE}FILES+= test_splitted_rar_seek_support_ac.uu ${PACKAGE}FILES+= test_write_disk_appledouble.cpio.gz.uu ${PACKAGE}FILES+= test_write_disk_hfs_compression.tgz.uu ${PACKAGE}FILES+= test_write_disk_mac_metadata.tar.gz.uu ${PACKAGE}FILES+= test_write_disk_no_hfs_compression.tgz.uu .include Index: stable/11/usr.bin/bsdcat/Makefile =================================================================== --- stable/11/usr.bin/bsdcat/Makefile (revision 368706) +++ stable/11/usr.bin/bsdcat/Makefile (revision 368707) @@ -1,31 +1,31 @@ # $FreeBSD$ .include _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive _LIBARCHIVECONFDIR= ${SRCTOP}/lib/libarchive PROG= bsdcat -BSDCAT_VERSION_STRING= 3.4.3 +BSDCAT_VERSION_STRING= 3.5.0 .PATH: ${_LIBARCHIVEDIR}/cat SRCS= bsdcat.c cmdline.c .PATH: ${_LIBARCHIVEDIR}/libarchive_fe SRCS+= err.c CFLAGS+= -DBSDCAT_VERSION_STRING=\"${BSDCAT_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\" CFLAGS+= -I${_LIBARCHIVEDIR}/cat -I${_LIBARCHIVEDIR}/libarchive_fe LIBADD= archive .if ${MK_ICONV} != "no" CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST=const .endif .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/11/usr.bin/cpio/Makefile =================================================================== --- stable/11/usr.bin/cpio/Makefile (revision 368706) +++ stable/11/usr.bin/cpio/Makefile (revision 368707) @@ -1,39 +1,39 @@ # $FreeBSD$ .include _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive _LIBARCHIVECONFDIR= ${SRCTOP}/lib/libarchive PROG= bsdcpio -BSDCPIO_VERSION_STRING= 3.4.3 +BSDCPIO_VERSION_STRING= 3.5.0 .PATH: ${_LIBARCHIVEDIR}/cpio SRCS= cpio.c cmdline.c .PATH: ${_LIBARCHIVEDIR}/libarchive_fe SRCS+= err.c line_reader.c passphrase.c CFLAGS+= -DBSDCPIO_VERSION_STRING=\"${BSDCPIO_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\" CFLAGS+= -I${_LIBARCHIVEDIR}/cpio -I${_LIBARCHIVEDIR}/libarchive_fe .ifdef RELEASE_CRUNCH # FreeBSD's installer uses cpio in crunched binaries that are # statically linked, cannot use -lcrypto, and are size sensitive. CFLAGS+= -DSMALLER .endif LIBADD= archive .if ${MK_ICONV} != "no" CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST=const .endif SYMLINKS=bsdcpio ${BINDIR}/cpio MLINKS= bsdcpio.1 cpio.1 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/11/usr.bin/tar/Makefile =================================================================== --- stable/11/usr.bin/tar/Makefile (revision 368706) +++ stable/11/usr.bin/tar/Makefile (revision 368707) @@ -1,40 +1,40 @@ # $FreeBSD$ .include _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive PROG= bsdtar -BSDTAR_VERSION_STRING= 3.4.3 +BSDTAR_VERSION_STRING= 3.5.0 .PATH: ${_LIBARCHIVEDIR}/tar SRCS= bsdtar.c \ cmdline.c \ creation_set.c \ read.c \ subst.c \ util.c \ write.c .PATH: ${_LIBARCHIVEDIR}/libarchive_fe SRCS+= err.c \ line_reader.c \ passphrase.c LIBADD= archive .if ${MK_ICONV} != "no" CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST=const .endif CFLAGS+= -DBSDTAR_VERSION_STRING=\"${BSDTAR_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"${SRCTOP}/lib/libarchive/config_freebsd.h\" CFLAGS+= -I${_LIBARCHIVEDIR}/tar -I${_LIBARCHIVEDIR}/libarchive CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive_fe SYMLINKS= bsdtar ${BINDIR}/tar MLINKS= bsdtar.1 tar.1 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/11 =================================================================== --- stable/11 (revision 368706) +++ stable/11 (revision 368707) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r368234,368608