diff --git a/contrib/libarchive/NEWS b/contrib/libarchive/NEWS index d6324487691b..61d1ca47340e 100644 --- a/contrib/libarchive/NEWS +++ b/contrib/libarchive/NEWS @@ -1,749 +1,753 @@ -Wed 07, 2022: libarchive 3.6.2 released +Jul 18, 2023: libarchive 3.7.0 released + +Jul 14, 2023: bsdunzip port from FreeBSD + +Dec 07, 2022: libarchive 3.6.2 released Apr 08, 2022: libarchive 3.6.1 released Feb 09, 2022: libarchive 3.6.0 released Feb 08, 2022: libarchive 3.5.3 released Aug 22, 2021: libarchive 3.5.2 released Dec 26, 2020: libarchive 3.5.1 released 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. diff --git a/contrib/libarchive/README.md b/contrib/libarchive/README.md index 404076237871..727ed49856b6 100644 --- a/contrib/libarchive/README.md +++ b/contrib/libarchive/README.md @@ -1,232 +1,244 @@ # Welcome to libarchive! The libarchive project develops a portable, efficient C library that can read and write streaming archives in a variety of formats. It also includes implementations of the common `tar`, `cpio`, and `zcat` command-line tools that use the libarchive library. ## Questions? Issues? -* http://www.libarchive.org is the home for ongoing +* https://www.libarchive.org is the home for ongoing libarchive development, including documentation, and links to the libarchive mailing lists. * To report an issue, use the issue tracker at https://github.com/libarchive/libarchive/issues * To submit an enhancement to libarchive, please submit a pull request via GitHub: https://github.com/libarchive/libarchive/pulls ## Contents of the Distribution This distribution bundle includes the following major components: * **libarchive**: a library for reading and writing streaming archives * **tar**: the 'bsdtar' program is a full-featured 'tar' implementation built on libarchive * **cpio**: the 'bsdcpio' program is a different interface to essentially the same functionality * **cat**: the 'bsdcat' program is a simple replacement tool for zcat, bzcat, xzcat, and such +* **unzip**: the 'bsdunzip' program is a simple replacement tool for Info-ZIP's unzip * **examples**: Some small example programs that you may find useful. * **examples/minitar**: a compact sample demonstrating use of libarchive. * **contrib**: Various items sent to me by third parties; please contact the authors with any questions. The top-level directory contains the following information files: * **NEWS** - highlights of recent changes * **COPYING** - what you can do with this * **INSTALL** - installation instructions * **README** - this file * **CMakeLists.txt** - input for "cmake" build tool, see INSTALL * **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`). The following files in the top-level directory are used by the 'configure' script: * `Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers * `Makefile.in`, `config.h.in` - templates used by configure script ## Documentation In addition to the informational articles and documentation in the online [libarchive Wiki](https://github.com/libarchive/libarchive/wiki), the distribution also includes a number of manual pages: * bsdtar.1 explains the use of the bsdtar program * bsdcpio.1 explains the use of the bsdcpio program * bsdcat.1 explains the use of the bsdcat program * libarchive.3 gives an overview of the library as a whole * archive_read.3, archive_write.3, archive_write_disk.3, and archive_read_disk.3 provide detailed calling sequences for the read and write APIs * archive_entry.3 details the "struct archive_entry" utility class * archive_internals.3 provides some insight into libarchive's internal structure and operation. * libarchive-formats.5 documents the file formats supported by the library * cpio.5, mtree.5, and tar.5 provide detailed information about these popular archive formats, including hard-to-find details about modern cpio and tar variants. The manual pages above are provided in the 'doc' directory in a number of different formats. You should also read the copious comments in `archive.h` and the source code for the sample programs for more details. Please let us know about any errors or omissions you find. ## Supported Formats Currently, the library automatically detects and reads the following formats: * Old V7 tar archives * POSIX ustar * GNU tar format (including GNU long filenames, long link names, and sparse files) * Solaris 9 extended tar format (including ACLs) * POSIX pax interchange format * POSIX octet-oriented cpio * SVR4 ASCII cpio * Binary cpio (big-endian or little-endian) * PWB binary cpio * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions) * ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives) * ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries) * GNU and BSD 'ar' archives * 'mtree' format - * 7-Zip archives + * 7-Zip archives (including archives that use zstandard compression) * Microsoft CAB format * LHA and LZH archives * RAR and RAR 5.0 archives (with some limitations due to RAR's proprietary status) * XAR archives The library also detects and handles any of the following before evaluating the archive: * uuencoded files * files with RPM wrapper * gzip compression * bzip2 compression * compress/LZW compression * lzma, lzip, and xz compression * lz4 compression * lzop compression * zstandard compression The library can create archives in any of the following formats: * POSIX ustar * POSIX pax interchange format * "restricted" pax format, which will create ustar archives except for entries that require pax extensions (for long filenames, ACLs, etc). * Old GNU tar format * Old V7 tar format * POSIX octet-oriented cpio * SVR4 "newc" cpio * Binary cpio (little-endian) * PWB binary cpio * shar archives * ZIP archives (with uncompressed or "deflate" compressed entries) * GNU and BSD 'ar' archives * 'mtree' format * ISO9660 format * 7-Zip archives * XAR archives When creating archives, the result can be filtered with any of the following: * uuencode * gzip compression * bzip2 compression * compress/LZW compression * lzma, lzip, and xz compression * lz4 compression * lzop compression * zstandard compression ## Notes about the Library Design The following notes address many of the most common questions we are asked about libarchive: * This is a heavily stream-oriented system. That means that it is optimized to read or write the archive in a single pass from beginning to end. For example, this allows libarchive to process archives too large to store on disk by processing them on-the-fly as they are read from or written to a network or tape drive. This also makes libarchive useful for tools that need to produce archives on-the-fly (such as webservers that provide archived contents of a users account). * In-place modification and random access to the contents of an archive are not directly supported. For some formats, this is not an issue: For example, tar.gz archives are not designed for random access. In some other cases, libarchive can re-open an archive and scan it from the beginning quickly enough to provide the needed abilities even without true random access. Of course, some applications do require true random access; those applications should consider alternatives to libarchive. * The library is designed to be extended with new compression and archive formats. The only requirement is that the format be readable or writable as a stream and that each archive entry be independent. There are articles on the libarchive Wiki explaining how to extend libarchive. * On read, compression and format are always detected automatically. * The same API is used for all formats; it should be very easy for software using libarchive to transparently handle any of libarchive's archiving formats. * Libarchive's automatic support for decompression can be used without archiving by explicitly selecting the "raw" and "empty" formats. * I've attempted to minimize static link pollution. If you don't explicitly invoke a particular feature (such as support for a particular compression or format), it won't get pulled in to statically-linked programs. In particular, if you don't explicitly enable a particular compression or decompression support, you won't need to link against the corresponding compression or decompression libraries. This also reduces the size of statically-linked binaries in environments where that matters. * The library is generally _thread safe_ depending on the platform: it does not define any global variables of its own. However, some platforms do not provide fully thread-safe versions of key C library functions. On those platforms, libarchive will use the non-thread-safe functions. Patches to improve this are of great interest to us. +* The function `archive_write_disk_header()` is _not_ thread safe on + POSIX machines and could lead to security issue resulting in world + writeable directories. Thus it must be mutexed by the calling code. + This is due to calling `umask(oldumask = umask(0))`, which sets the + umask for the whole process to 0 for a short time frame. + In case other thread calls the same function in parallel, it might + get interrupted by it and cause the executable to use umask=0 for the + remaining execution. + This will then lead to implicitely created directories to have 777 + permissions without sticky bit. + * In particular, libarchive's modules to read or write a directory tree do use `chdir()` to optimize the directory traversals. This can cause problems for programs that expect to do disk access from multiple threads. Of course, those modules are completely optional and you can use the rest of libarchive without them. * The library is _not_ thread aware, however. It does no locking or thread management of any kind. If you create a libarchive object and need to access it from multiple threads, you will need to provide your own locking. * On read, the library accepts whatever blocks you hand it. Your read callback is free to pass the library a byte at a time or mmap the entire archive and give it to the library at once. On write, the library always produces correctly-blocked output. * The object-style approach allows you to have multiple archive streams open at once. bsdtar uses this in its "@archive" extension. * The archive itself is read/written using callback functions. You can read an archive directly from an in-memory buffer or write it to a socket, if you wish. There are some utility functions to provide easy-to-use "open file," etc, capabilities. * The read/write APIs are designed to allow individual entries to be read or written to any data source: You can create a block of data in memory and add it to a tar archive without first writing a temporary file. You can also read an entry from an archive and write the data directly to a socket. If you want to read/write entries to disk, there are convenience functions to make this especially easy. * Note: The "pax interchange format" is a POSIX standard extended tar format that should be used when the older _ustar_ format is not appropriate. It has many advantages over other tar formats (including the legacy GNU tar format) and is widely supported by current tar implementations. diff --git a/contrib/libarchive/SECURITY.md b/contrib/libarchive/SECURITY.md new file mode 100644 index 000000000000..6ca188b603fe --- /dev/null +++ b/contrib/libarchive/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives us time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +You may submit the report in the following ways: + +- send an email to security@libarchive.de; and/or +- send us a [private vulnerability report](https://github.com/libarchive/libarchive/security/advisories/new) + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +This project is maintained by volunteers on a reasonable-effort basis. As such, we ask +that you give me 90 days to work on a fix before public exposure. diff --git a/contrib/libarchive/cpio/cpio.c b/contrib/libarchive/cpio/cpio.c index 847c92d31449..fbeae4133091 100644 --- a/contrib/libarchive/cpio/cpio.c +++ b/contrib/libarchive/cpio/cpio.c @@ -1,1517 +1,1510 @@ /*- * 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 * 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 "cpio_platform.h" __FBSDID("$FreeBSD$"); #include #include #include #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "cpio.h" #include "err.h" #include "line_reader.h" #include "passphrase.h" /* Fixed size of uname/gname caches. */ #define name_cache_size 101 #ifndef O_BINARY #define O_BINARY 0 #endif struct name_cache { int probes; int hits; size_t size; struct { id_t id; char *name; } cache[name_cache_size]; }; static int extract_data(struct archive *, struct archive *); const char * cpio_i64toa(int64_t); static const char *cpio_rename(const char *name); static int entry_to_archive(struct cpio *, struct archive_entry *); static int file_to_archive(struct cpio *, const char *); static void free_cache(struct name_cache *cache); static void list_item_verbose(struct cpio *, struct archive_entry *); static void long_help(void) __LA_DEAD; static const char *lookup_gname(struct cpio *, gid_t gid); static int lookup_gname_helper(struct cpio *, const char **name, id_t gid); static const char *lookup_uname(struct cpio *, uid_t uid); static int lookup_uname_helper(struct cpio *, const char **name, id_t uid); static void mode_in(struct cpio *) __LA_DEAD; static void mode_list(struct cpio *) __LA_DEAD; static void mode_out(struct cpio *); static void mode_pass(struct cpio *, const char *); static const char *remove_leading_slash(const char *); static int restore_time(struct cpio *, struct archive_entry *, const char *, int fd); static void usage(void) __LA_DEAD; static void version(void) __LA_DEAD; static const char * passphrase_callback(struct archive *, void *); static void passphrase_free(char *); int main(int argc, char *argv[]) { static char buff[16384]; struct cpio _cpio; /* Allocated on stack. */ struct cpio *cpio; const char *errmsg; char *tptr; int uid, gid; int opt, t; cpio = &_cpio; memset(cpio, 0, sizeof(*cpio)); cpio->buff = buff; cpio->buff_size = sizeof(buff); #if defined(HAVE_SIGACTION) && defined(SIGPIPE) { /* Ignore SIGPIPE signals. */ struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); } #endif /* Set lafe_progname before calling lafe_warnc. */ lafe_setprogname(*argv, "bsdcpio"); #if HAVE_SETLOCALE if (setlocale(LC_ALL, "") == NULL) lafe_warnc(0, "Failed to set default locale"); #endif cpio->uid_override = -1; cpio->gid_override = -1; cpio->argv = argv; cpio->argc = argc; cpio->mode = '\0'; cpio->verbose = 0; cpio->compress = '\0'; cpio->extract_flags = ARCHIVE_EXTRACT_NO_AUTODIR; cpio->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; cpio->extract_flags |= ARCHIVE_EXTRACT_PERM; cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; cpio->extract_flags |= ARCHIVE_EXTRACT_ACL; #if !defined(_WIN32) && !defined(__CYGWIN__) if (geteuid() == 0) cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; #endif cpio->bytes_per_block = 512; cpio->filename = NULL; cpio->matching = archive_match_new(); if (cpio->matching == NULL) lafe_errc(1, 0, "Out of memory"); while ((opt = cpio_getopt(cpio)) != -1) { switch (opt) { case '0': /* GNU convention: --null, -0 */ cpio->option_null = 1; break; case '6': /* in/out: assume/create 6th edition (PWB) format */ cpio->option_pwb = 1; break; case '7': /* out: create archive using 7th Edition binary format */ cpio->format = "bin"; break; case 'A': /* NetBSD/OpenBSD */ cpio->option_append = 1; break; case 'a': /* POSIX 1997 */ cpio->option_atime_restore = 1; break; case 'B': /* POSIX 1997 */ cpio->bytes_per_block = 5120; break; case OPTION_B64ENCODE: cpio->add_filter = opt; break; case 'C': /* NetBSD/OpenBSD */ errno = 0; tptr = NULL; t = (int)strtol(cpio->argument, &tptr, 10); if (errno || t <= 0 || *(cpio->argument) == '\0' || tptr == NULL || *tptr != '\0') { lafe_errc(1, 0, "Invalid blocksize: %s", cpio->argument); } cpio->bytes_per_block = t; break; case 'c': /* POSIX 1997 */ cpio->format = "odc"; break; case 'd': /* POSIX 1997 */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR; break; case 'E': /* NetBSD/OpenBSD */ if (archive_match_include_pattern_from_file( cpio->matching, cpio->argument, cpio->option_null) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); break; case 'F': /* NetBSD/OpenBSD/GNU cpio */ cpio->filename = cpio->argument; break; case 'f': /* POSIX 1997 */ if (archive_match_exclude_pattern(cpio->matching, cpio->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); break; case OPTION_GRZIP: cpio->compress = opt; break; case 'H': /* GNU cpio (also --format) */ cpio->format = cpio->argument; break; case 'h': long_help(); break; case 'I': /* NetBSD/OpenBSD */ cpio->filename = cpio->argument; break; case 'i': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -i and -%c", cpio->mode); cpio->mode = opt; break; case 'J': /* GNU tar, others */ cpio->compress = opt; break; case 'j': /* GNU tar, others */ cpio->compress = opt; break; case OPTION_INSECURE: cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; break; case 'L': /* GNU cpio */ cpio->option_follow_links = 1; break; case 'l': /* POSIX 1997 */ cpio->option_link = 1; break; case OPTION_LRZIP: case OPTION_LZ4: case OPTION_LZMA: /* GNU tar, others */ case OPTION_LZOP: /* GNU tar, others */ case OPTION_ZSTD: cpio->compress = opt; break; case 'm': /* POSIX 1997 */ cpio->extract_flags |= ARCHIVE_EXTRACT_TIME; break; case 'n': /* GNU cpio */ cpio->option_numeric_uid_gid = 1; break; case OPTION_NO_PRESERVE_OWNER: /* GNU cpio */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; break; case 'O': /* GNU cpio */ cpio->filename = cpio->argument; break; case 'o': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -o and -%c", cpio->mode); cpio->mode = opt; break; case 'p': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -p and -%c", cpio->mode); cpio->mode = opt; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; break; case OPTION_PASSPHRASE: cpio->passphrase = cpio->argument; break; case OPTION_PRESERVE_OWNER: cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; break; case OPTION_QUIET: /* GNU cpio */ cpio->quiet = 1; break; case 'R': /* GNU cpio, also --owner */ /* TODO: owner_parse should return uname/gname * also; use that to set [ug]name_override. */ errmsg = owner_parse(cpio->argument, &uid, &gid); if (errmsg) { lafe_warnc(-1, "%s", errmsg); usage(); } if (uid != -1) { cpio->uid_override = uid; cpio->uname_override = NULL; } if (gid != -1) { cpio->gid_override = gid; cpio->gname_override = NULL; } break; case 'r': /* POSIX 1997 */ cpio->option_rename = 1; break; case 't': /* POSIX 1997 */ cpio->option_list = 1; break; case 'u': /* POSIX 1997 */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; break; case OPTION_UUENCODE: cpio->add_filter = opt; break; case 'v': /* POSIX 1997 */ cpio->verbose++; break; case 'V': /* GNU cpio */ cpio->dot++; break; case OPTION_VERSION: /* GNU convention */ version(); break; #if 0 /* * cpio_getopt() handles -W specially, so it's not * available here. */ case 'W': /* Obscure, but useful GNU convention. */ break; #endif case 'y': /* tar convention */ cpio->compress = opt; break; case 'Z': /* tar convention */ cpio->compress = opt; break; case 'z': /* tar convention */ cpio->compress = opt; break; default: usage(); } } /* * Sanity-check args, error out on nonsensical combinations. */ /* -t implies -i if no mode was specified. */ if (cpio->option_list && cpio->mode == '\0') cpio->mode = 'i'; /* -t requires -i */ if (cpio->option_list && cpio->mode != 'i') lafe_errc(1, 0, "Option -t requires -i"); /* -n requires -it */ if (cpio->option_numeric_uid_gid && !cpio->option_list) lafe_errc(1, 0, "Option -n requires -it"); /* Can only specify format when writing */ if (cpio->format != NULL && cpio->mode != 'o') lafe_errc(1, 0, "Option --format requires -o"); /* -l requires -p */ if (cpio->option_link && cpio->mode != 'p') lafe_errc(1, 0, "Option -l requires -p"); /* -v overrides -V */ if (cpio->dot && cpio->verbose) cpio->dot = 0; /* TODO: Flag other nonsensical combinations. */ switch (cpio->mode) { case 'o': if (cpio->format == NULL) { if (cpio->option_pwb) cpio->format = "pwb"; else cpio->format = "cpio"; } mode_out(cpio); break; case 'i': while (*cpio->argv != NULL) { if (archive_match_include_pattern(cpio->matching, *cpio->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); --cpio->argc; ++cpio->argv; } if (cpio->option_list) mode_list(cpio); else mode_in(cpio); break; case 'p': if (*cpio->argv == NULL || **cpio->argv == '\0') lafe_errc(1, 0, "-p mode requires a target directory"); mode_pass(cpio, *cpio->argv); break; default: lafe_errc(1, 0, "Must specify at least one of -i, -o, or -p"); } archive_match_free(cpio->matching); free_cache(cpio->gname_cache); free_cache(cpio->uname_cache); + archive_read_close(cpio->archive_read_disk); + archive_read_free(cpio->archive_read_disk); free(cpio->destdir); passphrase_free(cpio->ppbuff); return (cpio->return_value); } static void usage(void) { const char *p; p = lafe_getprogname(); fprintf(stderr, "Brief Usage:\n"); fprintf(stderr, " List: %s -it < archive\n", p); fprintf(stderr, " Extract: %s -i < archive\n", p); fprintf(stderr, " Create: %s -o < filenames > archive\n", p); fprintf(stderr, " Help: %s --help\n", p); exit(1); } static const char *long_help_msg = "First option must be a mode specifier:\n" " -i Input -o Output -p Pass\n" "Common Options:\n" " -v Verbose filenames -V one dot per file\n" "Create: %p -o [options] < [list of files] > [archive]\n" " -J,-y,-z,--lzma Compress archive with xz/bzip2/gzip/lzma\n" " --format {pwb|bin|odc|newc|ustar} Select archive format\n" "List: %p -it < [archive]\n" "Extract: %p -i [options] < [archive]\n"; /* * Note that the word 'bsdcpio' will always appear in the first line * of output. * * In particular, /bin/sh scripts that need to test for the presence * of bsdcpio can use the following template: * * if (cpio --help 2>&1 | grep bsdcpio >/dev/null 2>&1 ) then \ * echo bsdcpio; else echo not bsdcpio; fi */ static void long_help(void) { const char *prog; const char *p; prog = lafe_getprogname(); fflush(stderr); p = (strcmp(prog,"bsdcpio") != 0) ? "(bsdcpio)" : ""; printf("%s%s: manipulate archive files\n", prog, p); for (p = long_help_msg; *p != '\0'; p++) { if (*p == '%') { if (p[1] == 'p') { fputs(prog, stdout); p++; } else putchar('%'); } else putchar(*p); } version(); } static void version(void) { fprintf(stdout,"bsdcpio %s - %s \n", BSDCPIO_VERSION_STRING, archive_version_details()); exit(0); } static void mode_out(struct cpio *cpio) { struct archive_entry *entry, *spare; struct lafe_line_reader *lr; const char *p; int r; if (cpio->option_append) lafe_errc(1, 0, "Append mode not yet supported."); cpio->archive_read_disk = archive_read_disk_new(); if (cpio->archive_read_disk == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); if (cpio->option_follow_links) archive_read_disk_set_symlink_logical(cpio->archive_read_disk); else archive_read_disk_set_symlink_physical(cpio->archive_read_disk); archive_read_disk_set_standard_lookup(cpio->archive_read_disk); cpio->archive = archive_write_new(); if (cpio->archive == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); switch (cpio->compress) { case OPTION_GRZIP: r = archive_write_add_filter_grzip(cpio->archive); break; case 'J': r = archive_write_add_filter_xz(cpio->archive); break; case OPTION_LRZIP: r = archive_write_add_filter_lrzip(cpio->archive); break; case OPTION_LZ4: r = archive_write_add_filter_lz4(cpio->archive); break; case OPTION_LZMA: r = archive_write_add_filter_lzma(cpio->archive); break; case OPTION_LZOP: r = archive_write_add_filter_lzop(cpio->archive); break; case OPTION_ZSTD: r = archive_write_add_filter_zstd(cpio->archive); break; case 'j': case 'y': r = archive_write_add_filter_bzip2(cpio->archive); break; case 'z': r = archive_write_add_filter_gzip(cpio->archive); break; case 'Z': r = archive_write_add_filter_compress(cpio->archive); break; default: r = archive_write_add_filter_none(cpio->archive); break; } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested compression not available"); switch (cpio->add_filter) { case 0: r = ARCHIVE_OK; break; case OPTION_B64ENCODE: r = archive_write_add_filter_b64encode(cpio->archive); break; case OPTION_UUENCODE: r = archive_write_add_filter_uuencode(cpio->archive); break; } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested filter not available"); r = archive_write_set_format_by_name(cpio->archive, cpio->format); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); archive_write_set_bytes_per_block(cpio->archive, cpio->bytes_per_block); cpio->linkresolver = archive_entry_linkresolver_new(); archive_entry_linkresolver_set_strategy(cpio->linkresolver, archive_format(cpio->archive)); if (cpio->passphrase != NULL) r = archive_write_set_passphrase(cpio->archive, cpio->passphrase); else r = archive_write_set_passphrase_callback(cpio->archive, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); /* * The main loop: Copy each file into the output archive. */ r = archive_write_open_filename(cpio->archive, cpio->filename); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); lr = lafe_line_reader("-", cpio->option_null); while ((p = lafe_line_reader_next(lr)) != NULL) file_to_archive(cpio, p); lafe_line_reader_free(lr); /* * The hardlink detection may have queued up a couple of entries * that can now be flushed. */ entry = NULL; archive_entry_linkify(cpio->linkresolver, &entry, &spare); while (entry != NULL) { entry_to_archive(cpio, entry); archive_entry_free(entry); entry = NULL; archive_entry_linkify(cpio->linkresolver, &entry, &spare); } r = archive_write_close(cpio->archive); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(cpio->archive, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_free(cpio->archive); archive_entry_linkresolver_free(cpio->linkresolver); } static const char * remove_leading_slash(const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; } do { rp = p; /* Remove leading drive letter from archives created * on Windows. */ if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; } /* Remove leading "/../", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" * for next pass. */ } else p += 1; /* Remove "/". */ } } while (rp != p); return (p); } /* * This is used by both out mode (to copy objects from disk into * an archive) and pass mode (to copy objects from disk to * an archive_write_disk "archive"). */ static int file_to_archive(struct cpio *cpio, const char *srcpath) { const char *destpath; struct archive_entry *entry, *spare; size_t len; int r; /* * Create an archive_entry describing the source file. * */ entry = archive_entry_new(); if (entry == NULL) lafe_errc(1, 0, "Couldn't allocate entry"); archive_entry_copy_sourcepath(entry, srcpath); r = archive_read_disk_entry_from_file(cpio->archive_read_disk, entry, -1, NULL); if (r < ARCHIVE_FAILED) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive_read_disk)); if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(cpio->archive_read_disk)); if (r <= ARCHIVE_FAILED) { archive_entry_free(entry); cpio->return_value = 1; return (r); } if (cpio->uid_override >= 0) { archive_entry_set_uid(entry, cpio->uid_override); archive_entry_set_uname(entry, cpio->uname_override); } if (cpio->gid_override >= 0) { archive_entry_set_gid(entry, cpio->gid_override); archive_entry_set_gname(entry, cpio->gname_override); } /* * Generate a destination path for this entry. * "destination path" is the name to which it will be copied in * pass mode or the name that will go into the archive in * output mode. */ destpath = srcpath; if (cpio->destdir) { len = cpio->destdir_len + strlen(srcpath) + 8; if (len >= cpio->pass_destpath_alloc) { while (len >= cpio->pass_destpath_alloc) { cpio->pass_destpath_alloc += 512; cpio->pass_destpath_alloc *= 2; } free(cpio->pass_destpath); cpio->pass_destpath = malloc(cpio->pass_destpath_alloc); if (cpio->pass_destpath == NULL) lafe_errc(1, ENOMEM, "Can't allocate path buffer"); } strcpy(cpio->pass_destpath, cpio->destdir); strcat(cpio->pass_destpath, remove_leading_slash(srcpath)); destpath = cpio->pass_destpath; } if (cpio->option_rename) destpath = cpio_rename(destpath); if (destpath == NULL) { archive_entry_free(entry); return (0); } archive_entry_copy_pathname(entry, destpath); /* * If we're trying to preserve hardlinks, match them here. */ spare = NULL; if (cpio->linkresolver != NULL && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_linkify(cpio->linkresolver, &entry, &spare); } if (entry != NULL) { r = entry_to_archive(cpio, entry); archive_entry_free(entry); if (spare != NULL) { if (r == 0) r = entry_to_archive(cpio, spare); archive_entry_free(spare); } } return (r); } static int entry_to_archive(struct cpio *cpio, struct archive_entry *entry) { const char *destpath = archive_entry_pathname(entry); const char *srcpath = archive_entry_sourcepath(entry); int fd = -1; ssize_t bytes_read; int r; /* Print out the destination name to the user. */ if (cpio->verbose) fprintf(stderr,"%s", destpath); if (cpio->dot) fprintf(stderr, "."); /* * Option_link only makes sense in pass mode and for * regular files. Also note: if a link operation fails * because of cross-device restrictions, we'll fall back * to copy mode for that entry. * * TODO: Test other cpio implementations to see if they * hard-link anything other than regular files here. */ if (cpio->option_link && archive_entry_filetype(entry) == AE_IFREG) { struct archive_entry *t; /* Save the original entry in case we need it later. */ t = archive_entry_clone(entry); if (t == NULL) lafe_errc(1, ENOMEM, "Can't create link"); /* Note: link(2) doesn't create parent directories, * so we use archive_write_header() instead as a * convenience. */ archive_entry_set_hardlink(t, srcpath); /* This is a straight link that carries no data. */ archive_entry_set_size(t, 0); r = archive_write_header(cpio->archive, t); archive_entry_free(t); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); #ifdef EXDEV if (r != ARCHIVE_OK && archive_errno(cpio->archive) == EXDEV) { /* Cross-device link: Just fall through and use * the original entry to copy the file over. */ lafe_warnc(0, "Copying file instead"); } else #endif return (0); } /* * Make sure we can open the file (if necessary) before * trying to write the header. */ if (archive_entry_filetype(entry) == AE_IFREG) { if (archive_entry_size(entry) > 0) { fd = open(srcpath, O_RDONLY | O_BINARY); if (fd < 0) { lafe_warnc(errno, "%s: could not open file", srcpath); goto cleanup; } } } else { archive_entry_set_size(entry, 0); } r = archive_write_header(cpio->archive, entry); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s: %s", srcpath, archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); if (r >= ARCHIVE_WARN && archive_entry_size(entry) > 0 && fd >= 0) { bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); while (bytes_read > 0) { ssize_t bytes_write; bytes_write = archive_write_data(cpio->archive, cpio->buff, bytes_read); if (bytes_write < 0) lafe_errc(1, archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (bytes_write < bytes_read) { lafe_warnc(0, "Truncated write; file may have " "grown while being archived."); } bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); } } fd = restore_time(cpio, entry, srcpath, fd); cleanup: if (cpio->verbose) fprintf(stderr,"\n"); if (fd >= 0) close(fd); return (0); } static int restore_time(struct cpio *cpio, struct archive_entry *entry, const char *name, int fd) { #ifndef HAVE_UTIMES static int warned = 0; (void)cpio; /* UNUSED */ (void)entry; /* UNUSED */ (void)name; /* UNUSED */ if (!warned) lafe_warnc(0, "Can't restore access times on this platform"); warned = 1; return (fd); #else #if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif if (!cpio->option_atime_restore) return (fd); times[1].tv_sec = archive_entry_mtime(entry); times[1].tv_usec = archive_entry_mtime_nsec(entry) / 1000; times[0].tv_sec = archive_entry_atime(entry); times[0].tv_usec = archive_entry_atime_nsec(entry) / 1000; #if defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (fd >= 0 && futimes(fd, times) == 0) return (fd); #endif /* * Some platform cannot restore access times if the file descriptor * is still opened. */ if (fd >= 0) { close(fd); fd = -1; } #ifdef HAVE_LUTIMES if (lutimes(name, times) != 0) #else if ((AE_IFLNK != archive_entry_filetype(entry)) && utimes(name, times) != 0) #endif lafe_warnc(errno, "Can't update time for %s", name); #endif return (fd); } static void mode_in(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; struct archive *ext; const char *destpath; int r; ext = archive_write_disk_new(); if (ext == NULL) lafe_errc(1, 0, "Couldn't allocate restore object"); r = archive_write_disk_set_options(ext, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (cpio->option_pwb) archive_read_set_options(a, "pwb"); if (cpio->passphrase != NULL) r = archive_read_add_passphrase(a, cpio->passphrase); else r = archive_read_set_passphrase_callback(a, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->option_rename) { destpath = cpio_rename(archive_entry_pathname(entry)); archive_entry_set_pathname(entry, destpath); } else destpath = archive_entry_pathname(entry); if (destpath == NULL) continue; if (cpio->verbose) fprintf(stderr, "%s\n", destpath); if (cpio->dot) fprintf(stderr, "."); if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) archive_entry_set_gid(entry, cpio->gid_override); r = archive_write_header(ext, entry); if (r != ARCHIVE_OK) { fprintf(stderr, "%s: %s\n", archive_entry_pathname(entry), archive_error_string(ext)); } else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) { r = extract_data(a, ext); if (r != ARCHIVE_OK) cpio->return_value = 1; } } r = archive_read_close(a); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); r = archive_write_close(ext); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); archive_write_free(ext); exit(cpio->return_value); } /* * Exits if there's a fatal error. Returns ARCHIVE_OK * if everything is kosher. */ static int extract_data(struct archive *ar, struct archive *aw) { int r; size_t size; const void *block; int64_t offset; for (;;) { r = archive_read_data_block(ar, &block, &size, &offset); if (r == ARCHIVE_EOF) return (ARCHIVE_OK); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(ar), "%s", archive_error_string(ar)); exit(1); } r = (int)archive_write_data_block(aw, block, size, offset); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(aw), "%s", archive_error_string(aw)); return (r); } } } static void mode_list(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; int r; a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (cpio->option_pwb) archive_read_set_options(a, "pwb"); if (cpio->passphrase != NULL) r = archive_read_add_passphrase(a, cpio->passphrase); else r = archive_read_set_passphrase_callback(a, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->verbose) list_item_verbose(cpio, entry); else fprintf(stdout, "%s\n", archive_entry_pathname(entry)); } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); exit(0); } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct cpio *cpio, struct archive_entry *entry) { char size[32]; char date[32]; char uids[16], gids[16]; const char *uname, *gname; FILE *out = stdout; const char *fmt; time_t mtime; static time_t now; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (!now) time(&now); if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); uname = uids; strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ uname = archive_entry_uname(entry); if (uname == NULL) uname = lookup_uname(cpio, (uid_t)archive_entry_uid(entry)); /* Use gname if it's present, else lookup name from gid. */ gname = archive_entry_gname(entry); if (gname == NULL) gname = lookup_gname(cpio, (uid_t)archive_entry_gid(entry)); } /* Print device number or file size. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(size, sizeof(size), "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(size, cpio_i64toa(archive_entry_size(entry))); } /* Format the time using 'ls -l' conventions. */ mtime = archive_entry_mtime(entry); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ if (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%d %b %Y" : "%b %d %Y"; else fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; #else if (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; #endif -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &mtime) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&mtime, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = mtime; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&mtime); #endif strftime(date, sizeof(date), fmt, ltime); fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", archive_entry_strmode(entry), archive_entry_nlink(entry), uname, gname, size, date, archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ fprintf(out, " -> %s", archive_entry_symlink(entry)); fprintf(out, "\n"); } static void mode_pass(struct cpio *cpio, const char *destdir) { struct lafe_line_reader *lr; const char *p; int r; /* Ensure target dir has a trailing '/' to simplify path surgery. */ cpio->destdir_len = strlen(destdir); cpio->destdir = malloc(cpio->destdir_len + 8); memcpy(cpio->destdir, destdir, cpio->destdir_len); if (cpio->destdir_len == 0 || destdir[cpio->destdir_len - 1] != '/') cpio->destdir[cpio->destdir_len++] = '/'; cpio->destdir[cpio->destdir_len] = '\0'; cpio->archive = archive_write_disk_new(); if (cpio->archive == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); cpio->linkresolver = archive_entry_linkresolver_new(); archive_write_disk_set_standard_lookup(cpio->archive); cpio->archive_read_disk = archive_read_disk_new(); if (cpio->archive_read_disk == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); if (cpio->option_follow_links) archive_read_disk_set_symlink_logical(cpio->archive_read_disk); else archive_read_disk_set_symlink_physical(cpio->archive_read_disk); archive_read_disk_set_standard_lookup(cpio->archive_read_disk); lr = lafe_line_reader("-", cpio->option_null); while ((p = lafe_line_reader_next(lr)) != NULL) file_to_archive(cpio, p); lafe_line_reader_free(lr); archive_entry_linkresolver_free(cpio->linkresolver); r = archive_write_close(cpio->archive); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(cpio->archive, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_free(cpio->archive); free(cpio->pass_destpath); } /* * Prompt for a new name for this entry. Returns a pointer to the * new name or NULL if the entry should not be copied. This * implements the semantics defined in POSIX.1-1996, which specifies * that an input of '.' means the name should be unchanged. GNU cpio * treats '.' as a literal new name. */ static const char * cpio_rename(const char *name) { static char buff[1024]; FILE *t; char *p, *ret; #if defined(_WIN32) && !defined(__CYGWIN__) FILE *to; t = fopen("CONIN$", "r"); if (t == NULL) return (name); to = fopen("CONOUT$", "w"); if (to == NULL) { fclose(t); return (name); } fprintf(to, "%s (Enter/./(new name))? ", name); fclose(to); #else t = fopen("/dev/tty", "r+"); if (t == NULL) return (name); fprintf(t, "%s (Enter/./(new name))? ", name); fflush(t); #endif p = fgets(buff, sizeof(buff), t); fclose(t); if (p == NULL) /* End-of-file is a blank line. */ return (NULL); while (*p == ' ' || *p == '\t') ++p; if (*p == '\n' || *p == '\0') /* Empty line. */ return (NULL); if (*p == '.' && p[1] == '\n') /* Single period preserves original name. */ return (name); ret = p; /* Trim the final newline. */ while (*p != '\0' && *p != '\n') ++p; /* Overwrite the final \n with a null character. */ *p = '\0'; return (ret); } static void free_cache(struct name_cache *cache) { size_t i; if (cache != NULL) { for (i = 0; i < cache->size; i++) free(cache->cache[i].name); free(cache); } } /* * Lookup uname/gname from uid/gid, return NULL if no match. */ static const char * lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable, int (*lookup_fn)(struct cpio *, const char **, id_t), id_t id) { char asnum[16]; struct name_cache *cache; const char *name; int slot; if (*name_cache_variable == NULL) { *name_cache_variable = calloc(1, sizeof(struct name_cache)); if (*name_cache_variable == NULL) lafe_errc(1, ENOMEM, "No more memory"); (*name_cache_variable)->size = name_cache_size; } cache = *name_cache_variable; cache->probes++; slot = id % cache->size; if (cache->cache[slot].name != NULL) { if (cache->cache[slot].id == id) { cache->hits++; return (cache->cache[slot].name); } free(cache->cache[slot].name); cache->cache[slot].name = NULL; } if (lookup_fn(cpio, &name, id)) { /* If lookup failed, format it as a number. */ snprintf(asnum, sizeof(asnum), "%u", (unsigned)id); name = asnum; } cache->cache[slot].name = strdup(name); if (cache->cache[slot].name != NULL) { cache->cache[slot].id = id; return (cache->cache[slot].name); } /* * Conveniently, NULL marks an empty slot, so * if the strdup() fails, we've just failed to * cache it. No recovery necessary. */ return (NULL); } static const char * lookup_uname(struct cpio *cpio, uid_t uid) { return (lookup_name(cpio, &cpio->uname_cache, &lookup_uname_helper, (id_t)uid)); } static int lookup_uname_helper(struct cpio *cpio, const char **name, id_t id) { struct passwd *pwent; (void)cpio; /* UNUSED */ errno = 0; pwent = getpwuid((uid_t)id); if (pwent == NULL) { if (errno && errno != ENOENT) lafe_warnc(errno, "getpwuid(%s) failed", cpio_i64toa((int64_t)id)); return 1; } *name = pwent->pw_name; return 0; } static const char * lookup_gname(struct cpio *cpio, gid_t gid) { return (lookup_name(cpio, &cpio->gname_cache, &lookup_gname_helper, (id_t)gid)); } static int lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) { struct group *grent; (void)cpio; /* UNUSED */ errno = 0; grent = getgrgid((gid_t)id); if (grent == NULL) { if (errno && errno != ENOENT) lafe_warnc(errno, "getgrgid(%s) failed", cpio_i64toa((int64_t)id)); return 1; } *name = grent->gr_name; return 0; } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are a big headache. Hence the * following simple utility function. */ const char * cpio_i64toa(int64_t n0) { /* 2^64 =~ 1.8 * 10^19, so 20 decimal digits suffice. * We also need 1 byte for '-' and 1 for '\0'. */ static char buff[22]; int64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); n /= 10; } while (n > 0); if (n0 < 0) *--p = '-'; return p; } #define PPBUFF_SIZE 1024 static const char * passphrase_callback(struct archive *a, void *_client_data) { struct cpio *cpio = (struct cpio *)_client_data; (void)a; /* UNUSED */ if (cpio->ppbuff == NULL) { cpio->ppbuff = malloc(PPBUFF_SIZE); if (cpio->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", cpio->ppbuff, PPBUFF_SIZE); } static void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } diff --git a/contrib/libarchive/cpio/test/test_option_J_upper.c b/contrib/libarchive/cpio/test/test_option_J_upper.c index 1d7d05131b3d..e19d599e1ec7 100644 --- a/contrib/libarchive/cpio/test/test_option_J_upper.c +++ b/contrib/libarchive/cpio/test/test_option_J_upper.c @@ -1,59 +1,60 @@ /*- * 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" __FBSDID("$FreeBSD$"); DEFINE_TEST(test_option_J_upper) { char *p; int r; size_t s; /* Create a file. */ assertMakeFile("f", 0644, "a"); /* Archive it with xz compression. */ r = systemf("echo f | %s -o -J >archive.out 2>archive.err", testprog); p = slurpfile(&s, "archive.err"); p[s] = '\0'; if (r != 0) { if (strstr(p, "compression not available") != NULL) { skipping("This version of bsdcpio was compiled " "without xz support"); + free(p); return; } failure("-J option is broken"); assertEqualInt(r, 0); goto done; } free(p); /* Check that the archive file has an xz signature. */ p = slurpfile(&s, "archive.out"); assert(s > 2); assertEqualMem(p, "\3757zXZ", 5); done: free(p); } diff --git a/contrib/libarchive/cpio/test/test_option_c.c b/contrib/libarchive/cpio/test/test_option_c.c index 013caed56030..dfa62c13b13e 100644 --- a/contrib/libarchive/cpio/test/test_option_c.c +++ b/contrib/libarchive/cpio/test/test_option_c.c @@ -1,229 +1,229 @@ /*- * 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$"); static int is_octal(const char *p, size_t l) { while (l > 0) { if (*p < '0' || *p > '7') return (0); --l; ++p; } return (1); } -static int +static long long int from_octal(const char *p, size_t l) { - int r = 0; + long long int r = 0; while (l > 0) { r *= 8; r += *p - '0'; --l; ++p; } return (r); } #if !defined(_WIN32) || defined(__CYGWIN__) static int nlinks(const char *p) { struct stat st; assertEqualInt(0, stat(p, &st)); return st.st_nlink; } #endif DEFINE_TEST(test_option_c) { FILE *filelist; int r; int uid = 1000; int dev, ino, gid = 1000; time_t t, now; char *p, *e; size_t s; assertUmask(0); /* * Create an assortment of files. * TODO: Extend this to cover more filetypes. */ filelist = fopen("filelist", "w"); /* "file" */ assertMakeFile("file", 0644, "1234567890"); fprintf(filelist, "file\n"); /* "symlink" */ if (canSymlink()) { assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); } /* "dir" */ assertMakeDir("dir", 0775); /* Record some facts about what we just created: */ now = time(NULL); /* They were all created w/in last two seconds. */ fprintf(filelist, "dir\n"); /* Use the cpio program to create an archive. */ fclose(filelist); r = systemf("%s -R 1000:1000 -oc basic.out 2>basic.err", testprog); /* Verify that nothing went to stderr. */ assertTextFileContents("1 block\n", "basic.err"); /* Assert that the program finished. */ failure("%s -oc crashed", testprog); if (!assertEqualInt(r, 0)) return; /* Verify that stdout is a well-formed cpio file in "odc" format. */ p = slurpfile(&s, "basic.out"); assertEqualInt(s, 512); e = p; /* * Some of these assertions could be stronger, but it's * a little tricky because they depend on the local environment. */ /* First entry is "file" */ assert(is_octal(e, 76)); /* Entire header is octal digits. */ assertEqualMem(e + 0, "070707", 6); /* Magic */ assert(is_octal(e + 6, 6)); /* dev */ dev = from_octal(e + 6, 6); assert(is_octal(e + 12, 6)); /* ino */ ino = from_octal(e + 12, 6); #if defined(_WIN32) && !defined(__CYGWIN__) /* Group members bits and others bits do not work. */ assertEqualMem(e + 18, "100666", 6); /* Mode */ #else assertEqualMem(e + 18, "100644", 6); /* Mode */ #endif if (uid < 0) uid = from_octal(e + 24, 6); assertEqualInt(from_octal(e + 24, 6), uid); /* uid */ assert(is_octal(e + 30, 6)); /* gid */ gid = from_octal(e + 30, 6); assertEqualMem(e + 36, "000001", 6); /* nlink */ failure("file entries should not have rdev set (dev field was 0%o)", dev); assertEqualMem(e + 42, "000000", 6); /* rdev */ t = from_octal(e + 48, 11); /* mtime */ assert(t <= now); /* File wasn't created in future. */ assert(t >= now - 2); /* File was created w/in last 2 secs. */ assertEqualMem(e + 59, "000005", 6); /* Name size */ assertEqualMem(e + 65, "00000000012", 11); /* File size */ assertEqualMem(e + 76, "file\0", 5); /* Name contents */ assertEqualMem(e + 81, "1234567890", 10); /* File contents */ e += 91; /* "symlink" pointing to "file" */ if (canSymlink()) { assert(is_octal(e, 76)); /* Entire header is octal digits. */ assertEqualMem(e + 0, "070707", 6); /* Magic */ assertEqualInt(dev, from_octal(e + 6, 6)); /* dev */ assert(ino != from_octal(e + 12, 6)); /* ino */ #if !defined(_WIN32) || defined(__CYGWIN__) /* On Windows, symbolic link and group members bits and * others bits do not work. */ assertEqualMem(e + 18, "120777", 6); /* Mode */ #endif assertEqualInt(from_octal(e + 24, 6), uid); /* uid */ assertEqualInt(gid, from_octal(e + 30, 6)); /* gid */ assertEqualMem(e + 36, "000001", 6); /* nlink */ - failure("file entries should have rdev == 0 (dev was 0%o)", + failure("file entries should have rdev == 0 (dev was 0%llo)", from_octal(e + 6, 6)); assertEqualMem(e + 42, "000000", 6); /* rdev */ t = from_octal(e + 48, 11); /* mtime */ assert(t <= now); /* File wasn't created in future. */ assert(t >= now - 2); /* File was created w/in last 2 secs. */ assertEqualMem(e + 59, "000010", 6); /* Name size */ assertEqualMem(e + 65, "00000000004", 11); /* File size */ assertEqualMem(e + 76, "symlink\0", 8); /* Name contents */ assertEqualMem(e + 84, "file", 4); /* Symlink target. */ e += 88; } /* "dir" */ assert(is_octal(e, 76)); assertEqualMem(e + 0, "070707", 6); /* Magic */ /* Dev should be same as first entry. */ assert(is_octal(e + 6, 6)); /* dev */ assertEqualInt(dev, from_octal(e + 6, 6)); /* Ino must be different from first entry. */ assert(is_octal(e + 12, 6)); /* ino */ assert(ino != from_octal(e + 12, 6)); #if defined(_WIN32) && !defined(__CYGWIN__) /* Group members bits and others bits do not work. */ assertEqualMem(e + 18, "040777", 6); /* Mode */ #else /* Accept 042775 to accommodate systems where sgid bit propagates. */ if (memcmp(e + 18, "042775", 6) != 0) assertEqualMem(e + 18, "040775", 6); /* Mode */ #endif assertEqualInt(uid, from_octal(e + 24, 6)); /* uid */ /* Gid should be same as first entry. */ assert(is_octal(e + 30, 6)); /* gid */ assertEqualInt(gid, from_octal(e + 30, 6)); #if !defined(_WIN32) || defined(__CYGWIN__) assertEqualInt(nlinks("dir"), from_octal(e + 36, 6)); /* Nlink */ #endif t = from_octal(e + 48, 11); /* mtime */ assert(t <= now); /* File wasn't created in future. */ assert(t >= now - 2); /* File was created w/in last 2 secs. */ assertEqualMem(e + 59, "000004", 6); /* Name size */ assertEqualMem(e + 65, "00000000000", 11); /* File size */ assertEqualMem(e + 76, "dir\0", 4); /* name */ e += 80; /* TODO: Verify other types of entries. */ /* Last entry is end-of-archive marker. */ assert(is_octal(e, 76)); assertEqualMem(e + 0, "070707", 6); /* Magic */ assertEqualMem(e + 6, "000000", 6); /* dev */ assertEqualMem(e + 12, "000000", 6); /* ino */ assertEqualMem(e + 18, "000000", 6); /* Mode */ assertEqualMem(e + 24, "000000", 6); /* uid */ assertEqualMem(e + 30, "000000", 6); /* gid */ assertEqualMem(e + 36, "000001", 6); /* Nlink */ assertEqualMem(e + 42, "000000", 6); /* rdev */ assertEqualMem(e + 48, "00000000000", 11); /* mtime */ assertEqualMem(e + 59, "000013", 6); /* Name size */ assertEqualMem(e + 65, "00000000000", 11); /* File size */ assertEqualMem(e + 76, "TRAILER!!!\0", 11); /* Name */ free(p); } diff --git a/contrib/libarchive/cpio/test/test_option_t.c b/contrib/libarchive/cpio/test/test_option_t.c index 0f2dda27cc20..9eef0da571c4 100644 --- a/contrib/libarchive/cpio/test/test_option_t.c +++ b/contrib/libarchive/cpio/test/test_option_t.c @@ -1,124 +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 "test.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_LOCALE_H #include #endif DEFINE_TEST(test_option_t) { char *p; int r; time_t mtime; char date[32]; char date2[32]; struct tm *tmptr; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif /* List reference archive, make sure the TOC is correct. */ extract_reference_file("test_option_t.cpio"); r = systemf("%s -it < test_option_t.cpio >it.out 2>it.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "it.err"); extract_reference_file("test_option_t.stdout"); p = slurpfile(NULL, "test_option_t.stdout"); assertTextFileContents(p, "it.out"); free(p); /* We accept plain "-t" as a synonym for "-it" */ r = systemf("%s -t < test_option_t.cpio >t.out 2>t.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "t.err"); extract_reference_file("test_option_t.stdout"); p = slurpfile(NULL, "test_option_t.stdout"); assertTextFileContents(p, "t.out"); free(p); /* But "-ot" is an error. */ assert(0 != systemf("%s -ot < test_option_t.cpio >ot.out 2>ot.err", testprog)); assertEmptyFile("ot.out"); /* List reference archive verbosely, make sure the TOC is correct. */ r = systemf("%s -itv < test_option_t.cpio >tv.out 2>tv.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "tv.err"); extract_reference_file("test_option_tv.stdout"); /* This doesn't work because the usernames on different systems * are different and cpio now looks up numeric UIDs on * the local system. */ /* assertEqualFile("tv.out", "test_option_tv.stdout"); */ /* List reference archive with numeric IDs, verify TOC is correct. */ r = systemf("%s -itnv < test_option_t.cpio >itnv.out 2>itnv.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "itnv.err"); p = slurpfile(NULL, "itnv.out"); /* Since -n uses numeric UID/GID, this part should be the * same on every system. */ assertEqualMem(p, "-rw-r--r-- 1 1000 1000 0 ",42); /* Date varies depending on local timezone and locale. */ mtime = 1; #ifdef HAVE_LOCALE_H setlocale(LC_ALL, ""); #endif -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tmptr = localtime_s(&tmbuf, &mtime) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tmptr = localtime_r(&mtime, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = mtime; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tmptr = NULL; - else - tmptr = &tmbuf; #else tmptr = localtime(&mtime); #endif #if defined(_WIN32) && !defined(__CYGWIN__) strftime(date2, sizeof(date2)-1, "%b %d %Y", tmptr); _snprintf(date, sizeof(date)-1, "%12.12s file", date2); #else strftime(date2, sizeof(date2)-1, "%b %e %Y", tmptr); snprintf(date, sizeof(date)-1, "%12.12s file", date2); #endif assertEqualMem(p + 42, date, strlen(date)); free(p); /* But "-n" without "-t" is an error. */ assert(0 != systemf("%s -in < test_option_t.cpio >in.out 2>in.err", testprog)); assertEmptyFile("in.out"); } diff --git a/contrib/libarchive/libarchive/archive.h b/contrib/libarchive/libarchive/archive.h index 5fa56de6c9fd..4182cc55d4a4 100644 --- a/contrib/libarchive/libarchive/archive.h +++ b/contrib/libarchive/libarchive/archive.h @@ -1,1212 +1,1212 @@ /*- * 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 3006002 +#define ARCHIVE_VERSION_NUMBER 3007000 #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 */ #if defined(__LIBARCHIVE_BUILD) && defined(__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 #elif defined __LIBARCHIVE_ENABLE_VISIBILITY # define __LA_DECL __attribute__((visibility("default"))) #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.6.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.7.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_CPIO_PWB (ARCHIVE_FORMAT_CPIO | 7) #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_bin(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_cpio_odc(struct archive *); __LA_DECL int archive_write_set_format_cpio_pwb(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) /* Default: Sparse file information is read from disk. */ #define ARCHIVE_READDISK_NO_SPARSE (0x0080) __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 */ diff --git a/contrib/libarchive/libarchive/archive_digest.c b/contrib/libarchive/libarchive/archive_digest.c index 3361b19ada82..08a9aeb02320 100644 --- a/contrib/libarchive/libarchive/archive_digest.c +++ b/contrib/libarchive/libarchive/archive_digest.c @@ -1,1505 +1,1565 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Andres Mejia * 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 "archive_platform.h" #include "archive.h" #include "archive_digest_private.h" /* In particular, force the configure probe to break if it tries * to test a combination of OpenSSL and libmd. */ #if defined(ARCHIVE_CRYPTO_OPENSSL) && defined(ARCHIVE_CRYPTO_LIBMD) #error Cannot use both OpenSSL and libmd. #endif +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + /* * Message digest functions for Windows platform. */ #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) /* * Initialize a Message digest. */ +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +static int +win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) +{ + NTSTATUS status; + ctx->valid = 0; + + status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0); + if (!BCRYPT_SUCCESS(status)) + return (ARCHIVE_FAILED); + status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + return (ARCHIVE_FAILED); + } + + ctx->valid = 1; + return (ARCHIVE_OK); +} +#else static int win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) { ctx->valid = 0; if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, prov, CRYPT_VERIFYCONTEXT)) { if (GetLastError() != (DWORD)NTE_BAD_KEYSET) return (ARCHIVE_FAILED); if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, prov, CRYPT_NEWKEYSET)) return (ARCHIVE_FAILED); } if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) { CryptReleaseContext(ctx->cryptProv, 0); return (ARCHIVE_FAILED); } ctx->valid = 1; return (ARCHIVE_OK); } +#endif /* * Update a Message digest. */ static int win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) { if (!ctx->valid) return (ARCHIVE_FAILED); +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCryptHashData(ctx->hHash, + (PUCHAR)(uintptr_t)buf, + len, 0); +#else CryptHashData(ctx->hash, (unsigned char *)(uintptr_t)buf, (DWORD)len, 0); +#endif return (ARCHIVE_OK); } static int win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) { +#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) DWORD siglen = (DWORD)bufsize; +#endif if (!ctx->valid) return (ARCHIVE_FAILED); +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0); + BCryptDestroyHash(ctx->hHash); + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); +#else CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0); CryptDestroyHash(ctx->hash); CryptReleaseContext(ctx->cryptProv, 0); +#endif ctx->valid = 0; return (ARCHIVE_OK); } #endif /* defined(ARCHIVE_CRYPTO_*_WIN) */ /* MD5 implementations */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) static int __archive_md5init(archive_md5_ctx *ctx) { MD5Init(ctx); return (ARCHIVE_OK); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { MD5Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { MD5Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_MD5_LIBMD) static int __archive_md5init(archive_md5_ctx *ctx) { MD5Init(ctx); return (ARCHIVE_OK); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { MD5Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { MD5Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) static int __archive_md5init(archive_md5_ctx *ctx) { CC_MD5_Init(ctx); return (ARCHIVE_OK); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { CC_MD5_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { CC_MD5_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) static int __archive_md5init(archive_md5_ctx *ctx) { mbedtls_md5_init(ctx); if (mbedtls_md5_starts_ret(ctx) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { if (mbedtls_md5_update_ret(ctx, indata, insize) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { if (mbedtls_md5_finish_ret(ctx, md) == 0) { mbedtls_md5_free(ctx); return (ARCHIVE_OK); } else { mbedtls_md5_free(ctx); return (ARCHIVE_FATAL); } } #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE) static int __archive_md5init(archive_md5_ctx *ctx) { md5_init(ctx); return (ARCHIVE_OK); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { md5_update(ctx, insize, indata); return (ARCHIVE_OK); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { md5_digest(ctx, MD5_DIGEST_SIZE, md); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL) static int __archive_md5init(archive_md5_ctx *ctx) { if ((*ctx = EVP_MD_CTX_new()) == NULL) return (ARCHIVE_FAILED); if (!EVP_DigestInit(*ctx, EVP_md5())) return (ARCHIVE_FAILED); return (ARCHIVE_OK); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { EVP_DigestUpdate(*ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so * this is meant to cope with that. Real fix is probably to fix * archive_write_set_format_xar.c */ if (*ctx) { EVP_DigestFinal(*ctx, md, NULL); EVP_MD_CTX_free(*ctx); *ctx = NULL; } return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_MD5_WIN) static int __archive_md5init(archive_md5_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5)); +#endif } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { return (win_crypto_Update(ctx, indata, insize)); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { return (win_crypto_Final(md, 16, ctx)); } #else static int __archive_md5init(archive_md5_ctx *ctx) { (void)ctx; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { (void)ctx; /* UNUSED */ (void)indata; /* UNUSED */ (void)insize; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_md5final(archive_md5_ctx *ctx, void *md) { (void)ctx; /* UNUSED */ (void)md; /* UNUSED */ return (ARCHIVE_FAILED); } #endif /* RIPEMD160 implementations */ #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { RMD160Init(ctx); return (ARCHIVE_OK); } static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { RMD160Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { RMD160Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD) static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { RIPEMD160_Init(ctx); return (ARCHIVE_OK); } static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { RIPEMD160_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { RIPEMD160_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { mbedtls_ripemd160_init(ctx); if (mbedtls_ripemd160_starts_ret(ctx) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) { mbedtls_ripemd160_free(ctx); return (ARCHIVE_OK); } else { mbedtls_ripemd160_free(ctx); return (ARCHIVE_FATAL); } } #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE) static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { ripemd160_init(ctx); return (ARCHIVE_OK); } static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { ripemd160_update(ctx, insize, indata); return (ARCHIVE_OK); } static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { if ((*ctx = EVP_MD_CTX_new()) == NULL) return (ARCHIVE_FAILED); if (!EVP_DigestInit(*ctx, EVP_ripemd160())) return (ARCHIVE_FAILED); return (ARCHIVE_OK); } static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { EVP_DigestUpdate(*ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { if (*ctx) { EVP_DigestFinal(*ctx, md, NULL); EVP_MD_CTX_free(*ctx); *ctx = NULL; } return (ARCHIVE_OK); } #else static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { (void)ctx; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { (void)ctx; /* UNUSED */ (void)indata; /* UNUSED */ (void)insize; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { (void)ctx; /* UNUSED */ (void)md; /* UNUSED */ return (ARCHIVE_FAILED); } #endif /* SHA1 implementations */ #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) static int __archive_sha1init(archive_sha1_ctx *ctx) { SHA1Init(ctx); return (ARCHIVE_OK); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { SHA1Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { SHA1Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD) static int __archive_sha1init(archive_sha1_ctx *ctx) { SHA1_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { SHA1_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { SHA1_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) static int __archive_sha1init(archive_sha1_ctx *ctx) { CC_SHA1_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { CC_SHA1_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { CC_SHA1_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) static int __archive_sha1init(archive_sha1_ctx *ctx) { mbedtls_sha1_init(ctx); if (mbedtls_sha1_starts_ret(ctx) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { if (mbedtls_sha1_finish_ret(ctx, md) == 0) { mbedtls_sha1_free(ctx); return (ARCHIVE_OK); } else { mbedtls_sha1_free(ctx); return (ARCHIVE_FATAL); } } #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE) static int __archive_sha1init(archive_sha1_ctx *ctx) { sha1_init(ctx); return (ARCHIVE_OK); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { sha1_update(ctx, insize, indata); return (ARCHIVE_OK); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { sha1_digest(ctx, SHA1_DIGEST_SIZE, md); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) static int __archive_sha1init(archive_sha1_ctx *ctx) { if ((*ctx = EVP_MD_CTX_new()) == NULL) return (ARCHIVE_FAILED); if (!EVP_DigestInit(*ctx, EVP_sha1())) return (ARCHIVE_FAILED); return (ARCHIVE_OK); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { EVP_DigestUpdate(*ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so * this is meant to cope with that. Real fix is probably to fix * archive_write_set_format_xar.c */ if (*ctx) { EVP_DigestFinal(*ctx, md, NULL); EVP_MD_CTX_free(*ctx); *ctx = NULL; } return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA1_WIN) static int __archive_sha1init(archive_sha1_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1)); +#endif } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { return (win_crypto_Update(ctx, indata, insize)); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { return (win_crypto_Final(md, 20, ctx)); } #else static int __archive_sha1init(archive_sha1_ctx *ctx) { (void)ctx; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { (void)ctx; /* UNUSED */ (void)indata; /* UNUSED */ (void)insize; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { (void)ctx; /* UNUSED */ (void)md; /* UNUSED */ return (ARCHIVE_FAILED); } #endif /* SHA256 implementations */ #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) static int __archive_sha256init(archive_sha256_ctx *ctx) { SHA256_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { SHA256_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { SHA256_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2) static int __archive_sha256init(archive_sha256_ctx *ctx) { SHA256Init(ctx); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { SHA256Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { SHA256Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3) static int __archive_sha256init(archive_sha256_ctx *ctx) { SHA256Init(ctx); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { SHA256Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { SHA256Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD) static int __archive_sha256init(archive_sha256_ctx *ctx) { SHA256_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { SHA256_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { SHA256_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) static int __archive_sha256init(archive_sha256_ctx *ctx) { CC_SHA256_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { CC_SHA256_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { CC_SHA256_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) static int __archive_sha256init(archive_sha256_ctx *ctx) { mbedtls_sha256_init(ctx); if (mbedtls_sha256_starts_ret(ctx, 0) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { if (mbedtls_sha256_finish_ret(ctx, md) == 0) { mbedtls_sha256_free(ctx); return (ARCHIVE_OK); } else { mbedtls_sha256_free(ctx); return (ARCHIVE_FATAL); } } #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE) static int __archive_sha256init(archive_sha256_ctx *ctx) { sha256_init(ctx); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { sha256_update(ctx, insize, indata); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { sha256_digest(ctx, SHA256_DIGEST_SIZE, md); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) static int __archive_sha256init(archive_sha256_ctx *ctx) { if ((*ctx = EVP_MD_CTX_new()) == NULL) return (ARCHIVE_FAILED); if (!EVP_DigestInit(*ctx, EVP_sha256())) return (ARCHIVE_FAILED); return (ARCHIVE_OK); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { EVP_DigestUpdate(*ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { if (*ctx) { EVP_DigestFinal(*ctx, md, NULL); EVP_MD_CTX_free(*ctx); *ctx = NULL; } return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA256_WIN) static int __archive_sha256init(archive_sha256_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256)); +#endif } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { return (win_crypto_Update(ctx, indata, insize)); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { return (win_crypto_Final(md, 32, ctx)); } #else static int __archive_sha256init(archive_sha256_ctx *ctx) { (void)ctx; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { (void)ctx; /* UNUSED */ (void)indata; /* UNUSED */ (void)insize; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { (void)ctx; /* UNUSED */ (void)md; /* UNUSED */ return (ARCHIVE_FAILED); } #endif /* SHA384 implementations */ #if defined(ARCHIVE_CRYPTO_SHA384_LIBC) static int __archive_sha384init(archive_sha384_ctx *ctx) { SHA384_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { SHA384_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { SHA384_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2) static int __archive_sha384init(archive_sha384_ctx *ctx) { SHA384Init(ctx); return (ARCHIVE_OK); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { SHA384Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { SHA384Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3) static int __archive_sha384init(archive_sha384_ctx *ctx) { SHA384Init(ctx); return (ARCHIVE_OK); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { SHA384Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { SHA384Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) static int __archive_sha384init(archive_sha384_ctx *ctx) { CC_SHA384_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { CC_SHA384_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { CC_SHA384_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) static int __archive_sha384init(archive_sha384_ctx *ctx) { mbedtls_sha512_init(ctx); if (mbedtls_sha512_starts_ret(ctx, 1) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { if (mbedtls_sha512_finish_ret(ctx, md) == 0) { mbedtls_sha512_free(ctx); return (ARCHIVE_OK); } else { mbedtls_sha512_free(ctx); return (ARCHIVE_FATAL); } } #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE) static int __archive_sha384init(archive_sha384_ctx *ctx) { sha384_init(ctx); return (ARCHIVE_OK); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { sha384_update(ctx, insize, indata); return (ARCHIVE_OK); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { sha384_digest(ctx, SHA384_DIGEST_SIZE, md); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) static int __archive_sha384init(archive_sha384_ctx *ctx) { if ((*ctx = EVP_MD_CTX_new()) == NULL) return (ARCHIVE_FAILED); if (!EVP_DigestInit(*ctx, EVP_sha384())) return (ARCHIVE_FAILED); return (ARCHIVE_OK); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { EVP_DigestUpdate(*ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { if (*ctx) { EVP_DigestFinal(*ctx, md, NULL); EVP_MD_CTX_free(*ctx); *ctx = NULL; } return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA384_WIN) static int __archive_sha384init(archive_sha384_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384)); +#endif } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { return (win_crypto_Update(ctx, indata, insize)); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { return (win_crypto_Final(md, 48, ctx)); } #else static int __archive_sha384init(archive_sha384_ctx *ctx) { (void)ctx; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { (void)ctx; /* UNUSED */ (void)indata; /* UNUSED */ (void)insize; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { (void)ctx; /* UNUSED */ (void)md; /* UNUSED */ return (ARCHIVE_FAILED); } #endif /* SHA512 implementations */ #if defined(ARCHIVE_CRYPTO_SHA512_LIBC) static int __archive_sha512init(archive_sha512_ctx *ctx) { SHA512_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { SHA512_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { SHA512_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2) static int __archive_sha512init(archive_sha512_ctx *ctx) { SHA512Init(ctx); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { SHA512Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { SHA512Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3) static int __archive_sha512init(archive_sha512_ctx *ctx) { SHA512Init(ctx); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { SHA512Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { SHA512Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD) static int __archive_sha512init(archive_sha512_ctx *ctx) { SHA512_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { SHA512_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { SHA512_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) static int __archive_sha512init(archive_sha512_ctx *ctx) { CC_SHA512_Init(ctx); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { CC_SHA512_Update(ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { CC_SHA512_Final(md, ctx); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) static int __archive_sha512init(archive_sha512_ctx *ctx) { mbedtls_sha512_init(ctx); if (mbedtls_sha512_starts_ret(ctx, 0) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0) return (ARCHIVE_OK); else return (ARCHIVE_FATAL); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { if (mbedtls_sha512_finish_ret(ctx, md) == 0) { mbedtls_sha512_free(ctx); return (ARCHIVE_OK); } else { mbedtls_sha512_free(ctx); return (ARCHIVE_FATAL); } } #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) static int __archive_sha512init(archive_sha512_ctx *ctx) { sha512_init(ctx); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { sha512_update(ctx, insize, indata); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { sha512_digest(ctx, SHA512_DIGEST_SIZE, md); return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) static int __archive_sha512init(archive_sha512_ctx *ctx) { if ((*ctx = EVP_MD_CTX_new()) == NULL) return (ARCHIVE_FAILED); if (!EVP_DigestInit(*ctx, EVP_sha512())) return (ARCHIVE_FAILED); return (ARCHIVE_OK); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { EVP_DigestUpdate(*ctx, indata, insize); return (ARCHIVE_OK); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { if (*ctx) { EVP_DigestFinal(*ctx, md, NULL); EVP_MD_CTX_free(*ctx); *ctx = NULL; } return (ARCHIVE_OK); } #elif defined(ARCHIVE_CRYPTO_SHA512_WIN) static int __archive_sha512init(archive_sha512_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512)); +#endif } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { return (win_crypto_Update(ctx, indata, insize)); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { return (win_crypto_Final(md, 64, ctx)); } #else static int __archive_sha512init(archive_sha512_ctx *ctx) { (void)ctx; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { (void)ctx; /* UNUSED */ (void)indata; /* UNUSED */ (void)insize; /* UNUSED */ return (ARCHIVE_FAILED); } static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { (void)ctx; /* UNUSED */ (void)md; /* UNUSED */ return (ARCHIVE_FAILED); } #endif /* NOTE: Message Digest functions are set based on availability and by the * following order of preference. * 1. libc * 2. libc2 * 3. libc3 * 4. libSystem * 5. Nettle * 6. OpenSSL * 7. libmd * 8. Windows API */ const struct archive_digest __archive_digest = { /* MD5 */ &__archive_md5init, &__archive_md5update, &__archive_md5final, /* RIPEMD160 */ &__archive_ripemd160init, &__archive_ripemd160update, &__archive_ripemd160final, /* SHA1 */ &__archive_sha1init, &__archive_sha1update, &__archive_sha1final, /* SHA256 */ &__archive_sha256init, &__archive_sha256update, &__archive_sha256final, /* SHA384 */ &__archive_sha384init, &__archive_sha384update, &__archive_sha384final, /* SHA512 */ &__archive_sha512init, &__archive_sha512update, &__archive_sha512final }; diff --git a/contrib/libarchive/libarchive/archive_digest_private.h b/contrib/libarchive/libarchive/archive_digest_private.h index 9b3bd6621bf3..339b4edca48d 100644 --- a/contrib/libarchive/libarchive/archive_digest_private.h +++ b/contrib/libarchive/libarchive/archive_digest_private.h @@ -1,416 +1,426 @@ /*- * 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) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include +typedef struct { + int valid; + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; +} Digest_CTX; +#else #include #include typedef struct { int valid; HCRYPTPROV cryptProv; HCRYPTHASH hash; } Digest_CTX; #endif +#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 diff --git a/contrib/libarchive/libarchive/archive_entry.h b/contrib/libarchive/libarchive/archive_entry.h index ff64a4712a56..74033564396d 100644 --- a/contrib/libarchive/libarchive/archive_entry.h +++ b/contrib/libarchive/libarchive/archive_entry.h @@ -1,723 +1,723 @@ /*- * 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 3006002 +#define ARCHIVE_VERSION_NUMBER 3007000 /* * 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 */ #if defined(__LIBARCHIVE_BUILD) && defined(__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 #elif defined __LIBARCHIVE_ENABLE_VISIBILITY # define __LA_DECL __attribute__((visibility("default"))) #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 */ diff --git a/contrib/libarchive/libarchive/archive_getdate.c b/contrib/libarchive/libarchive/archive_getdate.c index 39e224cb9010..20ab1b1588fe 100644 --- a/contrib/libarchive/libarchive/archive_getdate.c +++ b/contrib/libarchive/libarchive/archive_getdate.c @@ -1,1165 +1,1104 @@ /* * This code is in the public domain and has no copyright. * * This is a plain C recursive-descent translation of an old * public-domain YACC grammar that has been used for parsing dates in * very many open-source projects. * * Since the original authors were generous enough to donate their * work to the public domain, I feel compelled to match their * generosity. * * Tim Kientzle, February 2009. */ /* * Header comment from original getdate.y: */ /* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990; ** ** This grammar has 10 shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ #include "archive_platform.h" #ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); #endif #include #include #include #include #include #define __LIBARCHIVE_BUILD 1 #include "archive_getdate.h" /* Basic time units. */ #define EPOCH 1970 #define MINUTE (60L) #define HOUR (60L * MINUTE) #define DAY (24L * HOUR) /* Daylight-savings mode: on, off, or not yet known. */ enum DSTMODE { DSTon, DSToff, DSTmaybe }; /* Meridian: am or pm. */ enum { tAM, tPM }; /* Token types returned by nexttoken() */ enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, tUNUMBER, tZONE, tDST }; struct token { int token; time_t value; }; /* * Parser state. */ struct gdstate { struct token *tokenp; /* Pointer to next token. */ /* HaveXxxx counts how many of this kind of phrase we've seen; * it's a fatal error to have more than one time, zone, day, * or date phrase. */ int HaveYear; int HaveMonth; int HaveDay; int HaveWeekDay; /* Day of week */ int HaveTime; /* Hour/minute/second */ int HaveZone; /* timezone and/or DST info */ int HaveRel; /* time offset; we can have more than one */ /* Absolute time values. */ time_t Timezone; /* Seconds offset from GMT */ time_t Day; time_t Hour; time_t Minutes; time_t Month; time_t Seconds; time_t Year; /* DST selection */ enum DSTMODE DSTmode; /* Day of week accounting, e.g., "3rd Tuesday" */ time_t DayOrdinal; /* "3" in "3rd Tuesday" */ time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */ /* Relative time values: hour/day/week offsets are measured in * seconds, month/year are counted in months. */ time_t RelMonth; time_t RelSeconds; }; /* * A series of functions that recognize certain common time phrases. * Each function returns 1 if it managed to make sense of some of the * tokens, zero otherwise. */ /* * hour:minute or hour:minute:second with optional AM, PM, or numeric * timezone offset */ static int timephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == ':' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == ':' && gds->tokenp[4].token == tUNUMBER) { /* "12:14:18" or "22:08:07" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->tokenp[2].value; gds->Seconds = gds->tokenp[4].value; gds->tokenp += 5; } else if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == ':' && gds->tokenp[2].token == tUNUMBER) { /* "12:14" or "22:08" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->tokenp[2].value; gds->Seconds = 0; gds->tokenp += 3; } else if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tAMPM) { /* "7" is a time if it's followed by "am" or "pm" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->Seconds = 0; /* We'll handle the AM/PM below. */ gds->tokenp += 1; } else { /* We can't handle this. */ return 0; } if (gds->tokenp[0].token == tAMPM) { /* "7:12pm", "12:20:13am" */ if (gds->Hour == 12) gds->Hour = 0; if (gds->tokenp[0].value == tPM) gds->Hour += 12; gds->tokenp += 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER) { /* "7:14+0700" */ gds->HaveZone++; gds->DSTmode = DSToff; gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR + (gds->tokenp[1].value % 100) * MINUTE); gds->tokenp += 2; } if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER) { /* "19:14:12-0530" */ gds->HaveZone++; gds->DSTmode = DSToff; gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR + (gds->tokenp[1].value % 100) * MINUTE); gds->tokenp += 2; } return 1; } /* * Timezone name, possibly including DST. */ static int zonephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tZONE && gds->tokenp[1].token == tDST) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSTon; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tZONE) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSToff; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tDAYZONE) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSTon; gds->tokenp += 1; return 1; } return 0; } /* * Year/month/day in various combinations. */ static int datephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '/' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == '/' && gds->tokenp[4].token == tUNUMBER) { gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; if (gds->tokenp[0].value >= 13) { /* First number is big: 2004/01/29, 99/02/17 */ gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; } else if ((gds->tokenp[4].value >= 13) || (gds->tokenp[2].value >= 13)) { /* Last number is big: 01/07/98 */ /* Middle number is big: 01/29/04 */ gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } else { /* No significant clues: 02/03/04 */ gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '/' && gds->tokenp[2].token == tUNUMBER) { /* "1/15" */ gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '-' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == '-' && gds->tokenp[4].token == tUNUMBER) { /* ISO 8601 format. yyyy-mm-dd. */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '-' && gds->tokenp[2].token == tMONTH && gds->tokenp[3].token == '-' && gds->tokenp[4].token == tUNUMBER) { gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; if (gds->tokenp[0].value > 31) { /* e.g. 1992-Jun-17 */ gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; } else { /* e.g. 17-JUN-1992. */ gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tMONTH && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == ',' && gds->tokenp[3].token == tUNUMBER) { /* "June 17, 2001" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[1].value; gds->Year = gds->tokenp[3].value; gds->tokenp += 4; return 1; } if (gds->tokenp[0].token == tMONTH && gds->tokenp[1].token == tUNUMBER) { /* "May 3" */ gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH && gds->tokenp[2].token == tUNUMBER) { /* "12 Sept 1997" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[1].value; gds->Year = gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH) { /* "12 Sept" */ gds->HaveMonth++; gds->HaveDay++; gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[1].value; gds->tokenp += 2; return 1; } return 0; } /* * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc. */ static int relunitphrase(struct gdstate *gds) { if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tSEC_UNIT) { /* "-3 hours" */ gds->HaveRel++; gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tSEC_UNIT) { /* "+1 minute" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tSEC_UNIT) { /* "1 day" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tMONTH_UNIT) { /* "-3 months" */ gds->HaveRel++; gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tMONTH_UNIT) { /* "+5 years" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH_UNIT) { /* "2 years" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == tSEC_UNIT) { /* "now", "tomorrow" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tMONTH_UNIT) { /* "month" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[0].value; gds->tokenp += 1; return 1; } return 0; } /* * Day of the week specification. */ static int dayphrase(struct gdstate *gds) { if (gds->tokenp[0].token == tDAY) { /* "tues", "wednesday," */ gds->HaveWeekDay++; gds->DayOrdinal = 1; gds->DayNumber = gds->tokenp[0].value; gds->tokenp += 1; if (gds->tokenp[0].token == ',') gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tDAY) { /* "second tues" "3 wed" */ gds->HaveWeekDay++; gds->DayOrdinal = gds->tokenp[0].value; gds->DayNumber = gds->tokenp[1].value; gds->tokenp += 2; return 1; } return 0; } /* * Try to match a phrase using one of the above functions. * This layer also deals with a couple of generic issues. */ static int phrase(struct gdstate *gds) { if (timephrase(gds)) return 1; if (zonephrase(gds)) return 1; if (datephrase(gds)) return 1; if (dayphrase(gds)) return 1; if (relunitphrase(gds)) { if (gds->tokenp[0].token == tAGO) { gds->RelSeconds = -gds->RelSeconds; gds->RelMonth = -gds->RelMonth; gds->tokenp += 1; } return 1; } /* Bare numbers sometimes have meaning. */ if (gds->tokenp[0].token == tUNUMBER) { if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) { gds->HaveYear++; gds->Year = gds->tokenp[0].value; gds->tokenp += 1; return 1; } if(gds->tokenp[0].value > 10000) { /* "20040301" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Day= (gds->tokenp[0].value)%100; gds->Month= (gds->tokenp[0].value/100)%100; gds->Year = gds->tokenp[0].value/10000; gds->tokenp += 1; return 1; } if (gds->tokenp[0].value < 24) { gds->HaveTime++; gds->Hour = gds->tokenp[0].value; gds->Minutes = 0; gds->Seconds = 0; gds->tokenp += 1; return 1; } if ((gds->tokenp[0].value / 100 < 24) && (gds->tokenp[0].value % 100 < 60)) { /* "513" is same as "5:13" */ gds->Hour = gds->tokenp[0].value / 100; gds->Minutes = gds->tokenp[0].value % 100; gds->Seconds = 0; gds->tokenp += 1; return 1; } } return 0; } /* * A dictionary of time words. */ static struct LEXICON { size_t abbrev; const char *name; int type; time_t value; } const TimeWords[] = { /* am/pm */ { 0, "am", tAMPM, tAM }, { 0, "pm", tAMPM, tPM }, /* Month names. */ { 3, "january", tMONTH, 1 }, { 3, "february", tMONTH, 2 }, { 3, "march", tMONTH, 3 }, { 3, "april", tMONTH, 4 }, { 3, "may", tMONTH, 5 }, { 3, "june", tMONTH, 6 }, { 3, "july", tMONTH, 7 }, { 3, "august", tMONTH, 8 }, { 3, "september", tMONTH, 9 }, { 3, "october", tMONTH, 10 }, { 3, "november", tMONTH, 11 }, { 3, "december", tMONTH, 12 }, /* Days of the week. */ { 2, "sunday", tDAY, 0 }, { 3, "monday", tDAY, 1 }, { 2, "tuesday", tDAY, 2 }, { 3, "wednesday", tDAY, 3 }, { 2, "thursday", tDAY, 4 }, { 2, "friday", tDAY, 5 }, { 2, "saturday", tDAY, 6 }, /* Timezones: Offsets are in seconds. */ { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */ { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */ { 0, "utc", tZONE, 0*HOUR }, { 0, "wet", tZONE, 0*HOUR }, /* Western European */ { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */ { 0, "wat", tZONE, 1*HOUR }, /* West Africa */ { 0, "at", tZONE, 2*HOUR }, /* Azores */ /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */ /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/ { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */ { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */ { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */ { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */ { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */ { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */ { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */ { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */ { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */ { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */ { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */ { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */ { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */ { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */ { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */ { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */ { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */ { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */ { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */ { 0, "nt", tZONE, 11*HOUR }, /* Nome */ { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */ { 0, "cet", tZONE, -1*HOUR }, /* Central European */ { 0, "met", tZONE, -1*HOUR }, /* Middle European */ { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */ { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */ { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */ { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */ { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */ { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */ { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */ { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */ { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */ { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */ { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */ { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */ { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */ /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */ /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */ { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */ { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */ { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/ { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */ { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */ { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */ { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */ { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */ { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */ { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */ { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */ { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */ { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */ { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */ { 0, "dst", tDST, 0 }, /* Time units. */ { 4, "years", tMONTH_UNIT, 12 }, { 5, "months", tMONTH_UNIT, 1 }, { 9, "fortnights", tSEC_UNIT, 14 * DAY }, { 4, "weeks", tSEC_UNIT, 7 * DAY }, { 3, "days", tSEC_UNIT, DAY }, { 4, "hours", tSEC_UNIT, HOUR }, { 3, "minutes", tSEC_UNIT, MINUTE }, { 3, "seconds", tSEC_UNIT, 1 }, /* Relative-time words. */ { 0, "tomorrow", tSEC_UNIT, DAY }, { 0, "yesterday", tSEC_UNIT, -DAY }, { 0, "today", tSEC_UNIT, 0 }, { 0, "now", tSEC_UNIT, 0 }, { 0, "last", tUNUMBER, -1 }, { 0, "this", tSEC_UNIT, 0 }, { 0, "next", tUNUMBER, 2 }, { 0, "first", tUNUMBER, 1 }, { 0, "1st", tUNUMBER, 1 }, /* { 0, "second", tUNUMBER, 2 }, */ { 0, "2nd", tUNUMBER, 2 }, { 0, "third", tUNUMBER, 3 }, { 0, "3rd", tUNUMBER, 3 }, { 0, "fourth", tUNUMBER, 4 }, { 0, "4th", tUNUMBER, 4 }, { 0, "fifth", tUNUMBER, 5 }, { 0, "5th", tUNUMBER, 5 }, { 0, "sixth", tUNUMBER, 6 }, { 0, "seventh", tUNUMBER, 7 }, { 0, "eighth", tUNUMBER, 8 }, { 0, "ninth", tUNUMBER, 9 }, { 0, "tenth", tUNUMBER, 10 }, { 0, "eleventh", tUNUMBER, 11 }, { 0, "twelfth", tUNUMBER, 12 }, { 0, "ago", tAGO, 1 }, /* Military timezones. */ { 0, "a", tZONE, 1*HOUR }, { 0, "b", tZONE, 2*HOUR }, { 0, "c", tZONE, 3*HOUR }, { 0, "d", tZONE, 4*HOUR }, { 0, "e", tZONE, 5*HOUR }, { 0, "f", tZONE, 6*HOUR }, { 0, "g", tZONE, 7*HOUR }, { 0, "h", tZONE, 8*HOUR }, { 0, "i", tZONE, 9*HOUR }, { 0, "k", tZONE, 10*HOUR }, { 0, "l", tZONE, 11*HOUR }, { 0, "m", tZONE, 12*HOUR }, { 0, "n", tZONE, -1*HOUR }, { 0, "o", tZONE, -2*HOUR }, { 0, "p", tZONE, -3*HOUR }, { 0, "q", tZONE, -4*HOUR }, { 0, "r", tZONE, -5*HOUR }, { 0, "s", tZONE, -6*HOUR }, { 0, "t", tZONE, -7*HOUR }, { 0, "u", tZONE, -8*HOUR }, { 0, "v", tZONE, -9*HOUR }, { 0, "w", tZONE, -10*HOUR }, { 0, "x", tZONE, -11*HOUR }, { 0, "y", tZONE, -12*HOUR }, { 0, "z", tZONE, 0*HOUR }, /* End of table. */ { 0, NULL, 0, 0 } }; /* * Year is either: * = A number from 0 to 99, which means a year from 1970 to 2069, or * = The actual year (>=100). */ static time_t Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes, time_t Seconds, time_t Timezone, enum DSTMODE DSTmode) { signed char DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t Julian; int i; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (Year < 69) Year += 2000; else if (Year < 100) Year += 1900; DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 29 : 28; /* Checking for 2038 bogusly assumes that time_t is 32 bits. But I'm too lazy to try to check for time_t overflow in another way. */ if (Year < EPOCH || Year >= 2038 || Month < 1 || Month > 12 /* Lint fluff: "conversion from long may lose accuracy" */ || Day < 1 || Day > DaysInMonth[(int)--Month] || Hours < 0 || Hours > 23 || Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) return -1; Julian = Day - 1; for (i = 0; i < Month; i++) Julian += DaysInMonth[i]; for (i = EPOCH; i < Year; i++) Julian += 365 + (i % 4 == 0); Julian *= DAY; Julian += Timezone; Julian += Hours * HOUR + Minutes * MINUTE + Seconds; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Julian, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Julian; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Julian); #endif if (DSTmode == DSTon || (DSTmode == DSTmaybe && ltime->tm_isdst)) Julian -= HOUR; return Julian; } static time_t DSTcorrect(time_t Start, time_t Future) { time_t StartDay; time_t FutureDay; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif - -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Start, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Start; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Start); #endif StartDay = (ltime->tm_hour + 1) % 24; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Future, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Future; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Future); #endif FutureDay = (ltime->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * HOUR; } static time_t RelativeDate(time_t Start, time_t zone, int dstmode, time_t DayOrdinal, time_t DayNumber) { struct tm *tm; time_t t, now; -#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif t = Start - zone; -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif defined(HAVE_GMTIME_R) tm = gmtime_r(&t, &tmbuf); -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - terr = _gmtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = gmtime(&t); #endif now = Start; now += DAY * ((DayNumber - tm->tm_wday + 7) % 7); now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); if (dstmode == DSTmaybe) return DSTcorrect(Start, now); return now - Start; } static time_t RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) { struct tm *tm; time_t Month; time_t Year; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (RelMonth == 0) return 0; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&Start, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Start; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = localtime(&Start); #endif Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, Timezone, DSTmaybe)); } /* * Tokenizer. */ static int nexttoken(const char **in, time_t *value) { char c; char buff[64]; for ( ; ; ) { while (isspace((unsigned char)**in)) ++*in; /* Skip parenthesized comments. */ if (**in == '(') { int Count = 0; do { c = *(*in)++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); continue; } /* Try the next token in the word table first. */ /* This allows us to match "2nd", for example. */ { const char *src = *in; const struct LEXICON *tp; unsigned i = 0; /* Force to lowercase and strip '.' characters. */ while (*src != '\0' && (isalnum((unsigned char)*src) || *src == '.') && i < sizeof(buff)-1) { if (*src != '.') { if (isupper((unsigned char)*src)) buff[i++] = tolower((unsigned char)*src); else buff[i++] = *src; } src++; } buff[i] = '\0'; /* * Find the first match. If the word can be * abbreviated, make sure we match at least * the minimum abbreviation. */ for (tp = TimeWords; tp->name; tp++) { size_t abbrev = tp->abbrev; if (abbrev == 0) abbrev = strlen(tp->name); if (strlen(buff) >= abbrev && strncmp(tp->name, buff, strlen(buff)) == 0) { /* Skip over token. */ *in = src; /* Return the match. */ *value = tp->value; return tp->type; } } } /* * Not in the word table, maybe it's a number. Note: * Because '-' and '+' have other special meanings, I * don't deal with signed numbers here. */ if (isdigit((unsigned char)(c = **in))) { for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) *value = 10 * *value + c - '0'; (*in)--; return (tUNUMBER); } return *(*in)++; } } #define TM_YEAR_ORIGIN 1900 /* Yield A - B, measured in seconds. */ static long difftm (struct tm *a, struct tm *b) { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); int days = ( /* difference in day of year */ a->tm_yday - b->tm_yday /* + intervening leap days */ + ((ay >> 2) - (by >> 2)) - (ay/100 - by/100) + ((ay/100 >> 2) - (by/100 >> 2)) /* + difference in years * 365 */ + (long)(ay-by) * 365 ); return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR + (a->tm_min - b->tm_min) * MINUTE + (a->tm_sec - b->tm_sec)); } /* * * The public function. * * TODO: tokens[] array should be dynamically sized. */ time_t __archive_get_date(time_t now, const char *p) { struct token tokens[256]; struct gdstate _gds; struct token *lasttoken; struct gdstate *gds; struct tm local, *tm; struct tm gmt, *gmt_ptr; time_t Start; time_t tod; long tzone; -#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif /* Clear out the parsed token array. */ memset(tokens, 0, sizeof(tokens)); /* Initialize the parser state. */ memset(&_gds, 0, sizeof(_gds)); gds = &_gds; /* Look up the current time. */ -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&local, &now) ? NULL : &local; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&now, &local); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = now; - terr = _localtime64_s(&local, &tmptime); - if (terr) - tm = NULL; - else - tm = &local; #else memset(&local, 0, sizeof(local)); tm = localtime(&now); #endif if (tm == NULL) return -1; -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S) +#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) local = *tm; #endif /* Look up UTC if we can and use that to determine the current * timezone offset. */ -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt; +#elif defined(HAVE_GMTIME_R) gmt_ptr = gmtime_r(&now, &gmt); -#elif defined(HAVE__GMTIME64_S) - tmptime = now; - terr = _gmtime64_s(&gmt, &tmptime); - if (terr) - gmt_ptr = NULL; - else - gmt_ptr = &gmt; #else memset(&gmt, 0, sizeof(gmt)); gmt_ptr = gmtime(&now); if (gmt_ptr != NULL) { /* Copy, in case localtime and gmtime use the same buffer. */ gmt = *gmt_ptr; } #endif if (gmt_ptr != NULL) tzone = difftm (&gmt, &local); else /* This system doesn't understand timezones; fake it. */ tzone = 0; if(local.tm_isdst) tzone += HOUR; /* Tokenize the input string. */ lasttoken = tokens; while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) { ++lasttoken; if (lasttoken > tokens + 255) return -1; } gds->tokenp = tokens; /* Match phrases until we run out of input tokens. */ while (gds->tokenp < lasttoken) { if (!phrase(gds)) return -1; } /* Use current local timezone if none was specified. */ if (!gds->HaveZone) { gds->Timezone = tzone; gds->DSTmode = DSTmaybe; } /* If a timezone was specified, use that for generating the default * time components instead of the local timezone. */ if (gds->HaveZone && gmt_ptr != NULL) { now -= gds->Timezone; -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt; +#elif defined(HAVE_GMTIME_R) gmt_ptr = gmtime_r(&now, &gmt); -#elif defined(HAVE__GMTIME64_S) - tmptime = now; - terr = _gmtime64_s(&gmt, &tmptime); - if (terr) - gmt_ptr = NULL; - else - gmt_ptr = &gmt; #else gmt_ptr = gmtime(&now); #endif if (gmt_ptr != NULL) local = *gmt_ptr; now += gds->Timezone; } if (!gds->HaveYear) gds->Year = local.tm_year + 1900; if (!gds->HaveMonth) gds->Month = local.tm_mon + 1; if (!gds->HaveDay) gds->Day = local.tm_mday; /* Note: No default for hour/min/sec; a specifier that just * gives date always refers to 00:00 on that date. */ /* If we saw more than one time, timezone, weekday, year, month, * or day, then give up. */ if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1 || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1) return -1; /* Compute an absolute time based on whatever absolute information * we collected. */ if (gds->HaveYear || gds->HaveMonth || gds->HaveDay || gds->HaveTime || gds->HaveWeekDay) { Start = Convert(gds->Month, gds->Day, gds->Year, gds->Hour, gds->Minutes, gds->Seconds, gds->Timezone, gds->DSTmode); if (Start < 0) return -1; } else { Start = now; if (!gds->HaveRel) Start -= local.tm_hour * HOUR + local.tm_min * MINUTE + local.tm_sec; } /* Add the relative offset. */ Start += gds->RelSeconds; Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth); /* Adjust for day-of-week offsets. */ if (gds->HaveWeekDay && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) { tod = RelativeDate(Start, gds->Timezone, gds->DSTmode, gds->DayOrdinal, gds->DayNumber); Start += tod; } /* -1 is an error indicator, so return 0 instead of -1 if * that's the actual time. */ return Start == -1 ? 0 : Start; } #if defined(TEST) /* ARGSUSED */ int main(int argc, char **argv) { time_t d; time_t now = time(NULL); while (*++argv != NULL) { (void)printf("Input: %s\n", *argv); d = get_date(now, *argv); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else (void)printf("Output: %s\n", ctime(&d)); } exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ diff --git a/contrib/libarchive/libarchive/archive_hmac.c b/contrib/libarchive/libarchive/archive_hmac.c index 0914f38e3670..edb3bf5abd42 100644 --- a/contrib/libarchive/libarchive/archive_hmac.c +++ b/contrib/libarchive/libarchive/archive_hmac.c @@ -1,334 +1,339 @@ /*- * 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_hmac_private.h" /* * On systems that do not support any recognized crypto libraries, * the archive_hmac.c file is expected to 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_hmac_build_hack(void) { return 0; } #ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len); return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { CCHmacUpdate(ctx, data, data_len); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { CCHmacFinal(ctx, out); *out_len = 20; } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); } #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifndef BCRYPT_HASH_REUSABLE_FLAG # define BCRYPT_HASH_REUSABLE_FLAG 0x00000020 #endif static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; PBYTE hash; ULONG result; NTSTATUS status; ctx->hAlg = NULL; status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (!BCRYPT_SUCCESS(status)) return -1; status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len, sizeof(hash_len), &result, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len); if (hash == NULL) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } status = BCryptCreateHash(hAlg, &hHash, NULL, 0, (PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); HeapFree(GetProcessHeap(), 0, hash); return -1; } ctx->hAlg = hAlg; ctx->hHash = hHash; ctx->hash_len = hash_len; ctx->hash = hash; return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0); if (ctx->hash_len == *out_len) memcpy(out, ctx->hash, *out_len); } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { if (ctx->hAlg != NULL) { BCryptCloseAlgorithmProvider(ctx->hAlg, 0); HeapFree(GetProcessHeap(), 0, ctx->hash); ctx->hAlg = NULL; } } #elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { 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_md_hmac_starts(ctx, key, key_len); if (ret != 0) { mbedtls_md_free(ctx); return (-1); } return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { mbedtls_md_hmac_update(ctx, data, data_len); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { (void)out_len; /* UNUSED */ mbedtls_md_hmac_finish(ctx, out); } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { mbedtls_md_free(ctx); memset(ctx, 0, sizeof(*ctx)); } #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { hmac_sha1_set_key(ctx, key_len, key); return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { hmac_sha1_update(ctx, data_len, data); } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { hmac_sha1_digest(ctx, (unsigned)*out_len, out); } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); } #elif defined(HAVE_LIBCRYPTO) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L - OSSL_PARAM params[2]; + EVP_MAC *mac; - EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL); + char sha1[] = "SHA1"; + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1), + OSSL_PARAM_END + }; + + mac = EVP_MAC_fetch(NULL, "HMAC", NULL); *ctx = EVP_MAC_CTX_new(mac); + EVP_MAC_free(mac); if (*ctx == NULL) return -1; - EVP_MAC_free(mac); - params[0] = OSSL_PARAM_construct_utf8_string("digest", (char *)"SHA1", 0); - params[1] = OSSL_PARAM_construct_end(); + EVP_MAC_init(*ctx, key, key_len, params); #else *ctx = HMAC_CTX_new(); if (*ctx == NULL) return -1; HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL); #endif return 0; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L EVP_MAC_update(*ctx, data, data_len); #else HMAC_Update(*ctx, data, data_len); #endif } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L size_t len = *out_len; #else unsigned int len = (unsigned int)*out_len; #endif #if OPENSSL_VERSION_NUMBER >= 0x30000000L EVP_MAC_final(*ctx, out, &len, *out_len); #else HMAC_Final(*ctx, out, &len); #endif *out_len = len; } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L EVP_MAC_CTX_free(*ctx); #else HMAC_CTX_free(*ctx); #endif *ctx = NULL; } #else /* Stub */ static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { (void)ctx;/* UNUSED */ (void)key;/* UNUSED */ (void)key_len;/* UNUSED */ return -1; } static void __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, size_t data_len) { (void)ctx;/* UNUSED */ (void)data;/* UNUSED */ (void)data_len;/* UNUSED */ } static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { (void)ctx;/* UNUSED */ (void)out;/* UNUSED */ (void)out_len;/* UNUSED */ } static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) { (void)ctx;/* UNUSED */ } #endif const struct archive_hmac __archive_hmac = { &__hmac_sha1_init, &__hmac_sha1_update, &__hmac_sha1_final, &__hmac_sha1_cleanup, }; diff --git a/contrib/libarchive/libarchive/archive_hmac_private.h b/contrib/libarchive/libarchive/archive_hmac_private.h index 50044a045e37..d0fda7f9667a 100644 --- a/contrib/libarchive/libarchive/archive_hmac_private.h +++ b/contrib/libarchive/libarchive/archive_hmac_private.h @@ -1,117 +1,119 @@ /*- * 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_HMAC_PRIVATE_H_INCLUDED #define ARCHIVE_HMAC_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_hmac.c file is expected to 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_hmac_build_hack(void); #ifdef __APPLE__ # include # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 # define ARCHIVE_HMAC_USE_Apple_CommonCrypto # endif #endif #ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto #include typedef CCHmacContext archive_hmac_sha1_ctx; #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #include typedef struct { BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; PBYTE hash; } archive_hmac_sha1_ctx; #elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H) #include typedef mbedtls_md_context_t archive_hmac_sha1_ctx; #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) #include typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx; #elif defined(HAVE_LIBCRYPTO) #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include + typedef EVP_MAC_CTX *archive_hmac_sha1_ctx; #else #include "archive_openssl_hmac_private.h" typedef HMAC_CTX* archive_hmac_sha1_ctx; #endif #else typedef int archive_hmac_sha1_ctx; #endif /* HMAC */ #define archive_hmac_sha1_init(ctx, key, key_len)\ __archive_hmac.__hmac_sha1_init(ctx, key, key_len) #define archive_hmac_sha1_update(ctx, data, data_len)\ __archive_hmac.__hmac_sha1_update(ctx, data, data_len) #define archive_hmac_sha1_final(ctx, out, out_len)\ __archive_hmac.__hmac_sha1_final(ctx, out, out_len) #define archive_hmac_sha1_cleanup(ctx)\ __archive_hmac.__hmac_sha1_cleanup(ctx) struct archive_hmac { /* HMAC */ int (*__hmac_sha1_init)(archive_hmac_sha1_ctx *, const uint8_t *, size_t); void (*__hmac_sha1_update)(archive_hmac_sha1_ctx *, const uint8_t *, size_t); void (*__hmac_sha1_final)(archive_hmac_sha1_ctx *, uint8_t *, size_t *); void (*__hmac_sha1_cleanup)(archive_hmac_sha1_ctx *); }; extern const struct archive_hmac __archive_hmac; #endif /* ARCHIVE_HMAC_PRIVATE_H_INCLUDED */ diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/libarchive/archive_openssl_evp_private.h index ebb06702d0c5..8ac4772808e6 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/libarchive/archive_openssl_evp_private.h @@ -1,53 +1,54 @@ /*- * 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. */ #ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED #define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif #include #include -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) #include /* malloc, free */ #include /* memset */ static inline EVP_MD_CTX *EVP_MD_CTX_new(void) { EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); return ctx; } static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_cleanup(ctx); memset(ctx, 0, sizeof(*ctx)); free(ctx); } #endif #endif diff --git a/contrib/libarchive/libarchive/archive_random.c b/contrib/libarchive/libarchive/archive_random.c index 9d1aa493f0c8..301765acd830 100644 --- a/contrib/libarchive/libarchive/archive_random.c +++ b/contrib/libarchive/libarchive/archive_random.c @@ -1,272 +1,301 @@ /*- * 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" __FBSDID("$FreeBSD$"); #ifdef HAVE_STDLIB_H #include #endif #if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) #ifdef HAVE_FCNTL #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_PTHREAD_H #include #endif -static void arc4random_buf(void *, size_t); +static void la_arc4random_buf(void *, size_t); #endif /* HAVE_ARC4RANDOM_BUF */ #include "archive.h" #include "archive_random_private.h" -#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#elif defined(HAVE_WINCRYPT_H) #include #endif +#endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* * Random number generator function. * This simply calls arc4random_buf function if the platform provides it. */ int archive_random(void *buf, size_t nbytes) { #if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + NTSTATUS status; + BCRYPT_ALG_HANDLE hAlg; + + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0); + if (!BCRYPT_SUCCESS(status)) + return ARCHIVE_FAILED; + status = BCryptGenRandom(hAlg, buf, nbytes, 0); + BCryptCloseAlgorithmProvider(hAlg, 0); + if (!BCRYPT_SUCCESS(status)) + return ARCHIVE_FAILED; + + return ARCHIVE_OK; +# else HCRYPTPROV hProv; BOOL success; success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) { success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET); } if (success) { success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf); CryptReleaseContext(hProv, 0); if (success) return ARCHIVE_OK; } /* TODO: Does this case really happen? */ return ARCHIVE_FAILED; +# endif +#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) + la_arc4random_buf(buf, nbytes); + return ARCHIVE_OK; #else arc4random_buf(buf, nbytes); return ARCHIVE_OK; #endif } #if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) /* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Arc4 random number generator for OpenBSD. * * This code is derived from section 17.1 of Applied Cryptography, * second edition, which describes a stream cipher allegedly * compatible with RSA Labs "RC4" cipher (the actual description of * which is a trade secret). The same algorithm is used as a stream * cipher called "arcfour" in Tatu Ylonen's ssh package. * * RC4 is a registered trademark of RSA Laboratories. */ #ifdef __GNUC__ #define inline __inline #else /* !__GNUC__ */ #define inline #endif /* !__GNUC__ */ struct arc4_stream { uint8_t i; uint8_t j; uint8_t s[256]; }; #define RANDOMDEV "/dev/urandom" #define KEYSIZE 128 #ifdef HAVE_PTHREAD_H static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; #define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx); #define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx); #else #define _ARC4_LOCK() #define _ARC4_UNLOCK() #endif static int rs_initialized; static struct arc4_stream rs; static pid_t arc4_stir_pid; static int arc4_count; static inline uint8_t arc4_getbyte(void); static void arc4_stir(void); static inline void arc4_init(void) { int n; for (n = 0; n < 256; n++) rs.s[n] = n; rs.i = 0; rs.j = 0; } static inline void arc4_addrandom(uint8_t *dat, int datlen) { int n; uint8_t si; rs.i--; for (n = 0; n < 256; n++) { rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si + dat[n % datlen]); rs.s[rs.i] = rs.s[rs.j]; rs.s[rs.j] = si; } rs.j = rs.i; } static void arc4_stir(void) { int done, fd, i; struct { struct timeval tv; pid_t pid; uint8_t rnd[KEYSIZE]; } rdat; if (!rs_initialized) { arc4_init(); rs_initialized = 1; } done = 0; fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0); if (fd >= 0) { if (read(fd, &rdat, KEYSIZE) == KEYSIZE) done = 1; (void)close(fd); } if (!done) { (void)gettimeofday(&rdat.tv, NULL); rdat.pid = getpid(); /* We'll just take whatever was on the stack too... */ } arc4_addrandom((uint8_t *)&rdat, KEYSIZE); /* * Discard early keystream, as per recommendations in: * "(Not So) Random Shuffles of RC4" by Ilya Mironov. * As per the Network Operations Division, cryptographic requirements * published on wikileaks on March 2017. */ for (i = 0; i < 3072; i++) (void)arc4_getbyte(); arc4_count = 1600000; } static void arc4_stir_if_needed(void) { pid_t pid = getpid(); if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) { arc4_stir_pid = pid; arc4_stir(); } } static inline uint8_t arc4_getbyte(void) { uint8_t si, sj; rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si); sj = rs.s[rs.j]; rs.s[rs.i] = sj; rs.s[rs.j] = si; return (rs.s[(si + sj) & 0xff]); } static void -arc4random_buf(void *_buf, size_t n) +la_arc4random_buf(void *_buf, size_t n) { uint8_t *buf = (uint8_t *)_buf; _ARC4_LOCK(); arc4_stir_if_needed(); while (n--) { if (--arc4_count <= 0) arc4_stir(); buf[n] = arc4_getbyte(); } _ARC4_UNLOCK(); } #endif /* !HAVE_ARC4RANDOM_BUF */ diff --git a/contrib/libarchive/libarchive/archive_read_data_into_fd.c b/contrib/libarchive/libarchive/archive_read_data_into_fd.c index 1f210fce146f..81a36125a4ba 100644 --- a/contrib/libarchive/libarchive/archive_read_data_into_fd.c +++ b/contrib/libarchive/libarchive/archive_read_data_into_fd.c @@ -1,139 +1,144 @@ /*- * 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_UNISTD_H #include #endif #include "archive.h" #include "archive_private.h" /* Maximum amount of data to write at one time. */ #define MAX_WRITE (1024 * 1024) /* * This implementation minimizes copying of data and is sparse-file aware. */ static int pad_to(struct archive *a, int fd, int can_lseek, size_t nulls_size, const char *nulls, int64_t target_offset, int64_t actual_offset) { size_t to_write; ssize_t bytes_written; if (can_lseek) { actual_offset = lseek(fd, target_offset - actual_offset, SEEK_CUR); if (actual_offset != target_offset) { archive_set_error(a, errno, "Seek error"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } while (target_offset > actual_offset) { to_write = nulls_size; if (target_offset < actual_offset + (int64_t)nulls_size) to_write = (size_t)(target_offset - actual_offset); bytes_written = write(fd, nulls, to_write); if (bytes_written < 0) { archive_set_error(a, errno, "Write error"); return (ARCHIVE_FATAL); } actual_offset += bytes_written; } return (ARCHIVE_OK); } int archive_read_data_into_fd(struct archive *a, int fd) { struct stat st; int r, r2; const void *buff; size_t size, bytes_to_write; ssize_t bytes_written; int64_t target_offset; int64_t actual_offset = 0; int can_lseek; char *nulls = NULL; size_t nulls_size = 16384; archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd"); can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode); - if (!can_lseek) + if (!can_lseek) { nulls = calloc(1, nulls_size); + if (!nulls) { + r = ARCHIVE_FATAL; + goto cleanup; + } + } while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) == ARCHIVE_OK) { const char *p = buff; if (target_offset > actual_offset) { r = pad_to(a, fd, can_lseek, nulls_size, nulls, target_offset, actual_offset); if (r != ARCHIVE_OK) break; actual_offset = target_offset; } while (size > 0) { bytes_to_write = size; if (bytes_to_write > MAX_WRITE) bytes_to_write = MAX_WRITE; bytes_written = write(fd, p, bytes_to_write); if (bytes_written < 0) { archive_set_error(a, errno, "Write error"); r = ARCHIVE_FATAL; goto cleanup; } actual_offset += bytes_written; p += bytes_written; size -= bytes_written; } } if (r == ARCHIVE_EOF && target_offset > actual_offset) { r2 = pad_to(a, fd, can_lseek, nulls_size, nulls, target_offset, actual_offset); if (r2 != ARCHIVE_OK) r = r2; } cleanup: free(nulls); if (r != ARCHIVE_EOF) return (r); return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_disk_posix.c b/contrib/libarchive/libarchive/archive_read_disk_posix.c index 5a94ec5d4399..e9657f6a72e8 100644 --- a/contrib/libarchive/libarchive/archive_read_disk_posix.c +++ b/contrib/libarchive/libarchive/archive_read_disk_posix.c @@ -1,2732 +1,2760 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * 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 * 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. */ /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LINUX_MAGIC_H #include #endif #ifdef HAVE_LINUX_FS_H #include #elif HAVE_SYS_MOUNT_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_DIRECT_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef HAVE_FCHDIR #error fchdir function required. #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #if defined(__hpux) && !defined(HAVE_DIRFD) #define dirfd(x) ((x)->__dd_fd) #define HAVE_DIRFD #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ /* * TODO: * 1) Loop checking. * 3) Arbitrary logical traversals by closing/reopening intermediate fds. */ struct restore_time { const char *name; time_t mtime; long mtime_nsec; time_t atime; long atime_nsec; mode_t filetype; int noatime; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; struct archive_string name; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to return back to the parent of a symlink. */ int symlink_parent_fd; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; int noatime; #if defined(USE_READDIR_R) size_t name_max; #endif long incr_xfer_size; long max_xfer_size; long min_xfer_size; long xfer_align; /* * Buffer used for reading file contents. */ /* Exactly allocated memory pointer. */ unsigned char *allocation_ptr; /* Pointer adjusted to the filesystem alignment . */ unsigned char *buff; size_t buff_size; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; DIR *d; #define INVALID_DIR_HANDLE NULL struct dirent *de; #if defined(USE_READDIR_R) struct dirent *dirent; size_t dirent_allocated; #endif int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* Dynamically-sized buffer for holding path */ struct archive_string path; /* Last path element */ const char *basename; /* Leading dir length */ size_t dirname_length; int depth; int openCount; int maxOpenCount; int initial_dir_fd; int working_dir_fd; struct stat lst; struct stat st; int descend; int nlink; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesystem; int entry_fd; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; unsigned char *entry_buff; size_t entry_buff_size; }; /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define onWorkingDir 64 /* We are on the working dir where we are * reading directory entry at this time. */ #define needsRestoreTimes 128 #define onInitialDir 256 /* We are on the initial dir. */ static int tree_dir_next_posix(struct tree *t); #ifdef HAVE_DIRENT_D_NAMLEN /* BSD extension; avoids need for a strlen() call. */ #define D_NAMELEN(dp) (dp)->d_namlen #else #define D_NAMELEN(dp) (strlen((dp)->d_name)) #endif /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const char *, int, int); static struct tree *tree_reopen(struct tree *, const char *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const char *, int, int64_t, int64_t, struct restore_time *); static int tree_enter_initial_dir(struct tree *); static int tree_enter_working_dir(struct tree *); static int tree_current_dir_fd(struct tree *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * * TODO: On platforms that support it, use openat()-style operations * to eliminate the chdir() operations entirely while still supporting * arbitrarily deep traversals. This makes access_path troublesome to * support, of course, which means we'll need a rich enough interface * that clients can function without it. (In particular, we'll need * tree_current_open() that returns an open file descriptor.) * */ static const char *tree_current_path(struct tree *); static const char *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const struct stat *tree_current_stat(struct tree *); static const struct stat *tree_current_lstat(struct tree *); static int tree_current_is_symblic_link_target(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const struct stat *); static int _archive_read_disk_open(struct archive *, const char *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); static int tree_dup(int); static const struct archive_vtable archive_read_disk_vtable = { .archive_free = _archive_read_free, .archive_close = _archive_read_close, .archive_read_data_block = _archive_read_data_block, .archive_read_next_header = _archive_read_next_header, .archive_read_next_header2 = _archive_read_next_header2, }; const char * archive_read_disk_gname(struct archive *_a, la_int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, la_int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, la_int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, la_int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = &archive_read_disk_vtable; a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->flags = ARCHIVE_READDISK_MAC_COPYFILE; a->open_on_current_dir = open_on_current_dir; a->tree_current_dir_fd = tree_current_dir_fd; a->tree_enter_working_dir = tree_enter_working_dir; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); #ifdef HAVE_UTIMES a->flags |= ARCHIVE_READDISK_RESTORE_ATIME; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); #else /* Display warning and unset flag */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore access time on this system"); a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME; return (ARCHIVE_WARN); #endif } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); a->flags = flags; if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } /* * Allocate memory for the reading buffer adjusted to the filesystem * alignment. */ static int setup_suitable_read_buffer(struct archive_read_disk *a) { struct tree *t = a->tree; struct filesystem *cf = t->current_filesystem; size_t asize; size_t s; if (cf->allocation_ptr == NULL) { /* If we couldn't get a filesystem alignment, * we use 4096 as default value but we won't use * O_DIRECT to open() and openat() operations. */ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align; if (cf->max_xfer_size != -1) asize = cf->max_xfer_size + xfer_align; else { long incr = cf->incr_xfer_size; /* Some platform does not set a proper value to * incr_xfer_size.*/ if (incr < 0) incr = cf->min_xfer_size; if (cf->min_xfer_size < 0) { incr = xfer_align; asize = xfer_align; } else asize = cf->min_xfer_size; /* Increase a buffer size up to 64K bytes in * a proper increment size. */ while (asize < 1024*64) asize += incr; /* Take a margin to adjust to the filesystem * alignment. */ asize += xfer_align; } cf->allocation_ptr = malloc(asize); if (cf->allocation_ptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* * Calculate proper address for the filesystem. */ s = (uintptr_t)cf->allocation_ptr; s %= xfer_align; if (s > 0) s = xfer_align - s; /* * Set a read buffer pointer in the proper alignment of * the current filesystem. */ cf->buff = cf->allocation_ptr + s; cf->buff_size = asize - xfer_align; } return (ARCHIVE_OK); } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; int r; ssize_t bytes; int64_t sparse_bytes; size_t buffbytes; int empty_sparse_region = 0; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Open the current file. */ if (t->entry_fd < 0) { int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. * * Carefully consider this to be enabled. */ #if defined(O_DIRECT) && 0/* Disabled for now */ if (t->current_filesystem->xfer_align != -1 && t->nlink == 1) flags |= O_DIRECT; #endif #if defined(O_NOATIME) /* * Linux has O_NOATIME flag; use it if we need. */ if ((t->flags & needsRestoreTimes) != 0 && t->restore_time.noatime == 0) flags |= O_NOATIME; #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, * if successful, set 1 to t->restore_time.noatime * not to restore an atime of the file later. * if failed by EPERM, retry it without O_NOATIME flag. */ if (flags & O_NOATIME) { if (t->entry_fd >= 0) t->restore_time.noatime = 1; else if (errno == EPERM) flags &= ~O_NOATIME; } #endif if (t->entry_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't open %s", tree_current_path(t)); r = ARCHIVE_FAILED; tree_enter_initial_dir(t); goto abort_read_data; } tree_enter_initial_dir(t); } /* * Allocate read buffer if not allocated. */ if (t->current_filesystem->allocation_ptr == NULL) { r = setup_suitable_read_buffer(a); if (r != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } t->entry_buff = t->current_filesystem->buff; t->entry_buff_size = t->current_filesystem->buff_size; buffbytes = t->entry_buff_size; if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; if (t->current_sparse->length == 0) empty_sparse_region = 1; /* * Skip hole. * TODO: Should we consider t->current_filesystem->xfer_align? */ if (t->current_sparse->offset > t->entry_total) { if (lseek(t->entry_fd, (off_t)t->current_sparse->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } sparse_bytes = t->current_sparse->offset - t->entry_total; t->entry_remaining_bytes -= sparse_bytes; t->entry_total += sparse_bytes; } /* * Read file contents. */ if (buffbytes > 0) { bytes = read(t->entry_fd, t->entry_buff, buffbytes); if (bytes < 0) { archive_set_error(&a->archive, errno, "Read error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } else bytes = 0; /* * Return an EOF unless we've read a leading empty sparse region, which * is used to represent fully-sparse files. */ if (bytes == 0 && !empty_sparse_region) { /* Get EOF */ t->entry_eof = 1; r = ARCHIVE_EOF; goto abort_read_data; } *buff = t->entry_buff; *size = bytes; *offset = t->entry_total; t->entry_total += bytes; t->entry_remaining_bytes -= bytes; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; t->entry_eof = 1; } t->current_sparse->offset += bytes; t->current_sparse->length -= bytes; if (t->current_sparse->length == 0 && !t->entry_eof) t->current_sparse++; return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fd >= 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; int delayed, delayed_errno, descend, r; struct archive_string delayed_str; delayed = ARCHIVE_OK; delayed_errno = 0; archive_string_init(&delayed_str); st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%s: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Couldn't visit directory", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); case 0: tree_enter_initial_dir(t); return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { if (errno == ENOENT && t->depth > 0) { delayed = ARCHIVE_WARN; delayed_errno = errno; if (delayed_str.length == 0) { archive_string_sprintf(&delayed_str, "%s", tree_current_path(t)); } else { archive_string_sprintf(&delayed_str, " %s", tree_current_path(t)); } } else { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); } } break; } } while (lst == NULL); #ifdef __APPLE__ if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { /* If we're using copyfile(), ignore "._XXX" files. */ const char *bname = strrchr(tree_current_path(t), '/'); if (bname == NULL) bname = tree_current_path(t); else ++bname; if (bname[0] == '.' && bname[1] == '_') return (ARCHIVE_RETRY); } #endif archive_entry_copy_pathname(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) { if (t->initial_filesystem_id != t->current_filesystem_id) descend = 0; } t->descend = descend; /* * Honor nodump flag. * If the file is marked with nodump flag, do not return this entry. */ if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) if (st->st_flags & UF_NODUMP) return (ARCHIVE_RETRY); #elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { int stflags; t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { r = ioctl(t->entry_fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &stflags); #ifdef FS_NODUMP_FL if (r == 0 && (stflags & FS_NODUMP_FL) != 0) #else if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) #endif return (ARCHIVE_RETRY); } } #endif } archive_entry_copy_stat(entry, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, * especially, invoking a callback. */ t->restore_time.mtime = archive_entry_mtime(entry); t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry); t->restore_time.atime = archive_entry_atime(entry); t->restore_time.atime_nsec = archive_entry_atime_nsec(entry); t->restore_time.filetype = archive_entry_filetype(entry); t->restore_time.noatime = t->current_filesystem->noatime; /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } /* * Populate the archive_entry with metadata from the disk. */ archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); if (r == ARCHIVE_OK) { r = delayed; if (r != ARCHIVE_OK) { archive_string_sprintf(&delayed_str, ": %s", "File removed before we read it"); archive_set_error(&(a->archive), delayed_errno, "%s", delayed_str.s); } } archive_string_free(&delayed_str); return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } archive_entry_clear(entry); for (;;) { r = next_entry(a, t, entry); if (t->entry_fd >= 0) { close(t->entry_fd); t->entry_fd = -1; } if (r == ARCHIVE_RETRY) { archive_entry_clear(entry); continue; } break; } /* Return to the initial directory. */ tree_enter_initial_dir(t); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: /* Overwrite the sourcepath based on the initial directory. */ archive_entry_copy_sourcepath(entry, tree_current_path(t)); t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->nlink = archive_entry_nlink(entry); t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); t->sparse_list[i].offset = offset; t->sparse_list[i].length = length; } if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = archive_entry_size(entry); } else { t->sparse_list[i].offset = archive_entry_size(entry); t->sparse_list[i].length = 0; } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (!archive_read_disk_can_descend(_a)) return (ARCHIVE_OK); /* * We must not treat the initial specified path as a physical dir, * because if we do then we will try and ascend out of it by opening * ".." which is (a) wrong and (b) causes spurious permissions errors * if ".." is not readable by us. Instead, treat it as if it were a * symlink. (This uses an extra fd, but it can only happen once at the * top level of a traverse.) But we can't necessarily assume t->st is * valid here (though t->lst is), which complicates the logic a * little. */ if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); if (t->stack->parent->parent != NULL) t->stack->flags |= isDir; else t->stack->flags |= isDirLink; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); return (_archive_read_disk_open(_a, pathname)); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_string path; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); /* Make a char string from a wchar_t string. */ archive_string_init(&path); if (archive_string_append_from_wcs(&path, pathname, wcslen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a char string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open(_a, path.s); archive_string_free(&path); return (ret); } static int _archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) a->tree = tree_reopen(a->tree, pathname, a->flags & ARCHIVE_READDISK_RESTORE_ATIME); else a->tree = tree_open(pathname, a->symlink_mode, a->flags & ARCHIVE_READDISK_RESTORE_ATIME); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * This is the new filesystem which we have to generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; t->current_filesystem->allocation_ptr = NULL; t->current_filesystem->buff = NULL; /* Setup the current filesystem properties which depend on * platform specific. */ return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } #if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN) static int get_xfer_size(struct tree *t, int fd, const char *path) { t->current_filesystem->xfer_align = -1; errno = 0; if (fd >= 0) { t->current_filesystem->incr_xfer_size = fpathconf(fd, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = fpathconf(fd, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = fpathconf(fd, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = fpathconf(fd, _PC_REC_XFER_ALIGN); } else if (path != NULL) { t->current_filesystem->incr_xfer_size = pathconf(path, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = pathconf(path, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = pathconf(path, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = pathconf(path, _PC_REC_XFER_ALIGN); } /* At least we need an alignment size. */ if (t->current_filesystem->xfer_align == -1) return ((errno == EINVAL)?1:-1); else return (0); } #else static int get_xfer_size(struct tree *t, int fd, const char *path) { (void)t; /* UNUSED */ (void)fd; /* UNUSED */ (void)path; /* UNUSED */ return (1);/* Not supported */ } #endif #if defined(HAVE_STATVFS) static inline __LA_UNUSED void set_statvfs_transfer_size(struct filesystem *fs, const struct statvfs *sfs) { fs->xfer_align = sfs->f_frsize > 0 ? (long)sfs->f_frsize : -1; fs->max_xfer_size = -1; #if defined(HAVE_STRUCT_STATVFS_F_IOSIZE) fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1; fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1; #else fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1; fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1; #endif } #endif #if defined(HAVE_STRUCT_STATFS) static inline __LA_UNUSED void set_statfs_transfer_size(struct filesystem *fs, const struct statfs *sfs) { fs->xfer_align = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1; fs->max_xfer_size = -1; #if defined(HAVE_STRUCT_STATFS_F_IOSIZE) fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1; fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1; #else fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1; fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1; #endif } #endif #if defined(HAVE_STRUCT_STATFS) && defined(HAVE_STATFS) && \ defined(HAVE_FSTATFS) && defined(MNT_LOCAL) && !defined(ST_LOCAL) /* * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) /* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make * this accurate; some platforms have both and we need the one that's * used by getvfsbyname() * * Then the following would become: * #if defined(GETVFSBYNAME_ARG_TYPE) * GETVFSBYNAME_ARG_TYPE vfc; * #endif */ # if defined(HAVE_STRUCT_XVFSCONF) struct xvfsconf vfc; # else struct vfsconf vfc; # endif #endif int r, xr = 0; #if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX) long nm; #endif t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); } if (r == -1 || xr == -1) { archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ set_statfs_transfer_size(t->current_filesystem, &sfs); } if (sfs.f_flags & MNT_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) r = getvfsbyname(sfs.f_fstypename, &vfc); if (r == -1) { archive_set_error(&a->archive, errno, "getvfsbyname failed"); return (ARCHIVE_FAILED); } if (vfc.vfc_flags & VFCF_SYNTHETIC) t->current_filesystem->synthetic = 1; else t->current_filesystem->synthetic = 0; #endif #if defined(MNT_NOATIME) if (sfs.f_flags & MNT_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; #else # if defined(_PC_NAME_MAX) /* Mac OS X does not have f_namemax in struct statfs. */ if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); # else nm = -1; # endif if (nm == -1) t->current_filesystem->name_max = NAME_MAX; else t->current_filesystem->name_max = nm; #endif + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL) /* * Gather current filesystem properties on NetBSD */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs svfs; int r, xr = 0; t->current_filesystem->synthetic = -1; if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } if (tree_current_is_symblic_link_target(t)) { r = statvfs(tree_current_access_path(t), &svfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &svfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else r = statvfs(".", &svfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN * for pathconf() function. */ set_statvfs_transfer_size(t->current_filesystem, &svfs); } if (svfs.f_flag & ST_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(ST_NOATIME) if (svfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; /* Set maximum filename length. */ t->current_filesystem->name_max = svfs.f_namemax; return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\ defined(HAVE_STATFS) && defined(HAVE_FSTATFS) /* * Note: statfs is deprecated since LSB 3.2 */ #ifndef CIFS_SUPER_MAGIC #define CIFS_SUPER_MAGIC 0xFF534D42 #endif #ifndef DEVFS_SUPER_MAGIC #define DEVFS_SUPER_MAGIC 0x1373 #endif /* * Gather current filesystem properties on Linux */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_STATVFS) struct statvfs svfs; #endif int r, vr = 0, xr = 0; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_FSTATVFS) vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */ #endif r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(tree_current_access_path(t), &svfs); #endif r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATFS #if defined(HAVE_FSTATVFS) vr = fstatvfs(tree_current_dir_fd(t), &svfs); #endif r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(".", &svfs); #endif r = statfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1 || vr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ #if defined(HAVE_STATVFS) set_statvfs_transfer_size(t->current_filesystem, &svfs); #else set_statfs_transfer_size(t->current_filesystem, &sfs); #endif } switch (sfs.f_type) { case AFS_SUPER_MAGIC: case CIFS_SUPER_MAGIC: case CODA_SUPER_MAGIC: case NCP_SUPER_MAGIC:/* NetWare */ case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: t->current_filesystem->remote = 1; t->current_filesystem->synthetic = 0; break; case DEVFS_SUPER_MAGIC: case PROC_SUPER_MAGIC: case USBDEVICE_SUPER_MAGIC: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 1; break; default: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 0; break; } #if defined(ST_NOATIME) #if defined(HAVE_STATVFS) if (svfs.f_flag & ST_NOATIME) #else if (sfs.f_flags & ST_NOATIME) #endif t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ +#if defined(HAVE_STATVFS) + t->current_filesystem->name_max = svfs.f_namelen; +#else t->current_filesystem->name_max = sfs.f_namelen; +#endif + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATVFS_H) &&\ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) /* * Gather current filesystem properties on other posix platform. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs svfs; int r, xr = 0; t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatvfs(fd, &svfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(tree_current_access_path(t), &svfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &svfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(".", &svfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ set_statvfs_transfer_size(t->current_filesystem, &svfs); } #if defined(ST_NOATIME) if (svfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = svfs.f_namemax; + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif return (ARCHIVE_OK); } #else /* * Generic: Gather current filesystem properties. * TODO: Is this generic function really needed? */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; #if defined(_PC_NAME_MAX) && defined(USE_READDIR_R) long nm; #endif t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ t->current_filesystem->noatime = 0; (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */ t->current_filesystem->xfer_align = -1;/* Unknown */ t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = -1; t->current_filesystem->incr_xfer_size = -1; #if defined(USE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) # endif /* _PC_NAME_MAX */ /* * Some systems (HP-UX or others?) incorrectly defined * NAME_MAX macro to be a smaller value. */ # if defined(NAME_MAX) && NAME_MAX >= 255 t->current_filesystem->name_max = NAME_MAX; # else /* No way to get a trusted value of maximum filename * length. */ t->current_filesystem->name_max = PATH_MAX; # endif /* NAME_MAX */ # if defined(_PC_NAME_MAX) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #endif static int close_and_restore_time(int fd, struct tree *t, struct restore_time *rt) { #ifndef HAVE_UTIMES (void)t; /* UNUSED */ (void)rt; /* UNUSED */ return (close(fd)); #else #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) struct timespec timespecs[2]; #endif struct timeval times[2]; if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) { if (fd >= 0) return (close(fd)); else return (0); } #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) timespecs[1].tv_sec = rt->mtime; timespecs[1].tv_nsec = rt->mtime_nsec; timespecs[0].tv_sec = rt->atime; timespecs[0].tv_nsec = rt->atime_nsec; /* futimens() is defined in POSIX.1-2008. */ if (futimens(fd, timespecs) == 0) return (close(fd)); #endif times[1].tv_sec = rt->mtime; times[1].tv_usec = rt->mtime_nsec / 1000; times[0].tv_sec = rt->atime; times[0].tv_usec = rt->atime_nsec / 1000; #if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (futimes(fd, times) == 0) return (close(fd)); #endif close(fd); #if defined(HAVE_FUTIMESAT) if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0) return (0); #endif #ifdef HAVE_LUTIMES if (lutimes(rt->name, times) != 0) #else if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0) #endif return (-1); #endif return (0); } static int open_on_current_dir(struct tree *t, const char *path, int flags) { #ifdef HAVE_OPENAT return (openat(tree_current_dir_fd(t), path, flags)); #else if (tree_enter_working_dir(t) != 0) return (-1); return (open(path, flags)); #endif } static int tree_dup(int fd) { int new_fd; #ifdef F_DUPFD_CLOEXEC static volatile int can_dupfd_cloexec = 1; if (can_dupfd_cloexec) { new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd != -1) return (new_fd); /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, * but it cannot be used. So we have to try dup(). */ /* We won't try F_DUPFD_CLOEXEC. */ can_dupfd_cloexec = 0; } #endif /* F_DUPFD_CLOEXEC */ new_fd = dup(fd); __archive_ensure_cloexec_flag(new_fd); return (new_fd); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const char *path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = calloc(1, sizeof(*te)); if (te == NULL) __archive_errx(1, "Out of memory"); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); te->symlink_parent_fd = -1; archive_strcpy(&te->name, path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->restore_time.name = te->name.s; if (rt != NULL) { te->restore_time.mtime = rt->mtime; te->restore_time.mtime_nsec = rt->mtime_nsec; te->restore_time.atime = rt->atime; te->restore_time.atime_nsec = rt->atime_nsec; te->restore_time.filetype = rt->filetype; te->restore_time.noatime = rt->noatime; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const char *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == '/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_string_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/') archive_strappend_char(&t->path, '/'); t->basename = t->path.s + archive_strlen(&t->path); archive_strncat(&t->path, name, name_length); t->restore_time.name = t->basename; } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const char *path, int symlink_mode, int restore_time) { struct tree *t; if ((t = calloc(1, sizeof(*t))) == NULL) return (NULL); archive_string_init(&t->path); archive_string_ensure(&t->path, 31); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { #if defined(O_PATH) /* Linux */ const int o_flag = O_PATH; #elif defined(O_SEARCH) /* SunOS */ const int o_flag = O_SEARCH; #elif defined(__FreeBSD__) && defined(O_EXEC) /* FreeBSD */ const int o_flag = O_EXEC; #endif t->flags = (restore_time != 0)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; t->tree_errno = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_DIR_HANDLE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&t->path); t->entry_fd = -1; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* First item is set up a lot like a symlink traversal. */ tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); #if defined(O_PATH) || defined(O_SEARCH) || \ (defined(__FreeBSD__) && defined(O_EXEC)) /* * Most likely reason to fail opening "." is that it's not readable, * so try again for execute. The consequences of not opening this are * unhelpful and unnecessary errors later. */ if (t->initial_dir_fd < 0) t->initial_dir_fd = open(".", o_flag | O_CLOEXEC); #endif __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } static int tree_descent(struct tree *t) { int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; } else { t->depth++; /* If it is a link, set up fd for the ascent. */ if (t->stack->flags & isDirLink) { t->stack->symlink_parent_fd = t->working_dir_fd; t->openCount++; if (t->openCount > t->maxOpenCount) t->maxOpenCount = t->openCount; } else close(t->working_dir_fd); /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; } return (r); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; int new_fd, r = 0, prev_dir_fd; te = t->stack; prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; else { new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(new_fd); } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; } else { /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; /* Current directory has been changed, we should * close an fd of previous working directory. */ close_and_restore_time(prev_dir_fd, t, &te->restore_time); if (te->flags & isDirLink) { t->openCount--; te->symlink_parent_fd = -1; } t->depth--; } return (r); } /* * Return to the initial directory where tree_open() was performed. */ static int tree_enter_initial_dir(struct tree *t) { int r = 0; if ((t->flags & onInitialDir) == 0) { r = fchdir(t->initial_dir_fd); if (r == 0) { t->flags &= ~onWorkingDir; t->flags |= onInitialDir; } } return (r); } /* * Restore working directory of directory traversals. */ static int tree_enter_working_dir(struct tree *t) { int r = 0; /* * Change the current directory if really needed. * Sometimes this is unneeded when we did not do * descent. */ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) { r = fchdir(t->working_dir_fd); if (r == 0) { t->flags &= ~onInitialDir; t->flags |= onWorkingDir; } } return (r); } static int tree_current_dir_fd(struct tree *t) { return (t->working_dir_fd); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; while (t->basename[0] == '/') t->basename++; archive_string_free(&te->name); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_DIR_HANDLE) { r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); /* t->dirname_length = t->path_length; */ /* tree_pop(t); */ t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_posix(struct tree *t) { int r; const char *name; size_t namelen; if (t->d == NULL) { #if defined(USE_READDIR_R) size_t dirent_size; #endif #if defined(HAVE_FDOPENDIR) t->d = fdopendir(tree_dup(t->working_dir_fd)); #else /* HAVE_FDOPENDIR */ if (tree_enter_working_dir(t) == 0) { t->d = opendir("."); #ifdef HAVE_DIRFD __archive_ensure_cloexec_flag(dirfd(t->d)); #endif } #endif /* HAVE_FDOPENDIR */ if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } #if defined(USE_READDIR_R) dirent_size = offsetof(struct dirent, d_name) + t->filesystem_table[t->current->filesystem_id].name_max + 1; if (t->dirent == NULL || t->dirent_allocated < dirent_size) { free(t->dirent); t->dirent = malloc(dirent_size); if (t->dirent == NULL) { closedir(t->d); t->d = INVALID_DIR_HANDLE; (void)tree_ascend(t); tree_pop(t); t->tree_errno = ENOMEM; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->dirent_allocated = dirent_size; } #endif /* USE_READDIR_R */ } for (;;) { errno = 0; #if defined(USE_READDIR_R) r = readdir_r(t->d, t->dirent, &t->de); #ifdef _AIX /* Note: According to the man page, return value 9 indicates * that the readdir_r was not successful and the error code * is set to the global errno variable. And then if the end * of directory entries was reached, the return value is 9 * and the third parameter is set to NULL and errno is * unchanged. */ if (r == 9) r = errno; #endif /* _AIX */ if (r != 0 || t->de == NULL) { #else t->de = readdir(t->d); if (t->de == NULL) { r = errno; #endif closedir(t->d); t->d = INVALID_DIR_HANDLE; if (r != 0) { t->tree_errno = r; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } else return (0); } name = t->de->d_name; namelen = D_NAMELEN(t->de); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == '.' && name[1] == '\0') continue; if (name[0] == '.' && name[1] == '.' && name[2] == '\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } /* * Get the stat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->st, 0) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (la_stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->lst, AT_SYMLINK_NOFOLLOW) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; +#ifdef HAVE_LSTAT if (lstat(tree_current_access_path(t), &t->lst) != 0) +#else + if (la_stat(tree_current_access_path(t), &t->lst) != 0) +#endif #endif return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { const struct stat *st; /* * If we already have lstat() info, then try some * cheap tests to determine if this is a dir. */ if (t->flags & hasLstat) { /* If lstat() says it's a dir, it must be a dir. */ st = tree_current_lstat(t); if (st == NULL) return 0; if (S_ISDIR(st->st_mode)) return 1; /* Not a dir; might be a link to a dir. */ /* If it's not a link, then it's not a link to a dir. */ if (!S_ISLNK(st->st_mode)) return 0; /* * It's a link, but we don't know what it's a link to, * so we'll have to use stat(). */ } st = tree_current_stat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { const struct stat *st; /* * If stat() says it isn't a dir, then it's not a dir. * If stat() data is cached, this check is free, so do it first. */ if (t->flags & hasStat) { st = tree_current_stat(t); if (st == NULL) return (0); if (!S_ISDIR(st->st_mode)) return (0); } /* * Either stat() said it was a dir (in which case, we have * to determine whether it's really a link to a dir) or * stat() info wasn't available. So we use lstat(), which * hopefully is already cached. */ st = tree_current_lstat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const struct stat *st) { struct tree_entry *te; for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == (int64_t)st->st_dev && te->ino == (int64_t)st->st_ino) return (1); } return (0); } /* * Test whether the current file is symbolic link target and * on the other filesystem. */ static int tree_current_is_symblic_link_target(struct tree *t) { static const struct stat *lst, *st; lst = tree_current_lstat(t); st = tree_current_stat(t); return (st != NULL && lst != NULL && (int64_t)st->st_dev == t->current_filesystem->dev && st->st_dev != lst->st_dev); } /* * Return the access path for the entry just returned from tree_next(). */ static const char * tree_current_access_path(struct tree *t) { return (t->basename); } /* * Return the full path for the entry just returned from tree_next(). */ static const char * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } /* Close the handle of readdir(). */ if (t->d != INVALID_DIR_HANDLE) { closedir(t->d); t->d = INVALID_DIR_HANDLE; } /* Release anything remaining in the stack. */ while (t->stack != NULL) { if (t->stack->flags & isDirLink) close(t->stack->symlink_parent_fd); tree_pop(t); } if (t->working_dir_fd >= 0) { close(t->working_dir_fd); t->working_dir_fd = -1; } if (t->initial_dir_fd >= 0) { close(t->initial_dir_fd); t->initial_dir_fd = -1; } } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_string_free(&t->path); #if defined(USE_READDIR_R) free(t->dirent); #endif free(t->sparse_list); for (i = 0; i < t->max_filesystem_id; i++) free(t->filesystem_table[i].allocation_ptr); free(t->filesystem_table); free(t); } #endif diff --git a/contrib/libarchive/libarchive/archive_read_open_file.c b/contrib/libarchive/libarchive/archive_read_open_file.c index 6001dfd47780..c9dcbb1cfec1 100644 --- a/contrib/libarchive/libarchive/archive_read_open_file.c +++ b/contrib/libarchive/libarchive/archive_read_open_file.c @@ -1,180 +1,180 @@ /*- * 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 read_FILE_data { FILE *f; size_t block_size; void *buffer; char can_skip; }; static int file_close(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); static int64_t file_skip(struct archive *, void *, int64_t request); int archive_read_open_FILE(struct archive *a, FILE *f) { struct stat st; struct read_FILE_data *mine; size_t block_size = 128 * 1024; void *b; archive_clear_error(a); mine = (struct read_FILE_data *)malloc(sizeof(*mine)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } mine->block_size = block_size; mine->buffer = b; mine->f = f; /* * If we can't fstat() the file, it may just be that it's not * a file. (On some platforms, FILE * objects can wrap I/O * streams that don't support fileno()). As a result, fileno() * should be used cautiously.) */ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* Enable the seek optimization only for regular files. */ mine->can_skip = 1; } else mine->can_skip = 0; #if defined(__CYGWIN__) || defined(_WIN32) setmode(fileno(mine->f), O_BINARY); #endif archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); archive_read_set_close_callback(a, file_close); archive_read_set_callback_data(a, mine); return (archive_read_open1(a)); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; size_t bytes_read; *buff = mine->buffer; bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); if (bytes_read < mine->block_size && ferror(mine->f)) { archive_set_error(a, errno, "Error reading file"); } return (bytes_read); } static int64_t file_skip(struct archive *a, void *client_data, int64_t request) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; #if HAVE_FSEEKO off_t skip = (off_t)request; #elif HAVE__FSEEKI64 int64_t skip = request; #else long skip = (long)request; #endif int skip_bits = sizeof(skip) * 8 - 1; (void)a; /* UNUSED */ /* * If we can't skip, return 0 as the amount we did step and * the caller will work around by reading and discarding. */ if (!mine->can_skip) return (0); if (request == 0) return (0); /* If request is too big for a long or an off_t, reduce it. */ if (sizeof(request) > sizeof(skip)) { int64_t max_skip = (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1; if (request > max_skip) skip = max_skip; } #ifdef __ANDROID__ /* fileno() isn't safe on all platforms ... see above. */ if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0) -#elif HAVE_FSEEKO - if (fseeko(mine->f, skip, SEEK_CUR) != 0) #elif HAVE__FSEEKI64 if (_fseeki64(mine->f, skip, SEEK_CUR) != 0) +#elif HAVE_FSEEKO + if (fseeko(mine->f, skip, SEEK_CUR) != 0) #else if (fseek(mine->f, skip, SEEK_CUR) != 0) #endif { mine->can_skip = 0; return (0); } return (request); } static int file_close(struct archive *a, void *client_data) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ free(mine->buffer); free(mine); return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_set_options.3 b/contrib/libarchive/libarchive/archive_read_set_options.3 index b2db4cbcb893..162b79da48f4 100644 --- a/contrib/libarchive/libarchive/archive_read_set_options.3 +++ b/contrib/libarchive/libarchive/archive_read_set_options.3 @@ -1,271 +1,292 @@ .\" 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 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 January 31, 2020 .Dt ARCHIVE_READ_OPTIONS 3 .Os .Sh NAME .Nm archive_read_set_filter_option , .Nm archive_read_set_format_option , .Nm archive_read_set_option , .Nm archive_read_set_options .Nd functions controlling options for reading archives .\" .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .Ft int .Fo archive_read_set_filter_option .Fa "struct archive *" .Fa "const char *module" .Fa "const char *option" .Fa "const char *value" .Fc .Ft int .Fo archive_read_set_format_option .Fa "struct archive *" .Fa "const char *module" .Fa "const char *option" .Fa "const char *value" .Fc .Ft int .Fo archive_read_set_option .Fa "struct archive *" .Fa "const char *module" .Fa "const char *option" .Fa "const char *value" .Fc .Ft int .Fo archive_read_set_options .Fa "struct archive *" .Fa "const char *options" .Fc .Sh DESCRIPTION These functions provide a way for libarchive clients to configure specific read modules. .Bl -tag -width indent .It Xo .Fn archive_read_set_filter_option , .Fn archive_read_set_format_option .Xc Specifies an option that will be passed to currently-registered filters (including decompression filters) or format readers. .Pp If .Ar option and .Ar value are both .Dv NULL , these functions will do nothing and .Cm ARCHIVE_OK will be returned. If .Ar option is .Dv NULL but .Ar value is not, these functions will do nothing and .Cm ARCHIVE_FAILED will be returned. .Pp If .Ar module is not .Dv NULL , .Ar option and .Ar value will be provided to the filter or reader named .Ar module . The return value will be that of the module. If there is no such module, .Cm ARCHIVE_FAILED will be returned. .Pp If .Ar module is .Dv NULL , .Ar option and .Ar value will be provided to every registered module. If any module returns .Cm ARCHIVE_FATAL , this value will be returned immediately. Otherwise, .Cm ARCHIVE_OK will be returned if any module accepts the option, and .Cm ARCHIVE_FAILED in all other cases. .\" .It Xo .Fn archive_read_set_option .Xc Calls .Fn archive_read_set_format_option , then .Fn archive_read_set_filter_option . If either function returns .Cm ARCHIVE_FATAL , .Cm ARCHIVE_FATAL will be returned immediately. Otherwise, greater of the two values will be returned. .\" .It Xo .Fn archive_read_set_options .Xc .Ar options is a comma-separated list of options. If .Ar options is .Dv NULL or empty, .Cm ARCHIVE_OK will be returned immediately. .Pp Calls .Fn archive_read_set_option with each option in turn. If any .Fn archive_read_set_option call returns .Cm ARCHIVE_FATAL , .Cm ARCHIVE_FATAL will be returned immediately. .Pp Individual options have one of the following forms: .Bl -tag -compact -width indent .It Ar option=value The option/value pair will be provided to every module. Modules that do not accept an option with this name will ignore it. .It Ar option The option will be provided to every module with a value of .Dq 1 . .It Ar !option The option will be provided to every module with a NULL value. .It Ar module:option=value , Ar module:option , Ar module:!option As above, but the corresponding option and value will be provided only to modules whose name matches .Ar module . .El .El .\" .Sh OPTIONS .Bl -tag -compact -width indent .It Format cab .Bl -tag -compact -width indent .It Cm hdrcharset The value is used as a character set name that will be used when translating file names. .El .It Format cpio .Bl -tag -compact -width indent .It Cm compat-2x Libarchive 2.x incorrectly encoded Unicode filenames on some platforms. This option mimics the libarchive 2.x filename handling so that such archives can be read correctly. .It Cm hdrcharset The value is used as a character set name that will be used when translating file names. .It Cm pwb When reading a binary CPIO archive, assume that it is in the original PWB cpio format, and handle file mode bits accordingly. The default is to assume v7 format. .El .It Format iso9660 .Bl -tag -compact -width indent .It Cm joliet Support Joliet extensions. Defaults to enabled, use .Cm !joliet to disable. .It Cm rockridge Support RockRidge extensions. Defaults to enabled, use .Cm !rockridge to disable. .El .It Format lha .Bl -tag -compact -width indent .It Cm hdrcharset The value is used as a character set name that will be used when translating file names. .El .It Format mtree .Bl -tag -compact -width indent .It Cm checkfs Allow reading information missing from the mtree from the file system. Disabled by default. .El .It Format rar .Bl -tag -compact -width indent .It Cm hdrcharset The value is used as a character set name that will be used when translating file names. .El .It Format tar .Bl -tag -compact -width indent .It Cm compat-2x Libarchive 2.x incorrectly encoded Unicode filenames on some platforms. This option mimics the libarchive 2.x filename handling so that such archives can be read correctly. .It Cm hdrcharset The value is used as a character set name that will be used when translating file names. .It Cm mac-ext Support Mac OS metadata extension that records data in special files beginning with a period and underscore. Defaults to enabled on Mac OS, disabled on other platforms. Use .Cm !mac-ext to disable. .It Cm read_concatenated_archives Ignore zeroed blocks in the archive, which occurs when multiple tar archives have been concatenated together. Without this option, only the contents of the first concatenated archive would be read. .El +.It Format zip +.Bl -tag -compact -width indent +.It Cm compat-2x +Libarchive 2.x incorrectly encoded Unicode filenames on +some platforms. +This option mimics the libarchive 2.x filename handling +so that such archives can be read correctly. +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.It Cm ignorecrc32 +Skip the CRC32 check. +Mostly used for testing. +.It Cm mac-ext +Support Mac OS metadata extension that records data in special +files beginning with a period and underscore. +Defaults to enabled on Mac OS, disabled on other platforms. +Use +.Cm !mac-ext +to disable. +.El .El .\" .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_read 3 , .Xr archive_write_set_options 3 , .Xr libarchive 3 diff --git a/contrib/libarchive/libarchive/archive_read_support_filter_zstd.c b/contrib/libarchive/libarchive/archive_read_support_filter_zstd.c index 39f25f1bf88e..1959b5ac3991 100644 --- a/contrib/libarchive/libarchive/archive_read_support_filter_zstd.c +++ b/contrib/libarchive/libarchive/archive_read_support_filter_zstd.c @@ -1,297 +1,297 @@ /*- * Copyright (c) 2009-2011 Sean Purcell * 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_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #if HAVE_ZSTD_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_private.h" #include "archive_read_private.h" #if HAVE_ZSTD_H && HAVE_LIBZSTD struct private_data { ZSTD_DStream *dstream; unsigned char *out_block; size_t out_block_size; int64_t total_out; char in_frame; /* True = in the middle of a zstd frame. */ char eof; /* True = found end of compressed data. */ }; /* Zstd Filter. */ static ssize_t zstd_filter_read(struct archive_read_filter *, const void**); static int zstd_filter_close(struct archive_read_filter *); #endif /* * Note that we can detect zstd compressed files even if we can't decompress * them. (In fact, we like detecting them because we can give better error * messages.) So the bid framework here gets compiled even if no zstd library * is available. */ static int zstd_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int zstd_bidder_init(struct archive_read_filter *); static const struct archive_read_filter_bidder_vtable zstd_bidder_vtable = { .bid = zstd_bidder_bid, .init = zstd_bidder_init, }; int archive_read_support_filter_zstd(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; if (__archive_read_register_bidder(a, NULL, "zstd", &zstd_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); #if HAVE_ZSTD_H && HAVE_LIBZSTD return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external zstd program for zstd decompression"); return (ARCHIVE_WARN); #endif } /* * Test whether we can handle this data. */ static int zstd_bidder_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; ssize_t avail; unsigned prefix; /* Zstd frame magic values */ - const unsigned zstd_magic = 0xFD2FB528U; - const unsigned zstd_magic_skippable_start = 0x184D2A50U; - const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0; + unsigned zstd_magic = 0xFD2FB528U; + unsigned zstd_magic_skippable_start = 0x184D2A50U; + unsigned zstd_magic_skippable_mask = 0xFFFFFFF0; (void) self; /* UNUSED */ buffer = __archive_read_filter_ahead(filter, 4, &avail); if (buffer == NULL) return (0); prefix = archive_le32dec(buffer); if (prefix == zstd_magic) return (32); if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start) return (32); return (0); } #if !(HAVE_ZSTD_H && HAVE_LIBZSTD) /* * If we don't have the library on this system, we can't do the * decompression directly. We can, however, try to run "zstd -d" * in case that's available. */ static int zstd_bidder_init(struct archive_read_filter *self) { int r; r = __archive_read_program(self, "zstd -d -qq"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ self->code = ARCHIVE_FILTER_ZSTD; self->name = "zstd"; return (r); } #else static const struct archive_read_filter_vtable zstd_reader_vtable = { .read = zstd_filter_read, .close = zstd_filter_close, }; /* * Initialize the filter object */ static int zstd_bidder_init(struct archive_read_filter *self) { struct private_data *state; - const size_t out_block_size = ZSTD_DStreamOutSize(); + size_t out_block_size = ZSTD_DStreamOutSize(); void *out_block; ZSTD_DStream *dstream; self->code = ARCHIVE_FILTER_ZSTD; self->name = "zstd"; state = (struct private_data *)calloc(sizeof(*state), 1); out_block = (unsigned char *)malloc(out_block_size); dstream = ZSTD_createDStream(); if (state == NULL || out_block == NULL || dstream == NULL) { free(out_block); free(state); ZSTD_freeDStream(dstream); /* supports free on NULL */ archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for zstd decompression"); return (ARCHIVE_FATAL); } self->data = state; state->out_block_size = out_block_size; state->out_block = out_block; state->dstream = dstream; self->vtable = &zstd_reader_vtable; state->eof = 0; state->in_frame = 0; return (ARCHIVE_OK); } static ssize_t zstd_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; ssize_t avail_in; ZSTD_outBuffer out; ZSTD_inBuffer in; + size_t ret; state = (struct private_data *)self->data; out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 }; /* Try to fill the output buffer. */ while (out.pos < out.size && !state->eof) { if (!state->in_frame) { - const size_t ret = ZSTD_initDStream(state->dstream); + ret = ZSTD_initDStream(state->dstream); if (ZSTD_isError(ret)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Error initializing zstd decompressor: %s", ZSTD_getErrorName(ret)); return (ARCHIVE_FATAL); } } in.src = __archive_read_filter_ahead(self->upstream, 1, &avail_in); if (avail_in < 0) { return avail_in; } if (in.src == NULL && avail_in == 0) { if (!state->in_frame) { /* end of stream */ state->eof = 1; break; } else { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Truncated zstd input"); return (ARCHIVE_FATAL); } } in.size = avail_in; in.pos = 0; { - const size_t ret = - ZSTD_decompressStream(state->dstream, &out, &in); + ret = ZSTD_decompressStream(state->dstream, &out, &in); if (ZSTD_isError(ret)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(ret)); return (ARCHIVE_FATAL); } /* Decompressor made some progress */ __archive_read_filter_consume(self->upstream, in.pos); /* ret guaranteed to be > 0 if frame isn't done yet */ state->in_frame = (ret != 0); } } decompressed = out.pos; state->total_out += decompressed; if (decompressed == 0) *p = NULL; else *p = state->out_block; return (decompressed); } /* * Clean up the decompressor. */ static int zstd_filter_close(struct archive_read_filter *self) { struct private_data *state; state = (struct private_data *)self->data; ZSTD_freeDStream(state->dstream); free(state->out_block); free(state); return (ARCHIVE_OK); } #endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */ diff --git a/contrib/libarchive/libarchive/archive_read_support_format_7zip.c b/contrib/libarchive/libarchive/archive_read_support_format_7zip.c index 0ba4bee358b2..bb595b3e4b07 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_7zip.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_7zip.c @@ -1,3900 +1,4074 @@ /*- * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #ifdef HAVE_ZLIB_H #include #endif +#ifdef HAVE_ZSTD_H +#include +#endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #define _7ZIP_SIGNATURE "7z\xBC\xAF\x27\x1C" #define SFX_MIN_ADDR 0x27000 #define SFX_MAX_ADDR 0x60000 /* * Codec ID */ #define _7Z_COPY 0 #define _7Z_LZMA 0x030101 #define _7Z_LZMA2 0x21 #define _7Z_DEFLATE 0x040108 #define _7Z_BZ2 0x040202 #define _7Z_PPMD 0x030401 #define _7Z_DELTA 0x03 #define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */ #define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */ #define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */ #define _7Z_X86 0x03030103 #define _7Z_X86_BCJ2 0x0303011B #define _7Z_POWERPC 0x03030205 #define _7Z_IA64 0x03030401 #define _7Z_ARM 0x03030501 #define _7Z_ARMTHUMB 0x03030701 +#define _7Z_ARM64 0xa #define _7Z_SPARC 0x03030805 +#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */ + /* * 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 #define kDummy 0x19 struct _7z_digests { unsigned char *defineds; uint32_t *digests; }; struct _7z_folder { uint64_t numCoders; struct _7z_coder { unsigned long codec; uint64_t numInStreams; uint64_t numOutStreams; uint64_t propertiesSize; unsigned char *properties; } *coders; uint64_t numBindPairs; struct { uint64_t inIndex; uint64_t outIndex; } *bindPairs; uint64_t numPackedStreams; uint64_t *packedStreams; uint64_t numInStreams; uint64_t numOutStreams; uint64_t *unPackSize; unsigned char digest_defined; uint32_t digest; uint64_t numUnpackStreams; uint32_t packIndex; /* Unoperated bytes. */ uint64_t skipped_bytes; }; struct _7z_coders_info { uint64_t numFolders; struct _7z_folder *folders; uint64_t dataStreamIndex; }; struct _7z_pack_info { uint64_t pos; uint64_t numPackStreams; uint64_t *sizes; struct _7z_digests digest; /* Calculated from pos and numPackStreams. */ uint64_t *positions; }; struct _7z_substream_info { size_t unpack_streams; uint64_t *unpackSizes; unsigned char *digestsDefined; uint32_t *digests; }; struct _7z_stream_info { struct _7z_pack_info pi; struct _7z_coders_info ci; struct _7z_substream_info ss; }; struct _7z_header_info { uint64_t dataIndex; unsigned char *emptyStreamBools; unsigned char *emptyFileBools; unsigned char *antiBools; unsigned char *attrBools; }; struct _7zip_entry { size_t name_len; unsigned char *utf16name; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) const wchar_t *wname; #endif uint32_t folderIndex; uint32_t ssIndex; 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) time_t mtime; time_t atime; time_t ctime; long mtime_ns; long atime_ns; long ctime_ns; uint32_t mode; uint32_t attr; }; struct _7zip { /* Structural information about the archive. */ struct _7z_stream_info si; int header_is_being_read; int header_is_encoded; uint64_t header_bytes_remaining; unsigned long header_crc32; /* Header offset to check that reading points of the file contents * will not exceed the header. */ uint64_t header_offset; /* Base offset of the archive file for a seek in case reading SFX. */ uint64_t seek_base; /* List of entries */ size_t entries_remaining; uint64_t numFiles; struct _7zip_entry *entries; struct _7zip_entry *entry; unsigned char *entry_names; /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; uint64_t entry_bytes_remaining; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; /* Flags to mark progress of decompression. */ char end_of_entry; /* Uncompressed buffer control. */ #define UBUFF_SIZE (64 * 1024) unsigned char *uncompressed_buffer; unsigned char *uncompressed_buffer_pointer; size_t uncompressed_buffer_size; size_t uncompressed_buffer_bytes_remaining; /* Offset of the compressed data. */ int64_t stream_offset; /* * Decompressing control data. */ unsigned folder_index; uint64_t folder_outbytes_remaining; unsigned pack_stream_index; unsigned pack_stream_remaining; uint64_t pack_stream_inbytes_remaining; size_t pack_stream_bytes_unconsumed; /* The codec information of a folder. */ unsigned long codec; unsigned long codec2; /* * Decompressor controllers. */ /* Decoding LZMA1 and LZMA2 data. */ #ifdef HAVE_LZMA_H lzma_stream lzstream; int lzstream_valid; #endif /* Decoding bzip2 data. */ #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream bzstream; int bzstream_valid; #endif /* Decoding deflate data. */ #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; +#endif + /* Decoding Zstandard data. */ +#if HAVE_ZSTD_H + ZSTD_DStream *zstd_dstream; + int zstdstream_valid; #endif /* Decoding PPMd data. */ int ppmd7_stat; CPpmd7 ppmd7_context; CPpmd7z_RangeDec range_dec; IByteIn bytein; struct { const unsigned char *next_in; int64_t avail_in; int64_t total_in; int64_t stream_in; unsigned char *next_out; int64_t avail_out; int64_t total_out; int overconsumed; } ppstream; int ppmd7_valid; /* Decoding BCJ and BCJ2 data. */ uint32_t bcj_state; size_t odd_bcj_size; unsigned char odd_bcj[4]; /* Decoding BCJ data. */ size_t bcj_prevPosT; uint32_t bcj_prevMask; uint32_t bcj_ip; /* Decoding BCJ2 data. */ size_t main_stream_bytes_remaining; unsigned char *sub_stream_buff[3]; size_t sub_stream_size[3]; size_t sub_stream_bytes_remaining[3]; unsigned char *tmp_stream_buff; size_t tmp_stream_buff_size; size_t tmp_stream_bytes_avail; size_t tmp_stream_bytes_remaining; #ifdef _LZMA_PROB32 #define CProb uint32_t #else #define CProb uint16_t #endif CProb bcj2_p[256 + 2]; uint8_t bcj2_prevByte; uint32_t bcj2_range; uint32_t bcj2_code; uint64_t bcj2_outPos; /* Filename character-set conversion data. */ struct archive_string_conv *sconv; char format_name[64]; /* Custom value that is non-zero if this archive contains encrypted entries. */ int has_encrypted_entries; }; /* Maximum entry size. This limitation prevents reading intentional * corrupted 7-zip files on assuming there are not so many entries in * the files. */ #define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000) static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *); static int archive_read_support_format_7zip_capabilities(struct archive_read *a); static int archive_read_format_7zip_bid(struct archive_read *, int); static int archive_read_format_7zip_cleanup(struct archive_read *); static int archive_read_format_7zip_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_7zip_read_data_skip(struct archive_read *); static int archive_read_format_7zip_read_header(struct archive_read *, struct archive_entry *); static int check_7zip_header_in_sfx(const char *); static unsigned long decode_codec_id(const unsigned char *, size_t); static int decode_encoded_header_info(struct archive_read *, struct _7z_stream_info *); static int decompress(struct archive_read *, struct _7zip *, void *, size_t *, const void *, size_t *); static ssize_t extract_pack_stream(struct archive_read *, size_t); static void fileTimeToUtc(uint64_t, time_t *, long *); static uint64_t folder_uncompressed_size(struct _7z_folder *); static void free_CodersInfo(struct _7z_coders_info *); static void free_Digest(struct _7z_digests *); static void free_Folder(struct _7z_folder *); static void free_Header(struct _7z_header_info *); static void free_PackInfo(struct _7z_pack_info *); static void free_StreamsInfo(struct _7z_stream_info *); static void free_SubStreamsInfo(struct _7z_substream_info *); static int free_decompression(struct archive_read *, struct _7zip *); static ssize_t get_uncompressed_data(struct archive_read *, const void **, size_t, size_t); static const unsigned char * header_bytes(struct archive_read *, size_t); static int init_decompression(struct archive_read *, struct _7zip *, const struct _7z_coder *, const struct _7z_coder *); static int parse_7zip_uint64(struct archive_read *, uint64_t *); static int read_Bools(struct archive_read *, unsigned char *, size_t); static int read_CodersInfo(struct archive_read *, struct _7z_coders_info *); static int read_Digests(struct archive_read *, struct _7z_digests *, size_t); static int read_Folder(struct archive_read *, struct _7z_folder *); static int read_Header(struct archive_read *, struct _7z_header_info *, int); static int read_PackInfo(struct archive_read *, struct _7z_pack_info *); static int read_StreamsInfo(struct archive_read *, struct _7z_stream_info *); static int read_SubStreamsInfo(struct archive_read *, struct _7z_substream_info *, struct _7z_folder *, size_t); static int read_Times(struct archive_read *, struct _7z_header_info *, int); static void read_consume(struct archive_read *); static ssize_t read_stream(struct archive_read *, const void **, size_t, size_t); static int seek_pack(struct archive_read *); static int64_t skip_stream(struct archive_read *, size_t); static int skip_sfx(struct archive_read *, ssize_t); static int slurp_central_directory(struct archive_read *, struct _7zip *, struct _7z_header_info *); static int setup_decode_folder(struct archive_read *, struct _7z_folder *, int); static void x86_Init(struct _7zip *); static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); +static void arm_Init(struct _7zip *); +static size_t arm_Convert(struct _7zip *, uint8_t *, size_t); +static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); int archive_read_support_format_7zip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct _7zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_7zip"); zip = calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate 7zip data"); return (ARCHIVE_FATAL); } /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; r = __archive_read_register_format(a, zip, "7zip", archive_read_format_7zip_bid, NULL, archive_read_format_7zip_read_header, archive_read_format_7zip_read_data, archive_read_format_7zip_read_data_skip, NULL, archive_read_format_7zip_cleanup, archive_read_support_format_7zip_capabilities, archive_read_format_7zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } static int archive_read_support_format_7zip_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_7zip_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct _7zip * zip = (struct _7zip *)_a->format->data; if (zip) { return zip->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { const char *p; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (0); /* If first six bytes are the 7-Zip signature, * return the bid right now. */ if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) return (48); /* * It may a 7-Zip SFX archive file. If first two bytes are * 'M' and 'Z' available on Windows or first four bytes are * "\x7F\x45LF" available on posix like system, seek the 7-Zip * signature. Although we will perform a seek when reading * a header, what we do not use __archive_read_seek() here is * due to a bidding performance. */ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { ssize_t offset = SFX_MIN_ADDR; ssize_t window = 4096; ssize_t bytes_avail; while (offset + window <= (SFX_MAX_ADDR)) { 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 + 32 < buff + bytes_avail) { int step = check_7zip_header_in_sfx(p); if (step == 0) return (48); p += step; } offset = p - buff; } } return (0); } static int check_7zip_header_in_sfx(const char *p) { switch ((unsigned char)p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) return (6); /* * Test the CRC because its extraction code has 7-Zip * Magic Code, so we should do this in order not to * make a mis-detection. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) return (6); /* Hit the header! */ return (0); case 0x37: return (5); case 0x7A: return (4); case 0xBC: return (3); case 0xAF: return (2); case 0x27: return (1); default: return (6); } } static int skip_sfx(struct archive_read *a, ssize_t bytes_avail) { const void *h; const char *p, *q; size_t skip, offset; ssize_t bytes, window; /* * If bytes_avail > SFX_MIN_ADDR we do not have to call * __archive_read_seek() at this time since we have * already had enough data. */ if (bytes_avail > SFX_MIN_ADDR) __archive_read_consume(a, SFX_MIN_ADDR); else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0) return (ARCHIVE_FATAL); offset = 0; window = 1; while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) { 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 < 6) { /* This case might happen when window == 1. */ window = 4096; continue; } p = (const char *)h; q = p + bytes; /* * Scan ahead until we find something that looks * like the 7-Zip header. */ while (p + 32 < q) { int step = check_7zip_header_in_sfx(p); if (step == 0) { struct _7zip *zip = (struct _7zip *)a->format->data; skip = p - (const char *)h; __archive_read_consume(a, skip); zip->seek_base = SFX_MIN_ADDR + offset + skip; return (ARCHIVE_OK); } p += step; } skip = p - (const char *)h; __archive_read_consume(a, skip); offset += skip; if (window == 1) window = 4096; } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out 7-Zip header"); return (ARCHIVE_FATAL); } static int archive_read_format_7zip_read_header(struct archive_read *a, struct archive_entry *entry) { struct _7zip *zip = (struct _7zip *)a->format->data; struct _7zip_entry *zip_entry; int r, ret = ARCHIVE_OK; struct _7z_folder *folder = 0; uint64_t fidx = 0; /* * 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_7ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "7-Zip"; if (zip->entries == NULL) { struct _7z_header_info header; memset(&header, 0, sizeof(header)); r = slurp_central_directory(a, zip, &header); free_Header(&header); if (r != ARCHIVE_OK) return (r); zip->entries_remaining = (size_t)zip->numFiles; zip->entry = zip->entries; } else { ++zip->entry; } zip_entry = zip->entry; if (zip->entries_remaining <= 0 || zip_entry == NULL) return ARCHIVE_EOF; --zip->entries_remaining; zip->entry_offset = 0; zip->end_of_entry = 0; zip->entry_crc32 = crc32(0, NULL, 0); /* Setup a string conversion for a filename. */ if (zip->sconv == NULL) { zip->sconv = archive_string_conversion_from_charset( &a->archive, "UTF-16LE", 1); if (zip->sconv == NULL) return (ARCHIVE_FATAL); } /* Figure out if the entry is encrypted by looking at the folder that is associated to the current 7zip entry. If the folder has a coder with a _7Z_CRYPTO codec then the folder is encrypted. Hence the entry must also be encrypted. */ if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) { folder = &(zip->si.ci.folders[zip_entry->folderIndex]); for (fidx=0; folder && fidxnumCoders; fidx++) { switch(folder->coders[fidx].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: { archive_entry_set_is_data_encrypted(entry, 1); zip->has_encrypted_entries = 1; break; } } } } /* Now that we've checked for encryption, if there were still no * encrypted entries found we can say for sure that there are none. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if (archive_entry_copy_pathname_l(entry, (const char *)zip_entry->utf16name, zip_entry->name_len, zip->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(zip->sconv)); ret = ARCHIVE_WARN; } /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); if (zip_entry->flg & MTIME_IS_SET) archive_entry_set_mtime(entry, zip_entry->mtime, zip_entry->mtime_ns); if (zip_entry->flg & CTIME_IS_SET) archive_entry_set_ctime(entry, zip_entry->ctime, zip_entry->ctime_ns); if (zip_entry->flg & ATIME_IS_SET) archive_entry_set_atime(entry, zip_entry->atime, zip_entry->atime_ns); if (zip_entry->ssIndex != (uint32_t)-1) { zip->entry_bytes_remaining = zip->si.ss.unpackSizes[zip_entry->ssIndex]; archive_entry_set_size(entry, zip->entry_bytes_remaining); } else { zip->entry_bytes_remaining = 0; archive_entry_set_size(entry, 0); } /* If there's no body, force read_data() to return EOF immediately. */ if (zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) { unsigned char *symname = NULL; size_t symsize = 0; /* * Symbolic-name is recorded as its contents. We have to * read the contents at this time. */ while (zip->entry_bytes_remaining > 0) { const void *buff; unsigned char *mem; size_t size; int64_t offset; r = archive_read_format_7zip_read_data(a, &buff, &size, &offset); if (r < ARCHIVE_WARN) { free(symname); return (r); } mem = realloc(symname, symsize + size + 1); if (mem == NULL) { free(symname); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Symname"); return (ARCHIVE_FATAL); } symname = mem; memcpy(symname+symsize, buff, size); symsize += size; } if (symsize == 0) { /* If there is no symname, handle it as a regular * file. */ zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFREG; archive_entry_set_mode(entry, zip_entry->mode); } else { symname[symsize] = '\0'; archive_entry_copy_symlink(entry, (const char *)symname); } free(symname); archive_entry_set_size(entry, 0); } /* Set up a more descriptive format name. */ snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip"); a->archive.archive_format_name = zip->format_name; return (ret); } static int archive_read_format_7zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct _7zip *zip; ssize_t bytes; int ret = ARCHIVE_OK; zip = (struct _7zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if (zip->pack_stream_bytes_unconsumed) read_consume(a); *offset = zip->entry_offset; *size = 0; *buff = NULL; /* * If we hit end-of-entry last time, clean up and return * ARCHIVE_EOF this time. */ if (zip->end_of_entry) return (ARCHIVE_EOF); const uint64_t max_read_size = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time size_t bytes_to_read = max_read_size; if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) { bytes_to_read = zip->entry_bytes_remaining; } bytes = read_stream(a, buff, bytes_to_read, 0); if (bytes < 0) return ((int)bytes); if (bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } zip->entry_bytes_remaining -= bytes; if (zip->entry_bytes_remaining == 0) zip->end_of_entry = 1; /* Update checksum */ if ((zip->entry->flg & CRC32_IS_SET) && bytes) zip->entry_crc32 = crc32(zip->entry_crc32, *buff, (unsigned)bytes); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { /* Check computed CRC against file contents. */ if ((zip->entry->flg & CRC32_IS_SET) && zip->si.ss.digests[zip->entry->ssIndex] != zip->entry_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "7-Zip bad CRC: 0x%lx should be 0x%lx", (unsigned long)zip->entry_crc32, (unsigned long)zip->si.ss.digests[ zip->entry->ssIndex]); ret = ARCHIVE_WARN; } } *size = bytes; *offset = zip->entry_offset; zip->entry_offset += bytes; return (ret); } static int archive_read_format_7zip_read_data_skip(struct archive_read *a) { struct _7zip *zip; int64_t bytes_skipped; zip = (struct _7zip *)(a->format->data); if (zip->pack_stream_bytes_unconsumed) read_consume(a); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); zip->entry_bytes_remaining = 0; /* This entry is finished and done. */ zip->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_7zip_cleanup(struct archive_read *a) { struct _7zip *zip; zip = (struct _7zip *)(a->format->data); free_StreamsInfo(&(zip->si)); free(zip->entries); free(zip->entry_names); free_decompression(a, zip); free(zip->uncompressed_buffer); free(zip->sub_stream_buff[0]); free(zip->sub_stream_buff[1]); free(zip->sub_stream_buff[2]); free(zip->tmp_stream_buff); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } static void read_consume(struct archive_read *a) { struct _7zip *zip = (struct _7zip *)a->format->data; if (zip->pack_stream_bytes_unconsumed) { __archive_read_consume(a, zip->pack_stream_bytes_unconsumed); zip->stream_offset += zip->pack_stream_bytes_unconsumed; zip->pack_stream_bytes_unconsumed = 0; } } #ifdef HAVE_LZMA_H /* * Set an error code and choose an error message for liblzma. */ static void set_error(struct archive_read *a, int ret) { switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ case LZMA_OK: /* Decompressor made some progress. */ break; case LZMA_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Lzma library error: Cannot allocate memory"); break; case LZMA_MEMLIMIT_ERROR: archive_set_error(&a->archive, ENOMEM, "Lzma library error: Out of memory"); break; case LZMA_FORMAT_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: format not recognized"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Invalid options"); break; case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Corrupted input data"); break; case LZMA_BUF_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: No progress is possible"); break; default: /* Return an error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed: Unknown error"); break; } } #endif static unsigned long decode_codec_id(const unsigned char *codecId, size_t id_size) { unsigned i; unsigned long id = 0; for (i = 0; i < id_size; i++) { id <<= 8; id += codecId[i]; } return (id); } static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; struct _7zip *zip = (struct _7zip *)(a->format->data); Byte b; if (zip->ppstream.avail_in <= 0) { /* * Ppmd7_DecodeSymbol might require reading multiple bytes * and we are on boundary; * last resort to read using __archive_read_ahead. */ ssize_t bytes_avail = 0; const uint8_t* data = __archive_read_ahead(a, zip->ppstream.stream_in+1, &bytes_avail); if(bytes_avail < zip->ppstream.stream_in+1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7z file data"); zip->ppstream.overconsumed = 1; return (0); } zip->ppstream.next_in++; b = data[zip->ppstream.stream_in]; } else { b = *zip->ppstream.next_in++; } zip->ppstream.avail_in--; zip->ppstream.total_in++; zip->ppstream.stream_in++; return (b); } static int init_decompression(struct archive_read *a, struct _7zip *zip, const struct _7z_coder *coder1, const struct _7z_coder *coder2) { int r; zip->codec = coder1->codec; zip->codec2 = -1; switch (zip->codec) { case _7Z_COPY: case _7Z_BZ2: case _7Z_DEFLATE: + case _7Z_ZSTD: case _7Z_PPMD: if (coder2 != NULL) { if (coder2->codec != _7Z_X86 && - coder2->codec != _7Z_X86_BCJ2) { + coder2->codec != _7Z_X86_BCJ2 && + coder2->codec != _7Z_ARM && + coder2->codec != _7Z_ARM64) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported filter %lx for %lx", coder2->codec, coder1->codec); return (ARCHIVE_FAILED); } zip->codec2 = coder2->codec; zip->bcj_state = 0; if (coder2->codec == _7Z_X86) x86_Init(zip); + else if (coder2->codec == _7Z_ARM) + arm_Init(zip); } break; default: break; } switch (zip->codec) { case _7Z_COPY: break; case _7Z_LZMA: case _7Z_LZMA2: #ifdef HAVE_LZMA_H #if LZMA_VERSION_MAJOR >= 5 /* Effectively disable the limiter. */ #define LZMA_MEMLIMIT UINT64_MAX #else /* NOTE: This needs to check memory size which running system has. */ #define LZMA_MEMLIMIT (1U << 30) #endif { lzma_options_delta delta_opt; lzma_filter filters[LZMA_FILTERS_MAX], *ff; int fi = 0; if (zip->lzstream_valid) { lzma_end(&(zip->lzstream)); zip->lzstream_valid = 0; } /* * NOTE: liblzma incompletely handle the BCJ+LZMA compressed * data made by 7-Zip because 7-Zip does not add End-Of- * Payload Marker(EOPM) at the end of LZMA compressed data, * and so liblzma cannot know the end of the compressed data * without EOPM. So consequently liblzma will not return last * three or four bytes of uncompressed data because * LZMA_FILTER_X86 filter does not handle input data if its * data size is less than five bytes. If liblzma detect EOPM * or know the uncompressed data size, liblzma will flush out * the remaining that three or four bytes of uncompressed * data. That is why we have to use our converting program * for BCJ+LZMA. If we were able to tell the uncompressed * size to liblzma when using lzma_raw_decoder() liblzma * could correctly deal with BCJ+LZMA. But unfortunately * there is no way to do that. * Discussion about this can be found at XZ Utils forum. */ if (coder2 != NULL) { zip->codec2 = coder2->codec; filters[fi].options = NULL; switch (zip->codec2) { case _7Z_X86: if (zip->codec == _7Z_LZMA2) { filters[fi].id = LZMA_FILTER_X86; fi++; } else /* Use our filter. */ x86_Init(zip); break; case _7Z_X86_BCJ2: /* Use our filter. */ zip->bcj_state = 0; break; case _7Z_DELTA: if (coder2->propertiesSize != 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Delta parameter"); return (ARCHIVE_FAILED); } filters[fi].id = LZMA_FILTER_DELTA; memset(&delta_opt, 0, sizeof(delta_opt)); delta_opt.type = LZMA_DELTA_TYPE_BYTE; delta_opt.dist = (uint32_t)coder2->properties[0] + 1; filters[fi].options = &delta_opt; fi++; break; /* Following filters have not been tested yet. */ case _7Z_POWERPC: filters[fi].id = LZMA_FILTER_POWERPC; fi++; break; case _7Z_IA64: filters[fi].id = LZMA_FILTER_IA64; fi++; break; case _7Z_ARM: filters[fi].id = LZMA_FILTER_ARM; fi++; break; case _7Z_ARMTHUMB: filters[fi].id = LZMA_FILTER_ARMTHUMB; fi++; break; +#ifdef LZMA_FILTER_ARM64 + case _7Z_ARM64: + filters[fi].id = LZMA_FILTER_ARM64; + fi++; + break; +#endif case _7Z_SPARC: filters[fi].id = LZMA_FILTER_SPARC; fi++; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec2); return (ARCHIVE_FAILED); } } if (zip->codec == _7Z_LZMA2) filters[fi].id = LZMA_FILTER_LZMA2; else filters[fi].id = LZMA_FILTER_LZMA1; filters[fi].options = NULL; ff = &filters[fi]; r = lzma_properties_decode(&filters[fi], NULL, coder1->properties, (size_t)coder1->propertiesSize); if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); } fi++; filters[fi].id = LZMA_VLI_UNKNOWN; filters[fi].options = NULL; r = lzma_raw_decoder(&(zip->lzstream), filters); free(ff->options); if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); } zip->lzstream_valid = 1; zip->lzstream.total_in = 0; zip->lzstream.total_out = 0; break; } #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LZMA codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_BZ2: #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (zip->bzstream_valid) { BZ2_bzDecompressEnd(&(zip->bzstream)); zip->bzstream_valid = 0; } r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0); if (r == BZ_MEM_ERROR) r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1); if (r != BZ_OK) { int err = ARCHIVE_ERRNO_MISC; const char *detail = NULL; switch (r) { case BZ_PARAM_ERROR: detail = "invalid setup parameter"; break; case BZ_MEM_ERROR: err = ENOMEM; detail = "out of memory"; break; case BZ_CONFIG_ERROR: detail = "mis-compiled library"; break; } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", detail != NULL ? detail : "??"); zip->bzstream_valid = 0; return (ARCHIVE_FAILED); } zip->bzstream_valid = 1; zip->bzstream.total_in_lo32 = 0; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_out_lo32 = 0; zip->bzstream.total_out_hi32 = 0; break; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "BZ2 codec is unsupported"); return (ARCHIVE_FAILED); #endif + case _7Z_ZSTD: + { +#if defined(HAVE_ZSTD_H) + if (zip->zstdstream_valid) { + ZSTD_freeDStream(zip->zstd_dstream); + zip->zstdstream_valid = 0; + } + zip->zstd_dstream = ZSTD_createDStream(); + zip->zstdstream_valid = 1; + break; +#else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZSTD codec is unsupported"); + return (ARCHIVE_FAILED); +#endif + } case _7Z_DEFLATE: #ifdef HAVE_ZLIB_H 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, "Couldn't initialize zlib stream."); return (ARCHIVE_FAILED); } zip->stream_valid = 1; zip->stream.total_in = 0; zip->stream.total_out = 0; break; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "DEFLATE codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_PPMD: { unsigned order; uint32_t msize; if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( &zip->ppmd7_context); zip->ppmd7_valid = 0; } if (coder1->propertiesSize < 5) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed PPMd parameter"); return (ARCHIVE_FAILED); } order = coder1->properties[0]; msize = archive_le32dec(&(coder1->properties[1])); if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER || msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed PPMd parameter"); return (ARCHIVE_FAILED); } __archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context); r = __archive_ppmd7_functions.Ppmd7_Alloc( &zip->ppmd7_context, msize); if (r == 0) { archive_set_error(&a->archive, ENOMEM, "Coludn't allocate memory for PPMd"); return (ARCHIVE_FATAL); } __archive_ppmd7_functions.Ppmd7_Init( &zip->ppmd7_context, order); __archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable( &zip->range_dec); zip->ppmd7_valid = 1; zip->ppmd7_stat = 0; zip->ppstream.overconsumed = 0; zip->ppstream.total_in = 0; zip->ppstream.total_out = 0; break; } case _7Z_X86: case _7Z_X86_BCJ2: case _7Z_POWERPC: case _7Z_IA64: case _7Z_ARM: case _7Z_ARMTHUMB: case _7Z_SPARC: case _7Z_DELTA: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: if (a->entry) { archive_entry_set_is_metadata_encrypted(a->entry, 1); archive_entry_set_is_data_encrypted(a->entry, 1); zip->has_encrypted_entries = 1; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Crypto codec not supported yet (ID: 0x%lX)", zip->codec); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int decompress(struct archive_read *a, struct _7zip *zip, void *buff, size_t *outbytes, const void *b, size_t *used) { const uint8_t *t_next_in; uint8_t *t_next_out; size_t o_avail_in, o_avail_out; size_t t_avail_in, t_avail_out; uint8_t *bcj2_next_out; size_t bcj2_avail_out; int r, ret = ARCHIVE_OK; t_avail_in = o_avail_in = *used; t_avail_out = o_avail_out = *outbytes; t_next_in = b; t_next_out = buff; if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { int i; /* Do not copy out the BCJ remaining bytes when the output * buffer size is less than five bytes. */ if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) { *used = 0; *outbytes = 0; return (ret); } for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) { *t_next_out++ = zip->odd_bcj[i]; t_avail_out--; zip->odd_bcj_size--; } if (o_avail_in == 0 || t_avail_out == 0) { *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0) ret = ARCHIVE_EOF; return (ret); } } bcj2_next_out = t_next_out; bcj2_avail_out = t_avail_out; if (zip->codec2 == _7Z_X86_BCJ2) { /* * Decord a remaining decompressed main stream for BCJ2. */ if (zip->tmp_stream_bytes_remaining) { ssize_t bytes; size_t remaining = zip->tmp_stream_bytes_remaining; bytes = Bcj2_Decode(zip, t_next_out, t_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= remaining - zip->tmp_stream_bytes_remaining; t_avail_out -= bytes; if (o_avail_in == 0 || t_avail_out == 0) { *used = 0; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0 && zip->tmp_stream_bytes_remaining) ret = ARCHIVE_EOF; return (ret); } t_next_out += bytes; bcj2_next_out = t_next_out; bcj2_avail_out = t_avail_out; } t_next_out = zip->tmp_stream_buff; t_avail_out = zip->tmp_stream_buff_size; } switch (zip->codec) { case _7Z_COPY: { size_t bytes = (t_avail_in > t_avail_out)?t_avail_out:t_avail_in; memcpy(t_next_out, t_next_in, bytes); t_avail_in -= bytes; t_avail_out -= bytes; if (o_avail_in == 0) ret = ARCHIVE_EOF; break; } #ifdef HAVE_LZMA_H case _7Z_LZMA: case _7Z_LZMA2: zip->lzstream.next_in = t_next_in; zip->lzstream.avail_in = t_avail_in; zip->lzstream.next_out = t_next_out; zip->lzstream.avail_out = t_avail_out; r = lzma_code(&(zip->lzstream), LZMA_RUN); switch (r) { case LZMA_STREAM_END: /* Found end of stream. */ lzma_end(&(zip->lzstream)); zip->lzstream_valid = 0; ret = ARCHIVE_EOF; break; case LZMA_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression failed(%d)", r); return (ARCHIVE_FAILED); } t_avail_in = zip->lzstream.avail_in; t_avail_out = zip->lzstream.avail_out; break; #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case _7Z_BZ2: zip->bzstream.next_in = (char *)(uintptr_t)t_next_in; zip->bzstream.avail_in = t_avail_in; zip->bzstream.next_out = (char *)(uintptr_t)t_next_out; zip->bzstream.avail_out = t_avail_out; r = BZ2_bzDecompress(&(zip->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ switch (BZ2_bzDecompressEnd(&(zip->bzstream))) { case BZ_OK: break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FAILED); } zip->bzstream_valid = 0; ret = ARCHIVE_EOF; break; case BZ_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FAILED); } t_avail_in = zip->bzstream.avail_in; t_avail_out = zip->bzstream.avail_out; break; #endif #ifdef HAVE_ZLIB_H case _7Z_DEFLATE: zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in; zip->stream.avail_in = (uInt)t_avail_in; zip->stream.next_out = t_next_out; zip->stream.avail_out = (uInt)t_avail_out; r = inflate(&(zip->stream), 0); switch (r) { case Z_STREAM_END: /* Found end of stream. */ ret = ARCHIVE_EOF; break; case Z_OK: /* Decompressor made some progress.*/ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File decompression failed (%d)", r); return (ARCHIVE_FAILED); } t_avail_in = zip->stream.avail_in; t_avail_out = zip->stream.avail_out; break; +#endif +#ifdef HAVE_ZSTD_H + case _7Z_ZSTD: + { + ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos + ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos + + size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input); + if (ZSTD_isError(zret)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret)); + return ARCHIVE_FAILED; + } + t_avail_in -= input.pos; + t_avail_out -= output.pos; + break; + } #endif case _7Z_PPMD: { uint64_t flush_bytes; if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 || t_avail_out <= 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression internal error"); return (ARCHIVE_FAILED); } zip->ppstream.next_in = t_next_in; zip->ppstream.avail_in = t_avail_in; zip->ppstream.stream_in = 0; zip->ppstream.next_out = t_next_out; zip->ppstream.avail_out = t_avail_out; if (zip->ppmd7_stat == 0) { zip->bytein.a = a; zip->bytein.Read = &ppmd_read; zip->range_dec.Stream = &zip->bytein; r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init( &(zip->range_dec)); if (r == 0) { zip->ppmd7_stat = -1; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize PPMd range decoder"); return (ARCHIVE_FAILED); } if (zip->ppstream.overconsumed) { zip->ppmd7_stat = -1; return (ARCHIVE_FAILED); } zip->ppmd7_stat = 1; } if (t_avail_in == 0) /* XXX Flush out remaining decoded data XXX */ flush_bytes = zip->folder_outbytes_remaining; else flush_bytes = 0; do { int sym; sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &(zip->ppmd7_context), &(zip->range_dec.p)); if (sym < 0) { zip->ppmd7_stat = -1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to decode PPMd"); return (ARCHIVE_FAILED); } if (zip->ppstream.overconsumed) { zip->ppmd7_stat = -1; return (ARCHIVE_FAILED); } *zip->ppstream.next_out++ = (unsigned char)sym; zip->ppstream.avail_out--; zip->ppstream.total_out++; if (flush_bytes) flush_bytes--; } while (zip->ppstream.avail_out && (zip->ppstream.avail_in || flush_bytes)); t_avail_in = (size_t)zip->ppstream.avail_in; t_avail_out = (size_t)zip->ppstream.avail_out; break; } default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression internal error"); return (ARCHIVE_FAILED); } if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF) return (ret); *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; /* * Decord BCJ. */ - if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { - size_t l = x86_Convert(zip, buff, *outbytes); - zip->odd_bcj_size = *outbytes - l; - if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && - o_avail_in && ret != ARCHIVE_EOF) { - memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, - zip->odd_bcj_size); - *outbytes = l; - } else - zip->odd_bcj_size = 0; + if (zip->codec != _7Z_LZMA2) { + if (zip->codec2 == _7Z_X86) { + size_t l = x86_Convert(zip, buff, *outbytes); + + zip->odd_bcj_size = *outbytes - l; + if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && + o_avail_in && ret != ARCHIVE_EOF) { + memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, + zip->odd_bcj_size); + *outbytes = l; + } else + zip->odd_bcj_size = 0; + } else if (zip->codec2 == _7Z_ARM) { + *outbytes = arm_Convert(zip, buff, *outbytes); + } else if (zip->codec2 == _7Z_ARM64) { + *outbytes = arm64_Convert(zip, buff, *outbytes); + } } /* * Decord BCJ2 with a decompressed main stream. */ if (zip->codec2 == _7Z_X86_BCJ2) { ssize_t bytes; zip->tmp_stream_bytes_avail = zip->tmp_stream_buff_size - t_avail_out; if (zip->tmp_stream_bytes_avail > zip->main_stream_bytes_remaining) zip->tmp_stream_bytes_avail = zip->main_stream_bytes_remaining; zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail; bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= zip->tmp_stream_bytes_avail - zip->tmp_stream_bytes_remaining; bcj2_avail_out -= bytes; *outbytes = o_avail_out - bcj2_avail_out; } return (ret); } static int free_decompression(struct archive_read *a, struct _7zip *zip) { int r = ARCHIVE_OK; #if !defined(HAVE_ZLIB_H) &&\ !(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)) (void)a;/* UNUSED */ #endif #ifdef HAVE_LZMA_H if (zip->lzstream_valid) lzma_end(&(zip->lzstream)); #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (zip->bzstream_valid) { if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up bzip2 decompressor"); r = ARCHIVE_FATAL; } zip->bzstream_valid = 0; } #endif #ifdef HAVE_ZLIB_H if (zip->stream_valid) { if (inflateEnd(&(zip->stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } zip->stream_valid = 0; } #endif if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( &zip->ppmd7_context); zip->ppmd7_valid = 0; } return (r); } static int parse_7zip_uint64(struct archive_read *a, uint64_t *val) { const unsigned char *p; unsigned char avail, mask; int i; if ((p = header_bytes(a, 1)) == NULL) return (-1); avail = *p; mask = 0x80; *val = 0; for (i = 0; i < 8; i++) { if (avail & mask) { if ((p = header_bytes(a, 1)) == NULL) return (-1); *val |= ((uint64_t)*p) << (8 * i); mask >>= 1; continue; } *val += ((uint64_t)(avail & (mask -1))) << (8 * i); break; } return (0); } static int read_Bools(struct archive_read *a, unsigned char *data, size_t num) { const unsigned char *p; unsigned i, mask = 0, avail = 0; for (i = 0; i < num; i++) { if (mask == 0) { if ((p = header_bytes(a, 1)) == NULL) return (-1); avail = *p; mask = 0x80; } data[i] = (avail & mask)?1:0; mask >>= 1; } return (0); } static void free_Digest(struct _7z_digests *d) { free(d->defineds); free(d->digests); } static int read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num) { const unsigned char *p; unsigned i; if (num == 0) return (-1); memset(d, 0, sizeof(*d)); d->defineds = malloc(num); if (d->defineds == NULL) return (-1); /* * Read Bools. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == 0) { if (read_Bools(a, d->defineds, num) < 0) return (-1); } else /* All are defined */ memset(d->defineds, 1, num); d->digests = calloc(num, sizeof(*d->digests)); if (d->digests == NULL) return (-1); for (i = 0; i < num; i++) { if (d->defineds[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); d->digests[i] = archive_le32dec(p); } } return (0); } static void free_PackInfo(struct _7z_pack_info *pi) { free(pi->sizes); free(pi->positions); free_Digest(&(pi->digest)); } static int read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) { const unsigned char *p; unsigned i; memset(pi, 0, sizeof(*pi)); /* * Read PackPos. */ if (parse_7zip_uint64(a, &(pi->pos)) < 0) return (-1); /* * Read NumPackStreams. */ if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0) return (-1); if (pi->numPackStreams == 0) return (-1); if (UMAX_ENTRY < pi->numPackStreams) return (-1); /* * Read PackSizes[num] */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kEnd) /* PackSizes[num] are not present. */ return (0); if (*p != kSize) return (-1); pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); if (pi->sizes == NULL || pi->positions == NULL) return (-1); for (i = 0; i < pi->numPackStreams; i++) { if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0) return (-1); } /* * Read PackStreamDigests[num] */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kEnd) { /* PackStreamDigests[num] are not present. */ pi->digest.defineds = calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds)); pi->digest.digests = calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests)); if (pi->digest.defineds == NULL || pi->digest.digests == NULL) return (-1); return (0); } if (*p != kCRC) return (-1); if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0) return (-1); /* * Must be marked by kEnd. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p != kEnd) return (-1); return (0); } static void free_Folder(struct _7z_folder *f) { unsigned i; if (f->coders) { for (i = 0; i< f->numCoders; i++) { free(f->coders[i].properties); } free(f->coders); } free(f->bindPairs); free(f->packedStreams); free(f->unPackSize); } static int read_Folder(struct archive_read *a, struct _7z_folder *f) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; uint64_t numInStreamsTotal = 0; uint64_t numOutStreamsTotal = 0; unsigned i; memset(f, 0, sizeof(*f)); /* * Read NumCoders. */ if (parse_7zip_uint64(a, &(f->numCoders)) < 0) return (-1); if (f->numCoders > 4) /* Too many coders. */ return (-1); f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders)); if (f->coders == NULL) return (-1); for (i = 0; i< f->numCoders; i++) { size_t codec_size; int simple, attr; if ((p = header_bytes(a, 1)) == NULL) return (-1); /* * 0:3 CodecIdSize * 4: 0 - IsSimple * 1 - Is not Simple * 5: 0 - No Attributes * 1 - There are Attributes; * 7: Must be zero. */ codec_size = *p & 0xf; simple = (*p & 0x10)?0:1; attr = *p & 0x20; if (*p & 0x80) return (-1);/* Not supported. */ /* * Read Decompression Method IDs. */ if ((p = header_bytes(a, codec_size)) == NULL) return (-1); f->coders[i].codec = decode_codec_id(p, codec_size); if (simple) { f->coders[i].numInStreams = 1; f->coders[i].numOutStreams = 1; } else { if (parse_7zip_uint64( a, &(f->coders[i].numInStreams)) < 0) return (-1); if (UMAX_ENTRY < f->coders[i].numInStreams) return (-1); if (parse_7zip_uint64( a, &(f->coders[i].numOutStreams)) < 0) return (-1); if (UMAX_ENTRY < f->coders[i].numOutStreams) return (-1); } if (attr) { if (parse_7zip_uint64( a, &(f->coders[i].propertiesSize)) < 0) return (-1); if ((p = header_bytes( a, (size_t)f->coders[i].propertiesSize)) == NULL) return (-1); f->coders[i].properties = malloc((size_t)f->coders[i].propertiesSize); if (f->coders[i].properties == NULL) return (-1); memcpy(f->coders[i].properties, p, (size_t)f->coders[i].propertiesSize); } numInStreamsTotal += f->coders[i].numInStreams; numOutStreamsTotal += f->coders[i].numOutStreams; } if (numOutStreamsTotal == 0 || numInStreamsTotal < numOutStreamsTotal-1) return (-1); f->numBindPairs = numOutStreamsTotal - 1; if (zip->header_bytes_remaining < f->numBindPairs) return (-1); if (f->numBindPairs > 0) { f->bindPairs = calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs)); if (f->bindPairs == NULL) return (-1); } else f->bindPairs = NULL; for (i = 0; i < f->numBindPairs; i++) { if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0) return (-1); if (UMAX_ENTRY < f->bindPairs[i].inIndex) return (-1); if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0) return (-1); if (UMAX_ENTRY < f->bindPairs[i].outIndex) return (-1); } f->numPackedStreams = numInStreamsTotal - f->numBindPairs; f->packedStreams = calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams)); if (f->packedStreams == NULL) return (-1); if (f->numPackedStreams == 1) { for (i = 0; i < numInStreamsTotal; i++) { unsigned j; for (j = 0; j < f->numBindPairs; j++) { if (f->bindPairs[j].inIndex == i) break; } if (j == f->numBindPairs) break; } if (i == numInStreamsTotal) return (-1); f->packedStreams[0] = i; } else { for (i = 0; i < f->numPackedStreams; i++) { if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0) return (-1); if (UMAX_ENTRY < f->packedStreams[i]) return (-1); } } f->numInStreams = numInStreamsTotal; f->numOutStreams = numOutStreamsTotal; return (0); } static void free_CodersInfo(struct _7z_coders_info *ci) { unsigned i; if (ci->folders) { for (i = 0; i < ci->numFolders; i++) free_Folder(&(ci->folders[i])); free(ci->folders); } } static int read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) { const unsigned char *p; struct _7z_digests digest; unsigned i; memset(ci, 0, sizeof(*ci)); memset(&digest, 0, sizeof(digest)); if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kFolder) goto failed; /* * Read NumFolders. */ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0) goto failed; if (UMAX_ENTRY < ci->numFolders) return (-1); /* * Read External. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; switch (*p) { case 0: ci->folders = calloc((size_t)ci->numFolders, sizeof(*ci->folders)); if (ci->folders == NULL) return (-1); for (i = 0; i < ci->numFolders; i++) { if (read_Folder(a, &(ci->folders[i])) < 0) goto failed; } break; case 1: if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0) return (-1); if (UMAX_ENTRY < ci->dataStreamIndex) return (-1); if (ci->numFolders > 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); goto failed; } break; default: archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); goto failed; } if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kCodersUnPackSize) goto failed; for (i = 0; i < ci->numFolders; i++) { struct _7z_folder *folder = &(ci->folders[i]); unsigned j; folder->unPackSize = calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize)); if (folder->unPackSize == NULL) goto failed; for (j = 0; j < folder->numOutStreams; j++) { if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0) goto failed; } } /* * Read CRCs. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p == kEnd) return (0); if (*p != kCRC) goto failed; if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0) goto failed; for (i = 0; i < ci->numFolders; i++) { ci->folders[i].digest_defined = digest.defineds[i]; ci->folders[i].digest = digest.digests[i]; } /* * Must be kEnd. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kEnd) goto failed; free_Digest(&digest); return (0); failed: free_Digest(&digest); return (-1); } static uint64_t folder_uncompressed_size(struct _7z_folder *f) { int n = (int)f->numOutStreams; unsigned pairs = (unsigned)f->numBindPairs; while (--n >= 0) { unsigned i; for (i = 0; i < pairs; i++) { if (f->bindPairs[i].outIndex == (uint64_t)n) break; } if (i >= pairs) return (f->unPackSize[n]); } return (0); } static void free_SubStreamsInfo(struct _7z_substream_info *ss) { free(ss->unpackSizes); free(ss->digestsDefined); free(ss->digests); } static int read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, struct _7z_folder *f, size_t numFolders) { const unsigned char *p; uint64_t *usizes; size_t unpack_streams; int type; unsigned i; uint32_t numDigests; memset(ss, 0, sizeof(*ss)); for (i = 0; i < numFolders; i++) f[i].numUnpackStreams = 1; if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; if (type == kNumUnPackStream) { unpack_streams = 0; for (i = 0; i < numFolders; i++) { if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) return (-1); if (UMAX_ENTRY < f[i].numUnpackStreams) return (-1); if (unpack_streams > SIZE_MAX - UMAX_ENTRY) { return (-1); } unpack_streams += (size_t)f[i].numUnpackStreams; } if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } else unpack_streams = numFolders; ss->unpack_streams = unpack_streams; if (unpack_streams) { ss->unpackSizes = calloc(unpack_streams, sizeof(*ss->unpackSizes)); ss->digestsDefined = calloc(unpack_streams, sizeof(*ss->digestsDefined)); ss->digests = calloc(unpack_streams, sizeof(*ss->digests)); if (ss->unpackSizes == NULL || ss->digestsDefined == NULL || ss->digests == NULL) return (-1); } usizes = ss->unpackSizes; for (i = 0; i < numFolders; i++) { unsigned pack; uint64_t sum; if (f[i].numUnpackStreams == 0) continue; sum = 0; if (type == kSize) { for (pack = 1; pack < f[i].numUnpackStreams; pack++) { if (parse_7zip_uint64(a, usizes) < 0) return (-1); sum += *usizes++; } } *usizes++ = folder_uncompressed_size(&f[i]) - sum; } if (type == kSize) { if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } for (i = 0; i < unpack_streams; i++) { ss->digestsDefined[i] = 0; ss->digests[i] = 0; } numDigests = 0; for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams != 1 || !f[i].digest_defined) numDigests += (uint32_t)f[i].numUnpackStreams; } if (type == kCRC) { struct _7z_digests tmpDigests; unsigned char *digestsDefined = ss->digestsDefined; uint32_t * digests = ss->digests; int di = 0; memset(&tmpDigests, 0, sizeof(tmpDigests)); if (read_Digests(a, &(tmpDigests), numDigests) < 0) { free_Digest(&tmpDigests); return (-1); } for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams == 1 && f[i].digest_defined) { *digestsDefined++ = 1; *digests++ = f[i].digest; } else { unsigned j; for (j = 0; j < f[i].numUnpackStreams; j++, di++) { *digestsDefined++ = tmpDigests.defineds[di]; *digests++ = tmpDigests.digests[di]; } } } free_Digest(&tmpDigests); if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } /* * Must be kEnd. */ if (type != kEnd) return (-1); return (0); } static void free_StreamsInfo(struct _7z_stream_info *si) { free_PackInfo(&(si->pi)); free_CodersInfo(&(si->ci)); free_SubStreamsInfo(&(si->ss)); } static int read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; unsigned i; memset(si, 0, sizeof(*si)); if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kPackInfo) { uint64_t packPos; if (read_PackInfo(a, &(si->pi)) < 0) return (-1); if (si->pi.positions == NULL || si->pi.sizes == NULL) return (-1); /* * Calculate packed stream positions. */ packPos = si->pi.pos; for (i = 0; i < si->pi.numPackStreams; i++) { si->pi.positions[i] = packPos; packPos += si->pi.sizes[i]; if (packPos > zip->header_offset) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kUnPackInfo) { uint32_t packIndex; struct _7z_folder *f; if (read_CodersInfo(a, &(si->ci)) < 0) return (-1); /* * Calculate packed stream indexes. */ packIndex = 0; f = si->ci.folders; for (i = 0; i < si->ci.numFolders; i++) { f[i].packIndex = packIndex; packIndex += (uint32_t)f[i].numPackedStreams; if (packIndex > si->pi.numPackStreams) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kSubStreamsInfo) { if (read_SubStreamsInfo(a, &(si->ss), si->ci.folders, (size_t)si->ci.numFolders) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); } /* * Must be kEnd. */ if (*p != kEnd) return (-1); return (0); } static void free_Header(struct _7z_header_info *h) { free(h->emptyStreamBools); free(h->emptyFileBools); free(h->antiBools); free(h->attrBools); } static int read_Header(struct archive_read *a, struct _7z_header_info *h, int check_header_id) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; struct _7z_folder *folders; struct _7z_stream_info *si = &(zip->si); struct _7zip_entry *entries; uint32_t folderIndex, indexInFolder; unsigned i; int eindex, empty_streams, sindex; if (check_header_id) { /* * Read Header. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p != kHeader) return (-1); } /* * Read ArchiveProperties. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kArchiveProperties) { for (;;) { uint64_t size; if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == 0) break; if (parse_7zip_uint64(a, &size) < 0) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } /* * Read MainStreamsInfo. */ if (*p == kMainStreamsInfo) { if (read_StreamsInfo(a, &(zip->si)) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kEnd) return (0); /* * Read FilesInfo. */ if (*p != kFilesInfo) return (-1); if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) return (-1); if (UMAX_ENTRY < zip->numFiles) return (-1); zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); if (zip->entries == NULL) return (-1); entries = zip->entries; empty_streams = 0; for (;;) { int type; uint64_t size; size_t ll; if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; if (type == kEnd) break; if (parse_7zip_uint64(a, &size) < 0) return (-1); if (zip->header_bytes_remaining < size) return (-1); ll = (size_t)size; switch (type) { case kEmptyStream: if (h->emptyStreamBools != NULL) return (-1); h->emptyStreamBools = calloc((size_t)zip->numFiles, sizeof(*h->emptyStreamBools)); if (h->emptyStreamBools == NULL) return (-1); if (read_Bools( a, h->emptyStreamBools, (size_t)zip->numFiles) < 0) return (-1); empty_streams = 0; for (i = 0; i < zip->numFiles; i++) { if (h->emptyStreamBools[i]) empty_streams++; } break; case kEmptyFile: if (empty_streams <= 0) { /* Unexcepted sequence. Skip this. */ if (header_bytes(a, ll) == NULL) return (-1); break; } if (h->emptyFileBools != NULL) return (-1); h->emptyFileBools = calloc(empty_streams, sizeof(*h->emptyFileBools)); if (h->emptyFileBools == NULL) return (-1); if (read_Bools(a, h->emptyFileBools, empty_streams) < 0) return (-1); break; case kAnti: if (empty_streams <= 0) { /* Unexcepted sequence. Skip this. */ if (header_bytes(a, ll) == NULL) return (-1); break; } if (h->antiBools != NULL) return (-1); h->antiBools = calloc(empty_streams, sizeof(*h->antiBools)); if (h->antiBools == NULL) return (-1); if (read_Bools(a, h->antiBools, empty_streams) < 0) return (-1); break; case kCTime: case kATime: case kMTime: if (read_Times(a, h, type) < 0) return (-1); break; case kName: { unsigned char *np; size_t nl, nb; /* Skip one byte. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); ll--; if ((ll & 1) || ll < zip->numFiles * 4) return (-1); if (zip->entry_names != NULL) return (-1); zip->entry_names = malloc(ll); if (zip->entry_names == NULL) return (-1); np = zip->entry_names; nb = ll; /* * Copy whole file names. * NOTE: This loop prevents from expanding * the uncompressed buffer in order not to * use extra memory resource. */ while (nb) { size_t b; if (nb > UBUFF_SIZE) b = UBUFF_SIZE; else b = nb; if ((p = header_bytes(a, b)) == NULL) return (-1); memcpy(np, p, b); np += b; nb -= b; } np = zip->entry_names; nl = ll; for (i = 0; i < zip->numFiles; i++) { entries[i].utf16name = np; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) entries[i].wname = (wchar_t *)np; #endif /* Find a terminator. */ while (nl >= 2 && (np[0] || np[1])) { np += 2; nl -= 2; } if (nl < 2) return (-1);/* Terminator not found */ entries[i].name_len = np - entries[i].utf16name; np += 2; nl -= 2; } break; } case kAttributes: { int allAreDefined; if ((p = header_bytes(a, 2)) == NULL) return (-1); allAreDefined = *p; if (h->attrBools != NULL) return (-1); h->attrBools = calloc((size_t)zip->numFiles, sizeof(*h->attrBools)); if (h->attrBools == NULL) return (-1); if (allAreDefined) memset(h->attrBools, 1, (size_t)zip->numFiles); else { if (read_Bools(a, h->attrBools, (size_t)zip->numFiles) < 0) return (-1); } for (i = 0; i < zip->numFiles; i++) { if (h->attrBools[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); entries[i].attr = archive_le32dec(p); } } break; } case kDummy: if (ll == 0) break; __LA_FALLTHROUGH; default: if (header_bytes(a, ll) == NULL) return (-1); break; } } /* * Set up entry's attributes. */ folders = si->ci.folders; eindex = sindex = 0; folderIndex = indexInFolder = 0; for (i = 0; i < zip->numFiles; i++) { if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0) entries[i].flg |= HAS_STREAM; /* The high 16 bits of attributes is a posix file mode. */ entries[i].mode = entries[i].attr >> 16; if (entries[i].flg & HAS_STREAM) { if ((size_t)sindex >= si->ss.unpack_streams) return (-1); if (entries[i].mode == 0) entries[i].mode = AE_IFREG | 0666; if (si->ss.digestsDefined[sindex]) entries[i].flg |= CRC32_IS_SET; entries[i].ssIndex = sindex; sindex++; } else { int dir; if (h->emptyFileBools == NULL) dir = 1; else { if (h->emptyFileBools[eindex]) dir = 0; else dir = 1; eindex++; } if (entries[i].mode == 0) { if (dir) entries[i].mode = AE_IFDIR | 0777; else entries[i].mode = AE_IFREG | 0666; } else if (dir && (entries[i].mode & AE_IFMT) != AE_IFDIR) { entries[i].mode &= ~AE_IFMT; entries[i].mode |= AE_IFDIR; } if ((entries[i].mode & AE_IFMT) == AE_IFDIR && entries[i].name_len >= 2 && (entries[i].utf16name[entries[i].name_len-2] != '/' || entries[i].utf16name[entries[i].name_len-1] != 0)) { entries[i].utf16name[entries[i].name_len] = '/'; entries[i].utf16name[entries[i].name_len+1] = 0; entries[i].name_len += 2; } entries[i].ssIndex = -1; } if (entries[i].attr & 0x01) entries[i].mode &= ~0222;/* Read only. */ if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) { /* * The entry is an empty file or a directory file, * those both have no contents. */ entries[i].folderIndex = -1; continue; } if (indexInFolder == 0) { for (;;) { if (folderIndex >= si->ci.numFolders) return (-1); if (folders[folderIndex].numUnpackStreams) break; folderIndex++; } } entries[i].folderIndex = folderIndex; if ((entries[i].flg & HAS_STREAM) == 0) continue; indexInFolder++; if (indexInFolder >= folders[folderIndex].numUnpackStreams) { folderIndex++; indexInFolder = 0; } } return (0); } #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) static void fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns) { if (fileTime >= EPOC_TIME) { fileTime -= EPOC_TIME; /* milli seconds base */ *timep = (time_t)(fileTime / 10000000); /* nano seconds base */ *ns = (long)(fileTime % 10000000) * 100; } else { *timep = 0; *ns = 0; } } static int read_Times(struct archive_read *a, struct _7z_header_info *h, int type) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; struct _7zip_entry *entries = zip->entries; unsigned char *timeBools; int allAreDefined; unsigned i; timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools)); if (timeBools == NULL) return (-1); /* Read allAreDefined. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; allAreDefined = *p; if (allAreDefined) memset(timeBools, 1, (size_t)zip->numFiles); else { if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0) goto failed; } /* Read external. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p) { if (parse_7zip_uint64(a, &(h->dataIndex)) < 0) goto failed; if (UMAX_ENTRY < h->dataIndex) goto failed; } for (i = 0; i < zip->numFiles; i++) { if (!timeBools[i]) continue; if ((p = header_bytes(a, 8)) == NULL) goto failed; switch (type) { case kCTime: fileTimeToUtc(archive_le64dec(p), &(entries[i].ctime), &(entries[i].ctime_ns)); entries[i].flg |= CTIME_IS_SET; break; case kATime: fileTimeToUtc(archive_le64dec(p), &(entries[i].atime), &(entries[i].atime_ns)); entries[i].flg |= ATIME_IS_SET; break; case kMTime: fileTimeToUtc(archive_le64dec(p), &(entries[i].mtime), &(entries[i].mtime_ns)); entries[i].flg |= MTIME_IS_SET; break; } } free(timeBools); return (0); failed: free(timeBools); return (-1); } static int decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si) { struct _7zip *zip = (struct _7zip *)a->format->data; errno = 0; if (read_StreamsInfo(a, si) < 0) { if (errno == ENOMEM) archive_set_error(&a->archive, -1, "Couldn't allocate memory"); else archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } if (zip->header_offset < si->pi.pos + si->pi.sizes[0] || (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 || si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) { archive_set_error(&a->archive, -1, "Malformed Header offset"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static const unsigned char * header_bytes(struct archive_read *a, size_t rbytes) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; if (zip->header_bytes_remaining < rbytes) return (NULL); if (zip->pack_stream_bytes_unconsumed) read_consume(a); if (zip->header_is_encoded == 0) { p = __archive_read_ahead(a, rbytes, NULL); if (p == NULL) return (NULL); zip->header_bytes_remaining -= rbytes; zip->pack_stream_bytes_unconsumed = rbytes; } else { const void *buff; ssize_t bytes; bytes = read_stream(a, &buff, rbytes, rbytes); if (bytes <= 0) return (NULL); zip->header_bytes_remaining -= bytes; p = buff; } /* Update checksum */ zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes); return (p); } static int slurp_central_directory(struct archive_read *a, struct _7zip *zip, struct _7z_header_info *header) { const unsigned char *p; uint64_t next_header_offset; uint64_t next_header_size; uint32_t next_header_crc; ssize_t bytes_avail; int check_header_crc, r; if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) return (ARCHIVE_FATAL); if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { /* This is an executable ? Must be self-extracting... */ r = skip_sfx(a, bytes_avail); if (r < ARCHIVE_WARN) return (r); if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) return (ARCHIVE_FATAL); } zip->seek_base += 32; if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) { archive_set_error(&a->archive, -1, "Not 7-Zip archive file"); return (ARCHIVE_FATAL); } /* CRC check. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) { #ifdef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, -1, "Header CRC error"); return (ARCHIVE_FATAL); #endif } next_header_offset = archive_le64dec(p + 12); next_header_size = archive_le64dec(p + 20); next_header_crc = archive_le32dec(p + 28); if (next_header_size == 0) /* There is no entry in an archive file. */ return (ARCHIVE_EOF); if (((int64_t)next_header_offset) < 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 32); if (next_header_offset != 0) { if (bytes_avail >= (ssize_t)next_header_offset) __archive_read_consume(a, next_header_offset); else if (__archive_read_seek(a, next_header_offset + zip->seek_base, SEEK_SET) < 0) return (ARCHIVE_FATAL); } zip->stream_offset = next_header_offset; zip->header_offset = next_header_offset; zip->header_bytes_remaining = next_header_size; zip->header_crc32 = 0; zip->header_is_encoded = 0; zip->header_is_being_read = 1; zip->has_encrypted_entries = 0; check_header_crc = 1; if ((p = header_bytes(a, 1)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } /* Parse ArchiveProperties. */ switch (p[0]) { case kEncodedHeader: /* * The archive has an encoded header and we have to decode it * in order to parse the header correctly. */ r = decode_encoded_header_info(a, &(zip->si)); /* Check the EncodedHeader CRC.*/ if (r == 0 && zip->header_crc32 != next_header_crc) { archive_set_error(&a->archive, -1, #ifndef DONT_FAIL_ON_CRC_ERROR "Damaged 7-Zip archive"); r = -1; #endif } if (r == 0) { if (zip->si.ci.folders[0].digest_defined) next_header_crc = zip->si.ci.folders[0].digest; else check_header_crc = 0; if (zip->pack_stream_bytes_unconsumed) read_consume(a); r = setup_decode_folder(a, zip->si.ci.folders, 1); if (r == 0) { zip->header_bytes_remaining = zip->folder_outbytes_remaining; r = seek_pack(a); } } /* Clean up StreamsInfo. */ free_StreamsInfo(&(zip->si)); memset(&(zip->si), 0, sizeof(zip->si)); if (r < 0) return (ARCHIVE_FATAL); zip->header_is_encoded = 1; zip->header_crc32 = 0; /* FALL THROUGH */ case kHeader: /* * Parse the header. */ errno = 0; r = read_Header(a, header, zip->header_is_encoded); if (r < 0) { if (errno == ENOMEM) archive_set_error(&a->archive, -1, "Couldn't allocate memory"); else archive_set_error(&a->archive, -1, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } /* * Must be kEnd. */ if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } /* Check the Header CRC.*/ if (check_header_crc && zip->header_crc32 != next_header_crc) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); #endif } break; default: archive_set_error(&a->archive, -1, "Unexpected Property ID = %X", p[0]); return (ARCHIVE_FATAL); } /* Clean up variables be used for decoding the archive header */ zip->pack_stream_remaining = 0; zip->pack_stream_index = 0; zip->folder_outbytes_remaining = 0; zip->uncompressed_buffer_bytes_remaining = 0; zip->pack_stream_bytes_unconsumed = 0; zip->header_is_being_read = 0; return (ARCHIVE_OK); } static ssize_t get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; ssize_t bytes_avail; if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { /* Copy mode. */ *buff = __archive_read_ahead(a, minimum, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file data"); return (ARCHIVE_FATAL); } if ((size_t)bytes_avail > zip->uncompressed_buffer_bytes_remaining) bytes_avail = (ssize_t) zip->uncompressed_buffer_bytes_remaining; if ((size_t)bytes_avail > size) bytes_avail = (ssize_t)size; zip->pack_stream_bytes_unconsumed = bytes_avail; } else if (zip->uncompressed_buffer_pointer == NULL) { /* Decompression has failed. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } else { /* Packed mode. */ if (minimum > zip->uncompressed_buffer_bytes_remaining) { /* * If remaining uncompressed data size is less than * the minimum size, fill the buffer up to the * minimum size. */ if (extract_pack_stream(a, minimum) < 0) return (ARCHIVE_FATAL); } if (size > zip->uncompressed_buffer_bytes_remaining) bytes_avail = (ssize_t) zip->uncompressed_buffer_bytes_remaining; else bytes_avail = (ssize_t)size; *buff = zip->uncompressed_buffer_pointer; zip->uncompressed_buffer_pointer += bytes_avail; } zip->uncompressed_buffer_bytes_remaining -= bytes_avail; return (bytes_avail); } static ssize_t extract_pack_stream(struct archive_read *a, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; ssize_t bytes_avail; int r; if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { if (minimum == 0) minimum = 1; if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL || bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } if ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining) bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining; zip->pack_stream_inbytes_remaining -= bytes_avail; if ((uint64_t)bytes_avail > zip->folder_outbytes_remaining) bytes_avail = (ssize_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_avail; zip->uncompressed_buffer_bytes_remaining = bytes_avail; return (ARCHIVE_OK); } /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = UBUFF_SIZE; if (zip->uncompressed_buffer_size < minimum) { zip->uncompressed_buffer_size = minimum + 1023; zip->uncompressed_buffer_size &= ~0x3ff; } zip->uncompressed_buffer = malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer_bytes_remaining = 0; } else if (zip->uncompressed_buffer_size < minimum || zip->uncompressed_buffer_bytes_remaining < minimum) { /* * Make sure the uncompressed buffer can have bytes * at least `minimum' bytes. * NOTE: This case happen when reading the header. */ size_t used; if (zip->uncompressed_buffer_pointer != 0) used = zip->uncompressed_buffer_pointer - zip->uncompressed_buffer; else used = 0; if (zip->uncompressed_buffer_size < minimum) { /* * Expand the uncompressed buffer up to * the minimum size. */ void *p; size_t new_size; new_size = minimum + 1023; new_size &= ~0x3ff; p = realloc(zip->uncompressed_buffer, new_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer = (unsigned char *)p; zip->uncompressed_buffer_size = new_size; } /* * Move unconsumed bytes to the head. */ if (used) { memmove(zip->uncompressed_buffer, zip->uncompressed_buffer + used, zip->uncompressed_buffer_bytes_remaining); } } else zip->uncompressed_buffer_bytes_remaining = 0; zip->uncompressed_buffer_pointer = NULL; for (;;) { size_t bytes_in, bytes_out; const void *buff_in; unsigned char *buff_out; int end_of_data; /* * 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. */ buff_in = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } buff_out = zip->uncompressed_buffer + zip->uncompressed_buffer_bytes_remaining; bytes_out = zip->uncompressed_buffer_size - zip->uncompressed_buffer_bytes_remaining; bytes_in = bytes_avail; if (bytes_in > zip->pack_stream_inbytes_remaining) bytes_in = (size_t)zip->pack_stream_inbytes_remaining; /* Drive decompression. */ r = decompress(a, zip, buff_out, &bytes_out, buff_in, &bytes_in); switch (r) { case ARCHIVE_OK: end_of_data = 0; break; case ARCHIVE_EOF: end_of_data = 1; break; default: return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining -= bytes_in; if (bytes_out > zip->folder_outbytes_remaining) bytes_out = (size_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_out; zip->uncompressed_buffer_bytes_remaining += bytes_out; zip->pack_stream_bytes_unconsumed = bytes_in; /* * Continue decompression until uncompressed_buffer is full. */ if (zip->uncompressed_buffer_bytes_remaining == zip->uncompressed_buffer_size) break; if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size && zip->uncompressed_buffer_bytes_remaining + 5 > zip->uncompressed_buffer_size) break; if (zip->pack_stream_inbytes_remaining == 0 && zip->folder_outbytes_remaining == 0) break; if (end_of_data || (bytes_in == 0 && bytes_out == 0)) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } read_consume(a); } if (zip->uncompressed_buffer_bytes_remaining < minimum) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer_pointer = zip->uncompressed_buffer; return (ARCHIVE_OK); } static int seek_pack(struct archive_read *a) { struct _7zip *zip = (struct _7zip *)a->format->data; int64_t pack_offset; if (zip->pack_stream_remaining <= 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining = zip->si.pi.sizes[zip->pack_stream_index]; pack_offset = zip->si.pi.positions[zip->pack_stream_index]; if (zip->stream_offset != pack_offset) { if (0 > __archive_read_seek(a, pack_offset + zip->seek_base, SEEK_SET)) return (ARCHIVE_FATAL); zip->stream_offset = pack_offset; } zip->pack_stream_index++; zip->pack_stream_remaining--; return (ARCHIVE_OK); } static ssize_t read_stream(struct archive_read *a, const void **buff, size_t size, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; uint64_t skip_bytes = 0; ssize_t r; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { r = extract_pack_stream(a, 0); if (r < 0) return (r); return (get_uncompressed_data(a, buff, size, minimum)); } else if (zip->folder_outbytes_remaining > 0) { /* Extract a remaining pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); return (get_uncompressed_data(a, buff, size, minimum)); } } else return (get_uncompressed_data(a, buff, size, minimum)); /* * Current pack stream has been consumed. */ if (zip->pack_stream_remaining == 0) { if (zip->header_is_being_read) { /* Invalid sequence. This might happen when * reading a malformed archive. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } /* * All current folder's pack streams have been * consumed. Switch to next folder. */ if (zip->folder_index == 0 && (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes || zip->folder_index != zip->entry->folderIndex)) { zip->folder_index = zip->entry->folderIndex; skip_bytes = zip->si.ci.folders[zip->folder_index].skipped_bytes; } if (zip->folder_index >= zip->si.ci.numFolders) { /* * We have consumed all folders and its pack streams. */ *buff = NULL; return (0); } r = setup_decode_folder(a, &(zip->si.ci.folders[zip->folder_index]), 0); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->folder_index++; } /* * Switch to next pack stream. */ r = seek_pack(a); if (r < 0) return (r); /* Extract a new pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); /* * Skip the bytes we already has skipped in skip_stream(). */ while (skip_bytes) { ssize_t skipped; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { r = extract_pack_stream(a, 0); if (r < 0) return (r); } else if (zip->folder_outbytes_remaining > 0) { /* Extract a remaining pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } } skipped = get_uncompressed_data( a, buff, (size_t)skip_bytes, 0); if (skipped < 0) return (skipped); skip_bytes -= skipped; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } return (get_uncompressed_data(a, buff, size, minimum)); } static int setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, int header) { struct _7zip *zip = (struct _7zip *)a->format->data; const struct _7z_coder *coder1, *coder2; const char *cname = (header)?"archive header":"file content"; unsigned i; int r, found_bcj2 = 0; /* * Release the memory which the previous folder used for BCJ2. */ for (i = 0; i < 3; i++) { free(zip->sub_stream_buff[i]); zip->sub_stream_buff[i] = NULL; } /* * Initialize a stream reader. */ zip->pack_stream_remaining = (unsigned)folder->numPackedStreams; zip->pack_stream_index = (unsigned)folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; /* * Check coder types. */ for (i = 0; i < folder->numCoders; i++) { switch(folder->coders[i].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: { /* For entry that is associated with this folder, mark it as encrypted (data+metadata). */ zip->has_encrypted_entries = 1; if (a->entry) { archive_entry_set_is_data_encrypted(a->entry, 1); archive_entry_set_is_metadata_encrypted(a->entry, 1); } archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "The %s is encrypted, " "but currently not supported", cname); return (ARCHIVE_FATAL); } case _7Z_X86_BCJ2: { found_bcj2++; break; } } } /* Now that we've checked for encryption, if there were still no * encrypted entries found we can say for sure that there are none. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "The %s is encoded with many filters, " "but currently not supported", cname); return (ARCHIVE_FATAL); } coder1 = &(folder->coders[0]); if (folder->numCoders == 2) coder2 = &(folder->coders[1]); else coder2 = NULL; if (found_bcj2) { /* * Preparation to decode BCJ2. * Decoding BCJ2 requires four sources. Those are at least, * as far as I know, two types of the storage form. */ const struct _7z_coder *fc = folder->coders; static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL}; const struct _7z_coder *scoder[3] = {&coder_copy, &coder_copy, &coder_copy}; const void *buff; ssize_t bytes; unsigned char *b[3] = {NULL, NULL, NULL}; uint64_t sunpack[3] ={-1, -1, -1}; size_t s[3] = {0, 0, 0}; int idx[3] = {0, 1, 2}; if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 && folder->numInStreams == 7 && folder->numOutStreams == 4 && zip->pack_stream_remaining == 4) { /* Source type 1 made by 7zr or 7z with -m options. */ if (folder->bindPairs[0].inIndex == 5) { /* The form made by 7zr */ idx[0] = 1; idx[1] = 2; idx[2] = 0; scoder[1] = &(fc[1]); scoder[2] = &(fc[0]); sunpack[1] = folder->unPackSize[1]; sunpack[2] = folder->unPackSize[0]; coder1 = &(fc[2]); } else { /* * NOTE: Some patterns do not work. * work: * 7z a -m0=BCJ2 -m1=COPY -m2=COPY * -m3=(any) * 7z a -m0=BCJ2 -m1=COPY -m2=(any) * -m3=COPY * 7z a -m0=BCJ2 -m1=(any) -m2=COPY * -m3=COPY * not work: * other patterns. * * We have to handle this like `pipe' or * our libarchive7s filter frame work, * decoding the BCJ2 main stream sequentially, * m3 -> m2 -> m1 -> BCJ2. * */ if (fc[0].codec == _7Z_COPY && fc[1].codec == _7Z_COPY) coder1 = &(folder->coders[2]); else if (fc[0].codec == _7Z_COPY && fc[2].codec == _7Z_COPY) coder1 = &(folder->coders[1]); else if (fc[1].codec == _7Z_COPY && fc[2].codec == _7Z_COPY) coder1 = &(folder->coders[0]); else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unsupported form of " "BCJ2 streams"); return (ARCHIVE_FATAL); } } coder2 = &(fc[3]); zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[2]; } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 && zip->pack_stream_remaining == 4 && folder->numInStreams == 5 && folder->numOutStreams == 2) { /* Source type 0 made by 7z */ zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[0]; } else { /* We got an unexpected form. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unsupported form of BCJ2 streams"); return (ARCHIVE_FATAL); } /* Skip the main stream at this time. */ if ((r = seek_pack(a)) < 0) return (r); zip->pack_stream_bytes_unconsumed = (size_t)zip->pack_stream_inbytes_remaining; read_consume(a); /* Read following three sub streams. */ for (i = 0; i < 3; i++) { const struct _7z_coder *coder = scoder[i]; if ((r = seek_pack(a)) < 0) { free(b[0]); free(b[1]); free(b[2]); return (r); } if (sunpack[i] == (uint64_t)-1) zip->folder_outbytes_remaining = zip->pack_stream_inbytes_remaining; else zip->folder_outbytes_remaining = sunpack[i]; r = init_decompression(a, zip, coder, NULL); if (r != ARCHIVE_OK) { free(b[0]); free(b[1]); free(b[2]); return (ARCHIVE_FATAL); } /* Allocate memory for the decoded data of a sub * stream. */ b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { free(b[0]); free(b[1]); free(b[2]); archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } /* Extract a sub stream. */ while (zip->pack_stream_inbytes_remaining > 0) { r = (int)extract_pack_stream(a, 0); if (r < 0) { free(b[0]); free(b[1]); free(b[2]); return (r); } bytes = get_uncompressed_data(a, &buff, zip->uncompressed_buffer_bytes_remaining, 0); if (bytes < 0) { free(b[0]); free(b[1]); free(b[2]); return ((int)bytes); } memcpy(b[i]+s[i], buff, bytes); s[i] += bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } } /* Set the sub streams to the right place. */ for (i = 0; i < 3; i++) { zip->sub_stream_buff[i] = b[idx[i]]; zip->sub_stream_size[i] = s[idx[i]]; zip->sub_stream_bytes_remaining[i] = s[idx[i]]; } /* Allocate memory used for decoded main stream bytes. */ if (zip->tmp_stream_buff == NULL) { zip->tmp_stream_buff_size = 32 * 1024; zip->tmp_stream_buff = malloc(zip->tmp_stream_buff_size); if (zip->tmp_stream_buff == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } } zip->tmp_stream_bytes_avail = 0; zip->tmp_stream_bytes_remaining = 0; zip->odd_bcj_size = 0; zip->bcj2_outPos = 0; /* * Reset a stream reader in order to read the main stream * of BCJ2. */ zip->pack_stream_remaining = 1; zip->pack_stream_index = (unsigned)folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; } /* * Initialize the decompressor for the new folder's pack streams. */ r = init_decompression(a, zip, coder1, coder2); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static int64_t skip_stream(struct archive_read *a, size_t skip_bytes) { struct _7zip *zip = (struct _7zip *)a->format->data; const void *p; int64_t skipped_bytes; size_t bytes = skip_bytes; if (zip->folder_index == 0) { /* * Optimization for a list mode. * Avoid unnecessary decoding operations. */ zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes += skip_bytes; return (skip_bytes); } while (bytes) { skipped_bytes = read_stream(a, &p, bytes, 0); if (skipped_bytes < 0) return (skipped_bytes); if (skipped_bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } bytes -= (size_t)skipped_bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } return (skip_bytes); } /* * Brought from LZMA SDK. * * Bra86.c -- Converter for x86 code (BCJ) * 2008-10-04 : Igor Pavlov : Public domain * */ #define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) static void x86_Init(struct _7zip *zip) { zip->bcj_state = 0; zip->bcj_prevPosT = (size_t)0 - 1; zip->bcj_prevMask = 0; zip->bcj_ip = 5; } static size_t x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) { static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; size_t bufferPos, prevPosT; uint32_t ip, prevMask; if (size < 5) return 0; bufferPos = 0; prevPosT = zip->bcj_prevPosT; prevMask = zip->bcj_prevMask; ip = zip->bcj_ip; for (;;) { uint8_t *p = data + bufferPos; uint8_t *limit = data + size - 4; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; bufferPos = (size_t)(p - data); if (p >= limit) break; prevPosT = bufferPos - prevPosT; if (prevPosT > 3) prevMask = 0; else { prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; if (prevMask != 0) { unsigned char b = p[4 - kMaskToBitNumber[prevMask]]; if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) { prevPosT = bufferPos; prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; continue; } } } prevPosT = bufferPos; if (Test86MSByte(p[4])) { uint32_t src = ((uint32_t)p[4] << 24) | ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) | ((uint32_t)p[1]); uint32_t dest; for (;;) { uint8_t b; int b_index; dest = src - (ip + (uint32_t)bufferPos); if (prevMask == 0) break; b_index = kMaskToBitNumber[prevMask] * 8; b = (uint8_t)(dest >> (24 - b_index)); if (!Test86MSByte(b)) break; src = dest ^ ((1 << (32 - b_index)) - 1); } p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1)); p[3] = (uint8_t)(dest >> 16); p[2] = (uint8_t)(dest >> 8); p[1] = (uint8_t)dest; bufferPos += 5; } else { prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; } } zip->bcj_prevPosT = prevPosT; zip->bcj_prevMask = prevMask; zip->bcj_ip += (uint32_t)bufferPos; return (bufferPos); } +static void +arm_Init(struct _7zip *zip) +{ + zip->bcj_ip = 8; +} + +static size_t +arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + if (buf[i + 3] == 0xEB) { + // Calculate the transformed addr. + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= zip->bcj_ip + (uint32_t)i; + addr >>= 2; + + // Store the transformed addr in buf. + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } + + zip->bcj_ip += i; + + return i; +} + +static size_t +arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + + size_t i; + uint32_t instr; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = (uint32_t)buf[i] + | ((uint32_t)buf[i+1] << 8) + | ((uint32_t)buf[i+2] << 16) + | ((uint32_t)buf[i+3] << 24); + + if ((instr >> 26) == 0x25) { + /* BL instruction */ + addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2); + instr = 0x94000000 | (addr & 0x03FFFFFF); + + buf[i] = (uint8_t)instr; + buf[i+1] = (uint8_t)(instr >> 8); + buf[i+2] = (uint8_t)(instr >> 16); + buf[i+3] = (uint8_t)(instr >> 24); + } else if ((instr & 0x9F000000) == 0x90000000) { + /* ADRP instruction */ + addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC); + + /* Only convert values in the range +/-512 MiB. */ + if ((addr + 0x020000) & 0x1C0000) + continue; + + addr -= (zip->bcj_ip + (uint32_t)i) >> 12; + + instr &= 0x9000001F; + instr |= (addr & 3) << 29; + instr |= (addr & 0x03FFFC) << 3; + instr |= (0U - (addr & 0x020000)) & 0xE00000; + + buf[i] = (uint8_t)instr; + buf[i+1] = (uint8_t)(instr >> 8); + buf[i+2] = (uint8_t)(instr >> 16); + buf[i+3] = (uint8_t)(instr >> 24); + } + } + + zip->bcj_ip += i; + + return i; +} + /* * Brought from LZMA SDK. * * Bcj2.c -- Converter for x86 code (BCJ2) * 2008-10-04 : Igor Pavlov : Public domain * */ #define SZ_ERROR_DATA ARCHIVE_FAILED #define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) #define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) #define kNumTopBits 24 #define kTopValue ((uint32_t)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_READ_BYTE (*buffer++) #define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } #define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \ { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }} #define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; } #define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound) #define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; #define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; static ssize_t Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) { size_t inPos = 0, outPos = 0; const uint8_t *buf0, *buf1, *buf2, *buf3; size_t size0, size1, size2, size3; const uint8_t *buffer, *bufferLim; unsigned int i, j; size0 = zip->tmp_stream_bytes_remaining; buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0; size1 = zip->sub_stream_bytes_remaining[0]; buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1; size2 = zip->sub_stream_bytes_remaining[1]; buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2; size3 = zip->sub_stream_bytes_remaining[2]; buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3; buffer = buf3; bufferLim = buffer + size3; if (zip->bcj_state == 0) { /* * Initialize. */ zip->bcj2_prevByte = 0; for (i = 0; i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++) zip->bcj2_p[i] = kBitModelTotal >> 1; RC_INIT2; zip->bcj_state = 1; } /* * Gather the odd bytes of a previous call. */ for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) { outBuf[outPos++] = zip->odd_bcj[i]; zip->odd_bcj_size--; } if (outSize == 0) { zip->bcj2_outPos += outPos; return (outPos); } for (;;) { uint8_t b; CProb *prob; uint32_t bound; uint32_t ttt; size_t limit = size0 - inPos; if (outSize - outPos < limit) limit = outSize - outPos; if (zip->bcj_state == 1) { while (limit != 0) { uint8_t bb = buf0[inPos]; outBuf[outPos++] = bb; if (IsJ(zip->bcj2_prevByte, bb)) { zip->bcj_state = 2; break; } inPos++; zip->bcj2_prevByte = bb; limit--; } } if (limit == 0 || outPos == outSize) break; zip->bcj_state = 1; b = buf0[inPos++]; if (b == 0xE8) prob = zip->bcj2_p + zip->bcj2_prevByte; else if (b == 0xE9) prob = zip->bcj2_p + 256; else prob = zip->bcj2_p + 257; IF_BIT_0(prob) { UPDATE_0(prob) zip->bcj2_prevByte = b; } else { uint32_t dest; const uint8_t *v; uint8_t out[4]; UPDATE_1(prob) if (b == 0xE8) { v = buf1; if (size1 < 4) return SZ_ERROR_DATA; buf1 += 4; size1 -= 4; } else { v = buf2; if (size2 < 4) return SZ_ERROR_DATA; buf2 += 4; size2 -= 4; } dest = (((uint32_t)v[0] << 24) | ((uint32_t)v[1] << 16) | ((uint32_t)v[2] << 8) | ((uint32_t)v[3])) - ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4); out[0] = (uint8_t)dest; out[1] = (uint8_t)(dest >> 8); out[2] = (uint8_t)(dest >> 16); out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24); for (i = 0; i < 4 && outPos < outSize; i++) outBuf[outPos++] = out[i]; if (i < 4) { /* * Save odd bytes which we could not add into * the output buffer because of out of space. */ zip->odd_bcj_size = 4 -i; for (; i < 4; i++) { j = i - 4 + (unsigned)zip->odd_bcj_size; zip->odd_bcj[j] = out[i]; } break; } } } zip->tmp_stream_bytes_remaining -= inPos; zip->sub_stream_bytes_remaining[0] = size1; zip->sub_stream_bytes_remaining[1] = size2; zip->sub_stream_bytes_remaining[2] = bufferLim - buffer; zip->bcj2_outPos += outPos; return ((ssize_t)outPos); } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_cab.c b/contrib/libarchive/libarchive/archive_read_support_format_cab.c index 4d5029b1bd2e..3b552a84de12 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_cab.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_cab.c @@ -1,3228 +1,3228 @@ /*- * 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. */ snprintf(cab->format_name, sizeof(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 ((int)(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) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "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); #endif } 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); } 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]; + ((uint64_t)strm->next_in[3]) << 24 | + ((uint64_t)strm->next_in[2]) << 16 | + ((uint64_t)strm->next_in[5]) << 8 | + (uint64_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); } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_cpio.c b/contrib/libarchive/libarchive/archive_read_support_format_cpio.c index af94583c2a12..9ed003efd655 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_cpio.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_cpio.c @@ -1,1104 +1,1104 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * 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 /* #include */ /* See archive_platform.h */ #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_read_private.h" #define bin_magic_offset 0 #define bin_magic_size 2 #define bin_dev_offset 2 #define bin_dev_size 2 #define bin_ino_offset 4 #define bin_ino_size 2 #define bin_mode_offset 6 #define bin_mode_size 2 #define bin_uid_offset 8 #define bin_uid_size 2 #define bin_gid_offset 10 #define bin_gid_size 2 #define bin_nlink_offset 12 #define bin_nlink_size 2 #define bin_rdev_offset 14 #define bin_rdev_size 2 #define bin_mtime_offset 16 #define bin_mtime_size 4 #define bin_namesize_offset 20 #define bin_namesize_size 2 #define bin_filesize_offset 22 #define bin_filesize_size 4 #define bin_header_size 26 #define odc_magic_offset 0 #define odc_magic_size 6 #define odc_dev_offset 6 #define odc_dev_size 6 #define odc_ino_offset 12 #define odc_ino_size 6 #define odc_mode_offset 18 #define odc_mode_size 6 #define odc_uid_offset 24 #define odc_uid_size 6 #define odc_gid_offset 30 #define odc_gid_size 6 #define odc_nlink_offset 36 #define odc_nlink_size 6 #define odc_rdev_offset 42 #define odc_rdev_size 6 #define odc_mtime_offset 48 #define odc_mtime_size 11 #define odc_namesize_offset 59 #define odc_namesize_size 6 #define odc_filesize_offset 65 #define odc_filesize_size 11 #define odc_header_size 76 #define newc_magic_offset 0 #define newc_magic_size 6 #define newc_ino_offset 6 #define newc_ino_size 8 #define newc_mode_offset 14 #define newc_mode_size 8 #define newc_uid_offset 22 #define newc_uid_size 8 #define newc_gid_offset 30 #define newc_gid_size 8 #define newc_nlink_offset 38 #define newc_nlink_size 8 #define newc_mtime_offset 46 #define newc_mtime_size 8 #define newc_filesize_offset 54 #define newc_filesize_size 8 #define newc_devmajor_offset 62 #define newc_devmajor_size 8 #define newc_devminor_offset 70 #define newc_devminor_size 8 #define newc_rdevmajor_offset 78 #define newc_rdevmajor_size 8 #define newc_rdevminor_offset 86 #define newc_rdevminor_size 8 #define newc_namesize_offset 94 #define newc_namesize_size 8 #define newc_checksum_offset 102 #define newc_checksum_size 8 #define newc_header_size 110 /* * An afio large ASCII header, which they named itself. * afio utility uses this header, if a file size is larger than 2G bytes * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than * 0x7fffffff, which we cannot record to odc header because of its limit. * If not, uses odc header. */ #define afiol_magic_offset 0 #define afiol_magic_size 6 #define afiol_dev_offset 6 #define afiol_dev_size 8 /* hex */ #define afiol_ino_offset 14 #define afiol_ino_size 16 /* hex */ #define afiol_ino_m_offset 30 /* 'm' */ #define afiol_mode_offset 31 #define afiol_mode_size 6 /* oct */ #define afiol_uid_offset 37 #define afiol_uid_size 8 /* hex */ #define afiol_gid_offset 45 #define afiol_gid_size 8 /* hex */ #define afiol_nlink_offset 53 #define afiol_nlink_size 8 /* hex */ #define afiol_rdev_offset 61 #define afiol_rdev_size 8 /* hex */ #define afiol_mtime_offset 69 #define afiol_mtime_size 16 /* hex */ #define afiol_mtime_n_offset 85 /* 'n' */ #define afiol_namesize_offset 86 #define afiol_namesize_size 4 /* hex */ #define afiol_flag_offset 90 #define afiol_flag_size 4 /* hex */ #define afiol_xsize_offset 94 #define afiol_xsize_size 4 /* hex */ #define afiol_xsize_s_offset 98 /* 's' */ #define afiol_filesize_offset 99 #define afiol_filesize_size 16 /* hex */ #define afiol_filesize_c_offset 115 /* ':' */ #define afiol_header_size 116 struct links_entry { struct links_entry *next; struct links_entry *previous; unsigned int links; dev_t dev; int64_t ino; char *name; }; #define CPIO_MAGIC 0x13141516 struct cpio { int magic; int (*read_header)(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); struct links_entry *links_head; int64_t entry_bytes_remaining; int64_t entry_bytes_unconsumed; int64_t entry_offset; int64_t entry_padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; int option_pwb; }; static int64_t atol16(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive_read *, int); static int archive_read_format_cpio_options(struct archive_read *, const char *, const char *); static int archive_read_format_cpio_cleanup(struct archive_read *); static int archive_read_format_cpio_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cpio_skip(struct archive_read *); static int64_t be4(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_bin_le(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_newc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_odc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_afiol(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); static int64_t le4(const unsigned char *); static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry); int archive_read_support_format_cpio(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cpio *cpio; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cpio"); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } cpio->magic = CPIO_MAGIC; r = __archive_read_register_format(a, cpio, "cpio", archive_read_format_cpio_bid, archive_read_format_cpio_options, archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, archive_read_format_cpio_skip, NULL, archive_read_format_cpio_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cpio); return (ARCHIVE_OK); } static int archive_read_format_cpio_bid(struct archive_read *a, int best_bid) { const unsigned char *p; struct cpio *cpio; int bid; (void)best_bid; /* UNUSED */ cpio = (struct cpio *)(a->format->data); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); bid = 0; if (memcmp(p, "070707", 6) == 0) { /* ASCII cpio archive (odc, POSIX.1) */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070727", 6) == 0) { /* afio large ASCII cpio archive */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that almost hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070702", 6) == 0) { /* ASCII cpio archive (SVR4 with CRC) */ /* XXX TODO: Flag that we should check the CRC. XXX */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (p[0] * 256 + p[1] == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ } else if (p[0] + p[1] * 256 == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; /* Is more verification possible here? */ } else return (ARCHIVE_WARN); return (bid); } static int archive_read_format_cpio_options(struct archive_read *a, const char *key, const char *val) { struct cpio *cpio; int ret = ARCHIVE_FAILED; cpio = (struct cpio *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filenames as libarchive 2.x */ cpio->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, "cpio: hdrcharset option needs a character-set name"); else { cpio->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "pwb") == 0) { if (val != NULL && val[0] != 0) cpio->option_pwb = 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 int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; const void *h, *hl; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; int r; 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_read( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); if (r < ARCHIVE_WARN) return (r); /* Read name from buffer. */ h = __archive_read_ahead(a, namelength + name_pad, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, (const char *)h, namelength, 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 can't be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } cpio->entry_offset = 0; __archive_read_consume(a, namelength + name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { if (cpio->entry_bytes_remaining > 1024 * 1024) { archive_set_error(&a->archive, ENOMEM, "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); return (ARCHIVE_FATAL); } hl = __archive_read_ahead(a, (size_t)cpio->entry_bytes_remaining, NULL); if (hl == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_symlink_l(entry, (const char *)hl, (size_t)cpio->entry_bytes_remaining, 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, "Linkname can't be converted from %s to " "current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } __archive_read_consume(a, cpio->entry_bytes_remaining); cpio->entry_bytes_remaining = 0; } /* XXX TODO: If the full mode is 0160200, then this is a Solaris * ACL description for the following entry. Read this body * and parse it as a Solaris-style ACL, then read the next * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", - 11) == 0) { + 10) == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* Detect and record hardlinks to previously-extracted entries. */ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } return (r); } static int archive_read_format_cpio_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct cpio *cpio; cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_unconsumed) { __archive_read_consume(a, cpio->entry_bytes_unconsumed); cpio->entry_bytes_unconsumed = 0; } if (cpio->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) bytes_read = (ssize_t)cpio->entry_bytes_remaining; *size = bytes_read; cpio->entry_bytes_unconsumed = bytes_read; *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; return (ARCHIVE_OK); } else { if (cpio->entry_padding != __archive_read_consume(a, cpio->entry_padding)) { return (ARCHIVE_FATAL); } cpio->entry_padding = 0; *buff = NULL; *size = 0; *offset = cpio->entry_offset; return (ARCHIVE_EOF); } } static int archive_read_format_cpio_skip(struct archive_read *a) { struct cpio *cpio = (struct cpio *)(a->format->data); int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding + cpio->entry_bytes_unconsumed; if (to_skip != __archive_read_consume(a, to_skip)) { return (ARCHIVE_FATAL); } cpio->entry_bytes_remaining = 0; cpio->entry_padding = 0; cpio->entry_bytes_unconsumed = 0; return (ARCHIVE_OK); } /* * Skip forward to the next cpio newc header by searching for the * 07070[12] string. This should be generalized and merged with * find_odc_header below. */ static int is_hex(const char *p, size_t len) { while (len-- > 0) { if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')) ++p; else return (0); } return (1); } static int find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, newc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("07070", p, 5) == 0 && (p[5] == '1' || p[5] == '2') && is_hex(p, newc_header_size)) return (ARCHIVE_OK); /* * Scan ahead until we find something that looks * like a newc header. */ while (p + newc_header_size <= q) { switch (p[5]) { case '1': case '2': if (memcmp("07070", p, 5) == 0 && is_hex(p, newc_header_size)) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_newc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; int r; r = find_newc_header(a); if (r < ARCHIVE_WARN) return (r); /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, newc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out hex fields. */ header = (const char *)h; if (memcmp(header + newc_magic_offset, "070701", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)"; } else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)"; } else { /* TODO: Abort here? */ } archive_entry_set_devmajor(entry, (dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size)); archive_entry_set_devminor(entry, (dev_t)atol16(header + newc_devminor_offset, newc_devminor_size)); archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol16(header + newc_mode_offset, newc_mode_size)); archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size)); archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size)); archive_entry_set_rdevmajor(entry, (dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size)); archive_entry_set_rdevminor(entry, (dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size)); archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0); *namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size); /* Pad name to 2 more than a multiple of 4. */ *name_pad = (2 - *namelength) & 3; /* Make sure that the padded name length fits into size_t. */ if (*name_pad > SIZE_MAX - *namelength) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cpio archive has invalid namelength"); return (ARCHIVE_FATAL); } /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol16(header + newc_filesize_offset, newc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); /* Pad file contents to a multiple of 4. */ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; __archive_read_consume(a, newc_header_size); return (r); } /* * Skip forward to the next cpio odc header by searching for the * 070707 string. This is a hand-optimized search that could * probably be easily generalized to handle all character-based * cpio variants. */ static int is_octal(const char *p, size_t len) { while (len-- > 0) { if (*p < '0' || *p > '7') return (0); ++p; } return (1); } static int is_afio_large(const char *h, size_t len) { if (len < afiol_header_size) return (0); if (h[afiol_ino_m_offset] != 'm' || h[afiol_mtime_n_offset] != 'n' || h[afiol_xsize_s_offset] != 's' || h[afiol_filesize_c_offset] != ':') return (0); if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset)) return (0); if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset)) return (0); if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset)) return (0); if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size)) return (0); return (1); } static int find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, odc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) return (ARCHIVE_OK); if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; return (ARCHIVE_OK); } /* * Scan ahead until we find something that looks * like an odc header. */ while (p + odc_header_size <= q) { switch (p[5]) { case '7': if ((memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) || (memcmp("070727", p, 6) == 0 && is_afio_large(p, q - p))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (p[4] == '2') a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_odc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; int r; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; /* Find the start of the next header. */ r = find_odc_header(a); if (r < ARCHIVE_WARN) return (r); if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) { int r2 = (header_afiol(a, cpio, entry, namelength, name_pad)); if (r2 == ARCHIVE_OK) return (r); else return (r2); } /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, odc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol8(header + odc_dev_offset, odc_dev_size)); archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + odc_mode_offset, odc_mode_size)); archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size)); archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol8(header + odc_rdev_offset, odc_rdev_size)); archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0); *namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size); *name_pad = 0; /* No padding of filename. */ /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol8(header + odc_filesize_offset, odc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, odc_header_size); return (r); } /* * NOTE: if a filename suffix is ".z", it is the file gziped by afio. * it would be nice that we can show uncompressed file size and we can * uncompressed file contents automatically, unfortunately we have nothing * to get a uncompressed file size while reading each header. It means * we also cannot uncompress file contents under our framework. */ static int header_afiol(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; a->archive.archive_format_name = "afio large ASCII"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, afiol_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size)); archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size)); archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size)); archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size)); archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0); *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size); *name_pad = 0; /* No padding of filename. */ cpio->entry_bytes_remaining = atol16(header + afiol_filesize_offset, afiol_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, afiol_header_size); return (ARCHIVE_OK); } static int header_bin_le(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256); archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256); archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256); if (cpio->option_pwb) { /* turn off random bits left over from V6 inode */ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777); if ((archive_entry_mode(entry) & AE_IFMT) == 0) archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG); } archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256); archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256); archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256); archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256); archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = le4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int header_bin_be(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]); archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]); archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]); if (cpio->option_pwb) { /* turn off random bits left over from V6 inode */ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777); if ((archive_entry_mode(entry) & AE_IFMT) == 0) archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG); } archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]); archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]); archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]); archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]); archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1]; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = be4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int archive_read_format_cpio_cleanup(struct archive_read *a) { struct cpio *cpio; cpio = (struct cpio *)(a->format->data); /* Free inode->name map */ while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } free(cpio); (a->format->data) = NULL; return (ARCHIVE_OK); } static int64_t le4(const unsigned char *p) { - return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8)); + return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8)); } static int64_t be4(const unsigned char *p) { - return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3])); + return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); } /* * 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 atol8(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else return (l); p++; l <<= 3; l |= digit; } return (l); } static int64_t atol16(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= 'a' && *p <= 'f') digit = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') digit = *p - 'A' + 10; else if (*p >= '0' && *p <= '9') digit = *p - '0'; else return (l); p++; l <<= 4; l |= digit; } return (l); } static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry) { struct links_entry *le; dev_t dev; int64_t ino; if (archive_entry_nlink(entry) <= 1) return (ARCHIVE_OK); dev = archive_entry_dev(entry); ino = archive_entry_ino64(entry); /* * First look in the list of multiply-linked files. If we've * already dumped it, convert this entry to a hard link entry. */ for (le = cpio->links_head; le; le = le->next) { if (le->dev == dev && le->ino == ino) { archive_entry_copy_hardlink(entry, le->name); if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (cpio->links_head == le) cpio->links_head = le->next; free(le->name); free(le); } return (ARCHIVE_OK); } } le = (struct links_entry *)malloc(sizeof(struct links_entry)); if (le == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; le->name = strdup(archive_entry_pathname(entry)); if (le->name == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c b/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c index 390ab84f43e3..b3f537bec800 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c @@ -1,3279 +1,3279 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2009 Andreas Henriksson * 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_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #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_read_private.h" #include "archive_string.h" /* * An overview of ISO 9660 format: * * Each disk is laid out as follows: * * 32k reserved for private use * * Volume descriptor table. Each volume descriptor * is 2k and specifies basic format information. * The "Primary Volume Descriptor" (PVD) is defined by the * standard and should always be present; other volume * descriptors include various vendor-specific extensions. * * Files and directories. Each file/dir is specified by * an "extent" (starting sector and length in bytes). * Dirs are just files with directory records packed one * after another. The PVD contains a single dir entry * specifying the location of the root directory. Everything * else follows from there. * * This module works by first reading the volume descriptors, then * building a list of directory entries, sorted by starting * sector. At each step, I look for the earliest dir entry that * hasn't yet been read, seek forward to that location and read * that entry. If it's a dir, I slurp in the new dir entries and * add them to the heap; if it's a regular file, I return the * corresponding archive_entry and wait for the client to request * the file body. This strategy allows us to read most compliant * CDs with a single pass through the data, as required by libarchive. */ #define LOGICAL_BLOCK_SIZE 2048 #define SYSTEM_AREA_BLOCK 16 /* Structure of on-disk primary volume descriptor. */ #define PVD_type_offset 0 #define PVD_type_size 1 #define PVD_id_offset (PVD_type_offset + PVD_type_size) #define PVD_id_size 5 #define PVD_version_offset (PVD_id_offset + PVD_id_size) #define PVD_version_size 1 #define PVD_reserved1_offset (PVD_version_offset + PVD_version_size) #define PVD_reserved1_size 1 #define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size) #define PVD_system_id_size 32 #define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size) #define PVD_volume_id_size 32 #define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size) #define PVD_reserved2_size 8 #define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size) #define PVD_volume_space_size_size 8 #define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size) #define PVD_reserved3_size 32 #define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size) #define PVD_volume_set_size_size 4 #define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size) #define PVD_volume_sequence_number_size 4 #define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size) #define PVD_logical_block_size_size 4 #define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size) #define PVD_path_table_size_size 8 #define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size) #define PVD_type_1_path_table_size 4 #define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size) #define PVD_opt_type_1_path_table_size 4 #define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size) #define PVD_type_m_path_table_size 4 #define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size) #define PVD_opt_type_m_path_table_size 4 #define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size) #define PVD_root_directory_record_size 34 #define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size) #define PVD_volume_set_id_size 128 #define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size) #define PVD_publisher_id_size 128 #define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size) #define PVD_preparer_id_size 128 #define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size) #define PVD_application_id_size 128 #define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size) #define PVD_copyright_file_id_size 37 #define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size) #define PVD_abstract_file_id_size 37 #define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size) #define PVD_bibliographic_file_id_size 37 #define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size) #define PVD_creation_date_size 17 #define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size) #define PVD_modification_date_size 17 #define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size) #define PVD_expiration_date_size 17 #define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size) #define PVD_effective_date_size 17 #define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size) #define PVD_file_structure_version_size 1 #define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size) #define PVD_reserved4_size 1 #define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size) #define PVD_application_data_size 512 #define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size) #define PVD_reserved5_size (2048 - PVD_reserved5_offset) /* TODO: It would make future maintenance easier to just hardcode the * above values. In particular, ECMA119 states the offsets as part of * the standard. That would eliminate the need for the following check.*/ #if PVD_reserved5_offset != 1395 #error PVD offset and size definitions are wrong. #endif /* Structure of optional on-disk supplementary volume descriptor. */ #define SVD_type_offset 0 #define SVD_type_size 1 #define SVD_id_offset (SVD_type_offset + SVD_type_size) #define SVD_id_size 5 #define SVD_version_offset (SVD_id_offset + SVD_id_size) #define SVD_version_size 1 /* ... */ #define SVD_reserved1_offset 72 #define SVD_reserved1_size 8 #define SVD_volume_space_size_offset 80 #define SVD_volume_space_size_size 8 #define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size) #define SVD_escape_sequences_size 32 /* ... */ #define SVD_logical_block_size_offset 128 #define SVD_logical_block_size_size 4 #define SVD_type_L_path_table_offset 140 #define SVD_type_M_path_table_offset 148 /* ... */ #define SVD_root_directory_record_offset 156 #define SVD_root_directory_record_size 34 #define SVD_file_structure_version_offset 881 #define SVD_reserved2_offset 882 #define SVD_reserved2_size 1 #define SVD_reserved3_offset 1395 #define SVD_reserved3_size 653 /* ... */ /* FIXME: validate correctness of last SVD entry offset. */ /* Structure of an on-disk directory record. */ /* Note: ISO9660 stores each multi-byte integer twice, once in * each byte order. The sizes here are the size of just one * of the two integers. (This is why the offset of a field isn't * the same as the offset+size of the previous field.) */ #define DR_length_offset 0 #define DR_length_size 1 #define DR_ext_attr_length_offset 1 #define DR_ext_attr_length_size 1 #define DR_extent_offset 2 #define DR_extent_size 4 #define DR_size_offset 10 #define DR_size_size 4 #define DR_date_offset 18 #define DR_date_size 7 #define DR_flags_offset 25 #define DR_flags_size 1 #define DR_file_unit_size_offset 26 #define DR_file_unit_size_size 1 #define DR_interleave_offset 27 #define DR_interleave_size 1 #define DR_volume_sequence_number_offset 28 #define DR_volume_sequence_number_size 2 #define DR_name_len_offset 32 #define DR_name_len_size 1 #define DR_name_offset 33 #ifdef HAVE_ZLIB_H static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; struct zisofs { /* Set 1 if this file compressed by paged zlib */ int pz; int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; int initialized; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; uint32_t pz_offset; unsigned char header[16]; size_t header_avail; int header_passed; unsigned char *block_pointers; size_t block_pointers_alloc; size_t block_pointers_size; size_t block_pointers_avail; size_t block_off; uint32_t block_avail; z_stream stream; int stream_valid; }; #else struct zisofs { /* Set 1 if this file compressed by paged zlib */ int pz; }; #endif struct content { uint64_t offset;/* Offset on disk. */ uint64_t size; /* File size in bytes. */ struct content *next; }; /* In-memory storage for a directory record. */ struct file_info { struct file_info *use_next; struct file_info *parent; struct file_info *next; struct file_info *re_next; int subdirs; uint64_t key; /* Heap Key. */ uint64_t offset; /* Offset on disk. */ uint64_t size; /* File size in bytes. */ uint32_t ce_offset; /* Offset of CE. */ uint32_t ce_size; /* Size of CE. */ char rr_moved; /* Flag to rr_moved. */ char rr_moved_has_re_only; char re; /* Having RRIP "RE" extension. */ char re_descendant; uint64_t cl_offset; /* Having RRIP "CL" extension. */ int birthtime_is_set; time_t birthtime; /* File created time. */ time_t mtime; /* File last modified time. */ time_t atime; /* File last accessed time. */ time_t ctime; /* File attribute change time. */ uint64_t rdev; /* Device number. */ mode_t mode; uid_t uid; gid_t gid; int64_t number; int nlinks; struct archive_string name; /* Pathname */ unsigned char *utf16be_name; size_t utf16be_bytes; char name_continues; /* Non-zero if name continues */ struct archive_string symlink; char symlink_continues; /* Non-zero if link continues */ /* Set 1 if this file compressed by paged zlib(zisofs) */ int pz; int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; /* Set 1 if this file is multi extent. */ int multi_extent; struct { struct content *first; struct content **last; } contents; struct { struct file_info *first; struct file_info **last; } rede_files; }; struct heap_queue { struct file_info **files; int allocated; int used; }; struct iso9660 { int magic; #define ISO9660_MAGIC 0x96609660 int opt_support_joliet; int opt_support_rockridge; struct archive_string pathname; char seenRockridge; /* Set true if RR extensions are used. */ char seenSUSP; /* Set true if SUSP is being used. */ char seenJoliet; unsigned char suspOffset; struct file_info *rr_moved; struct read_ce_queue { struct read_ce_req { uint64_t offset;/* Offset of CE on disk. */ struct file_info *file; } *reqs; int cnt; int allocated; } read_ce_req; int64_t previous_number; struct archive_string previous_pathname; struct file_info *use_files; struct heap_queue pending_files; struct { struct file_info *first; struct file_info **last; } cache_files; struct { struct file_info *first; struct file_info **last; } re_files; uint64_t current_position; ssize_t logical_block_size; uint64_t volume_size; /* Total size of volume in bytes. */ int32_t volume_block;/* Total size of volume in logical blocks. */ struct vd { int location; /* Location of Extent. */ uint32_t size; } primary, joliet; int64_t entry_sparse_offset; int64_t entry_bytes_remaining; size_t entry_bytes_unconsumed; struct zisofs entry_zisofs; struct content *entry_content; struct archive_string_conv *sconv_utf16be; /* * Buffers for a full pathname in UTF-16BE in Joliet extensions. */ #define UTF16_NAME_MAX 1024 unsigned char *utf16be_path; size_t utf16be_path_len; unsigned char *utf16be_previous_path; size_t utf16be_previous_path_len; /* Null buffer used in bidder to improve its performance. */ unsigned char null[2048]; }; static int archive_read_format_iso9660_bid(struct archive_read *, int); static int archive_read_format_iso9660_options(struct archive_read *, const char *, const char *); static int archive_read_format_iso9660_cleanup(struct archive_read *); static int archive_read_format_iso9660_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_iso9660_read_data_skip(struct archive_read *); static int archive_read_format_iso9660_read_header(struct archive_read *, struct archive_entry *); static const char *build_pathname(struct archive_string *, struct file_info *, int); static int build_pathname_utf16be(unsigned char *, size_t, size_t *, struct file_info *); #if DEBUG static void dump_isodirrec(FILE *, const unsigned char *isodirrec); #endif static time_t time_from_tm(struct tm *); static time_t isodate17(const unsigned char *); static time_t isodate7(const unsigned char *); static int isBootRecord(struct iso9660 *, const unsigned char *); static int isVolumePartition(struct iso9660 *, const unsigned char *); static int isVDSetTerminator(struct iso9660 *, const unsigned char *); static int isJolietSVD(struct iso9660 *, const unsigned char *); static int isSVD(struct iso9660 *, const unsigned char *); static int isEVD(struct iso9660 *, const unsigned char *); static int isPVD(struct iso9660 *, const unsigned char *); static int next_cache_entry(struct archive_read *, struct iso9660 *, struct file_info **); static int next_entry_seek(struct archive_read *, struct iso9660 *, struct file_info **); static struct file_info * parse_file_info(struct archive_read *a, struct file_info *parent, const unsigned char *isodirrec, size_t reclen); static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *start, const unsigned char *end); static int register_CE(struct archive_read *a, int32_t location, struct file_info *file); static int read_CE(struct archive_read *a, struct iso9660 *iso9660); static void parse_rockridge_NM1(struct file_info *, const unsigned char *, int); static void parse_rockridge_SL1(struct file_info *, const unsigned char *, int); static void parse_rockridge_TF1(struct file_info *, const unsigned char *, int); static void parse_rockridge_ZF1(struct file_info *, const unsigned char *, int); static void register_file(struct iso9660 *, struct file_info *); static void release_files(struct iso9660 *); static unsigned toi(const void *p, int n); static inline void re_add_entry(struct iso9660 *, struct file_info *); static inline struct file_info * re_get_entry(struct iso9660 *); static inline int rede_add_entry(struct file_info *); static inline struct file_info * rede_get_entry(struct file_info *); static inline void cache_add_entry(struct iso9660 *iso9660, struct file_info *file); static inline struct file_info *cache_get_entry(struct iso9660 *iso9660); static int heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct file_info *file, uint64_t key); static struct file_info *heap_get_entry(struct heap_queue *heap); #define add_entry(arch, iso9660, file) \ heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset) #define next_entry(iso9660) \ heap_get_entry(&((iso9660)->pending_files)) int archive_read_support_format_iso9660(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct iso9660 *iso9660; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660"); iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } iso9660->magic = ISO9660_MAGIC; iso9660->cache_files.first = NULL; iso9660->cache_files.last = &(iso9660->cache_files.first); iso9660->re_files.first = NULL; iso9660->re_files.last = &(iso9660->re_files.first); /* Enable to support Joliet extensions by default. */ iso9660->opt_support_joliet = 1; /* Enable to support Rock Ridge extensions by default. */ iso9660->opt_support_rockridge = 1; r = __archive_read_register_format(a, iso9660, "iso9660", archive_read_format_iso9660_bid, archive_read_format_iso9660_options, archive_read_format_iso9660_read_header, archive_read_format_iso9660_read_data, archive_read_format_iso9660_read_data_skip, NULL, archive_read_format_iso9660_cleanup, NULL, NULL); if (r != ARCHIVE_OK) { free(iso9660); return (r); } return (ARCHIVE_OK); } static int archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) { struct iso9660 *iso9660; ssize_t bytes_read; const unsigned char *p; int seenTerminator; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 48) return (-1); iso9660 = (struct iso9660 *)(a->format->data); /* * Skip the first 32k (reserved area) and get the first * 8 sectors of the volume descriptor table. Of course, * if the I/O layer gives us more, we'll take it. */ #define RESERVED_AREA (SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE) p = __archive_read_ahead(a, RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE, &bytes_read); if (p == NULL) return (-1); /* Skip the reserved area. */ bytes_read -= RESERVED_AREA; p += RESERVED_AREA; /* Check each volume descriptor. */ seenTerminator = 0; for (; bytes_read > LOGICAL_BLOCK_SIZE; bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) { /* Do not handle undefined Volume Descriptor Type. */ if (p[0] >= 4 && p[0] <= 254) return (0); /* Standard Identifier must be "CD001" */ if (memcmp(p + 1, "CD001", 5) != 0) return (0); if (isPVD(iso9660, p)) continue; if (!iso9660->joliet.location) { if (isJolietSVD(iso9660, p)) continue; } if (isBootRecord(iso9660, p)) continue; if (isEVD(iso9660, p)) continue; if (isSVD(iso9660, p)) continue; if (isVolumePartition(iso9660, p)) continue; if (isVDSetTerminator(iso9660, p)) { seenTerminator = 1; break; } return (0); } /* * ISO 9660 format must have Primary Volume Descriptor and * Volume Descriptor Set Terminator. */ if (seenTerminator && iso9660->primary.location > 16) return (48); /* We didn't find a valid PVD; return a bid of zero. */ return (0); } static int archive_read_format_iso9660_options(struct archive_read *a, const char *key, const char *val) { struct iso9660 *iso9660; iso9660 = (struct iso9660 *)(a->format->data); if (strcmp(key, "joliet") == 0) { if (val == NULL || strcmp(val, "off") == 0 || strcmp(val, "ignore") == 0 || strcmp(val, "disable") == 0 || strcmp(val, "0") == 0) iso9660->opt_support_joliet = 0; else iso9660->opt_support_joliet = 1; return (ARCHIVE_OK); } if (strcmp(key, "rockridge") == 0 || strcmp(key, "Rockridge") == 0) { iso9660->opt_support_rockridge = val != NULL; 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 isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset, unsigned bytes) { while (bytes >= sizeof(iso9660->null)) { if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null))) return (0); offset += sizeof(iso9660->null); bytes -= sizeof(iso9660->null); } if (bytes) return memcmp(iso9660->null, h + offset, bytes) == 0; else return (1); } static int isBootRecord(struct iso9660 *iso9660, const unsigned char *h) { (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Boot Record must be 0. */ if (h[0] != 0) return (0); /* Volume Descriptor Version must be 1. */ if (h[6] != 1) return (0); return (1); } static int isVolumePartition(struct iso9660 *iso9660, const unsigned char *h) { int32_t location; /* Type of the Volume Partition Descriptor must be 3. */ if (h[0] != 3) return (0); /* Volume Descriptor Version must be 1. */ if (h[6] != 1) return (0); /* Unused Field */ if (h[7] != 0) return (0); location = archive_le32dec(h + 72); if (location <= SYSTEM_AREA_BLOCK || location >= iso9660->volume_block) return (0); if ((uint32_t)location != archive_be32dec(h + 76)) return (0); return (1); } static int isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h) { (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Set Terminator must be 255. */ if (h[0] != 255) return (0); /* Volume Descriptor Version must be 1. */ if (h[6] != 1) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, 7, 2048-7)) return (0); return (1); } static int isJolietSVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; /* Check if current sector is a kind of Supplementary Volume * Descriptor. */ if (!isSVD(iso9660, h)) return (0); /* FIXME: do more validations according to joliet spec. */ /* check if this SVD contains joliet extension! */ p = h + SVD_escape_sequences_offset; /* N.B. Joliet spec says p[1] == '\\', but.... */ if (p[0] == '%' && p[1] == '/') { int level = 0; if (p[2] == '@') level = 1; else if (p[2] == 'C') level = 2; else if (p[2] == 'E') level = 3; else /* not joliet */ return (0); iso9660->seenJoliet = level; } else /* not joliet */ return (0); logical_block_size = archive_le16dec(h + SVD_logical_block_size_offset); volume_block = archive_le32dec(h + SVD_volume_space_size_offset); iso9660->logical_block_size = logical_block_size; iso9660->volume_block = volume_block; iso9660->volume_size = logical_block_size * (uint64_t)volume_block; /* Read Root Directory Record in Volume Descriptor. */ p = h + SVD_root_directory_record_offset; iso9660->joliet.location = archive_le32dec(p + DR_extent_offset); iso9660->joliet.size = archive_le32dec(p + DR_size_offset); return (48); } static int isSVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; (void)iso9660; /* UNUSED */ /* Type 2 means it's a SVD. */ if (h[SVD_type_offset] != 2) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size)) return (0); if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size)) return (0); if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size)) return (0); /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[SVD_file_structure_version_offset] != 1) return (0); logical_block_size = archive_le16dec(h + SVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); volume_block = archive_le32dec(h + SVD_volume_space_size_offset); if (volume_block <= SYSTEM_AREA_BLOCK+4) return (0); /* Location of Occurrence of Type L Path Table must be * available location, * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_le32dec(h+SVD_type_L_path_table_offset); if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block) return (0); /* The Type M Path Table must be at a valid location (WinISO * and probably other programs omit this, so we allow zero) * * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_be32dec(h+SVD_type_M_path_table_offset); if ((location > 0 && location < SYSTEM_AREA_BLOCK+2) || location >= volume_block) return (0); /* Read Root Directory Record in Volume Descriptor. */ p = h + SVD_root_directory_record_offset; if (p[DR_length_offset] != 34) return (0); return (48); } static int isEVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; (void)iso9660; /* UNUSED */ /* Type of the Enhanced Volume Descriptor must be 2. */ if (h[PVD_type_offset] != 2) return (0); /* EVD version must be 2. */ if (h[PVD_version_offset] != 2) return (0); /* Reserved field must be 0. */ if (h[PVD_reserved1_offset] != 0) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger * restriction on this field. */ logical_block_size = archive_le16dec(h + PVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); volume_block = archive_le32dec(h + PVD_volume_space_size_offset); if (volume_block <= SYSTEM_AREA_BLOCK+4) return (0); /* File structure version must be 2 for ISO9660:1999. */ if (h[PVD_file_structure_version_offset] != 2) return (0); /* Location of Occurrence of Type L Path Table must be * available location, * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_le32dec(h+PVD_type_1_path_table_offset); if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block) return (0); /* Location of Occurrence of Type M Path Table must be * available location, * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_be32dec(h+PVD_type_m_path_table_offset); if ((location > 0 && location < SYSTEM_AREA_BLOCK+2) || location >= volume_block) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size)) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) return (0); /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; if (p[DR_length_offset] != 34) return (0); return (48); } static int isPVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; int i; /* Type of the Primary Volume Descriptor must be 1. */ if (h[PVD_type_offset] != 1) return (0); /* PVD version must be 1. */ if (h[PVD_version_offset] != 1) return (0); /* Reserved field must be 0. */ if (h[PVD_reserved1_offset] != 0) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger * restriction on this field. */ logical_block_size = archive_le16dec(h + PVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); volume_block = archive_le32dec(h + PVD_volume_space_size_offset); if (volume_block <= SYSTEM_AREA_BLOCK+4) return (0); /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[PVD_file_structure_version_offset] != 1) return (0); /* Location of Occurrence of Type L Path Table must be * available location, * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_le32dec(h+PVD_type_1_path_table_offset); if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block) return (0); /* The Type M Path Table must also be at a valid location * (although ECMA 119 requires a Type M Path Table, WinISO and * probably other programs omit it, so we permit a zero here) * * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_be32dec(h+PVD_type_m_path_table_offset); if ((location > 0 && location < SYSTEM_AREA_BLOCK+2) || location >= volume_block) return (0); /* Reserved field must be 0. */ /* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */ for (i = 0; i < PVD_reserved4_size; ++i) if (h[PVD_reserved4_offset + i] != 0 && h[PVD_reserved4_offset + i] != 0x20) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) return (0); /* XXX TODO: Check other values for sanity; reject more * malformed PVDs. XXX */ /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; if (p[DR_length_offset] != 34) return (0); if (!iso9660->primary.location) { iso9660->logical_block_size = logical_block_size; iso9660->volume_block = volume_block; iso9660->volume_size = logical_block_size * (uint64_t)volume_block; iso9660->primary.location = archive_le32dec(p + DR_extent_offset); iso9660->primary.size = archive_le32dec(p + DR_size_offset); } return (48); } static int read_children(struct archive_read *a, struct file_info *parent) { struct iso9660 *iso9660; const unsigned char *b, *p; struct file_info *multi; size_t step, skip_size; iso9660 = (struct iso9660 *)(a->format->data); /* flush any remaining bytes from the last round to ensure * we're positioned */ if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); iso9660->entry_bytes_unconsumed = 0; } if (iso9660->current_position > parent->offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order directory (%s) %jd > %jd", parent->name.s, (intmax_t)iso9660->current_position, (intmax_t)parent->offset); return (ARCHIVE_WARN); } if (parent->offset + parent->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Directory is beyond end-of-media: %s", parent->name.s); return (ARCHIVE_WARN); } if (iso9660->current_position < parent->offset) { int64_t skipsize; skipsize = parent->offset - iso9660->current_position; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); iso9660->current_position = parent->offset; } step = (size_t)(((parent->size + iso9660->logical_block_size -1) / iso9660->logical_block_size) * iso9660->logical_block_size); b = __archive_read_ahead(a, step, NULL); if (b == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } iso9660->current_position += step; multi = NULL; skip_size = step; while (step) { p = b; b += iso9660->logical_block_size; step -= iso9660->logical_block_size; for (; *p != 0 && p + DR_name_offset < b && p + *p <= b; p += *p) { struct file_info *child; /* N.B.: these special directory identifiers * are 8 bit "values" even on a * Joliet CD with UCS-2 (16bit) encoding. */ /* Skip '.' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\0') continue; /* Skip '..' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\001') continue; child = parse_file_info(a, parent, p, b - p); if (child == NULL) { __archive_read_consume(a, skip_size); return (ARCHIVE_FATAL); } if (child->cl_offset == 0 && (child->multi_extent || multi != NULL)) { struct content *con; if (multi == NULL) { multi = child; multi->contents.first = NULL; multi->contents.last = &(multi->contents.first); } con = malloc(sizeof(struct content)); if (con == NULL) { archive_set_error( &a->archive, ENOMEM, "No memory for multi extent"); __archive_read_consume(a, skip_size); return (ARCHIVE_FATAL); } con->offset = child->offset; con->size = child->size; con->next = NULL; *multi->contents.last = con; multi->contents.last = &(con->next); if (multi == child) { if (add_entry(a, iso9660, child) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { multi->size += child->size; if (!child->multi_extent) multi = NULL; } } else if (add_entry(a, iso9660, child) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } __archive_read_consume(a, skip_size); /* Read data which recorded by RRIP "CE" extension. */ if (read_CE(a, iso9660) != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static int choose_volume(struct archive_read *a, struct iso9660 *iso9660) { struct file_info *file; int64_t skipsize; struct vd *vd; const void *block; char seenJoliet; vd = &(iso9660->primary); if (!iso9660->opt_support_joliet) iso9660->seenJoliet = 0; if (iso9660->seenJoliet && vd->location > iso9660->joliet.location) /* This condition is unlikely; by way of caution. */ vd = &(iso9660->joliet); skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); iso9660->current_position = skipsize; block = __archive_read_ahead(a, vd->size, NULL); if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } /* * While reading Root Directory, flag seenJoliet must be zero to * avoid converting special name 0x00(Current Directory) and * next byte to UCS2. */ seenJoliet = iso9660->seenJoliet;/* Save flag. */ iso9660->seenJoliet = 0; file = parse_file_info(a, NULL, block, vd->size); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; /* * If the iso image has both RockRidge and Joliet, we preferentially * use RockRidge Extensions rather than Joliet ones. */ if (vd == &(iso9660->primary) && iso9660->seenRockridge && iso9660->seenJoliet) iso9660->seenJoliet = 0; if (vd == &(iso9660->primary) && !iso9660->seenRockridge && iso9660->seenJoliet) { /* Switch reading data from primary to joliet. */ vd = &(iso9660->joliet); skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location; skipsize -= iso9660->current_position; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); iso9660->current_position += skipsize; block = __archive_read_ahead(a, vd->size, NULL); if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } iso9660->seenJoliet = 0; file = parse_file_info(a, NULL, block, vd->size); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; } /* Store the root directory in the pending list. */ if (add_entry(a, iso9660, file) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->seenRockridge) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE; a->archive.archive_format_name = "ISO9660 with Rockridge extensions"; } return (ARCHIVE_OK); } static int archive_read_format_iso9660_read_header(struct archive_read *a, struct archive_entry *entry) { struct iso9660 *iso9660; struct file_info *file; int r, rd_r = ARCHIVE_OK; iso9660 = (struct iso9660 *)(a->format->data); if (!a->archive.archive_format) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; } if (iso9660->current_position == 0) { r = choose_volume(a, iso9660); if (r != ARCHIVE_OK) return (r); } file = NULL;/* Eliminate a warning. */ /* Get the next entry that appears after the current offset. */ r = next_entry_seek(a, iso9660, &file); if (r != ARCHIVE_OK) return (r); if (iso9660->seenJoliet) { /* * Convert UTF-16BE of a filename to local locale MBS * and store the result into a filename field. */ if (iso9660->sconv_utf16be == NULL) { iso9660->sconv_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } if (iso9660->utf16be_path == NULL) { iso9660->utf16be_path = malloc(UTF16_NAME_MAX); if (iso9660->utf16be_path == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } } if (iso9660->utf16be_previous_path == NULL) { iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX); if (iso9660->utf16be_previous_path == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } } iso9660->utf16be_path_len = 0; if (build_pathname_utf16be(iso9660->utf16be_path, UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname is too long"); return (ARCHIVE_FATAL); } r = archive_entry_copy_pathname_l(entry, (const char *)iso9660->utf16be_path, iso9660->utf16be_path_len, iso9660->sconv_utf16be); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "No 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( iso9660->sconv_utf16be)); rd_r = ARCHIVE_WARN; } } else { const char *path = build_pathname(&iso9660->pathname, file, 0); if (path == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname is too long"); return (ARCHIVE_FATAL); } else { archive_string_empty(&iso9660->pathname); archive_entry_set_pathname(entry, path); } } iso9660->entry_bytes_remaining = file->size; /* Offset for sparse-file-aware clients. */ iso9660->entry_sparse_offset = 0; if (file->offset + file->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File is beyond end-of-media: %s", archive_entry_pathname(entry)); iso9660->entry_bytes_remaining = 0; return (ARCHIVE_WARN); } /* Set up the entry structure with information about this entry. */ archive_entry_set_mode(entry, file->mode); archive_entry_set_uid(entry, file->uid); archive_entry_set_gid(entry, file->gid); archive_entry_set_nlink(entry, file->nlinks); if (file->birthtime_is_set) archive_entry_set_birthtime(entry, file->birthtime, 0); else archive_entry_unset_birthtime(entry); archive_entry_set_mtime(entry, file->mtime, 0); archive_entry_set_ctime(entry, file->ctime, 0); archive_entry_set_atime(entry, file->atime, 0); /* N.B.: Rock Ridge supports 64-bit device numbers. */ archive_entry_set_rdev(entry, (dev_t)file->rdev); archive_entry_set_size(entry, iso9660->entry_bytes_remaining); if (file->symlink.s != NULL) archive_entry_copy_symlink(entry, file->symlink.s); /* Note: If the input isn't seekable, we can't rewind to * return the same body again, so if the next entry refers to * the same data, we have to return it as a hardlink to the * original entry. */ if (file->number != -1 && file->number == iso9660->previous_number) { if (iso9660->seenJoliet) { r = archive_entry_copy_hardlink_l(entry, (const char *)iso9660->utf16be_previous_path, iso9660->utf16be_previous_path_len, iso9660->sconv_utf16be); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "No memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name( iso9660->sconv_utf16be)); rd_r = ARCHIVE_WARN; } } else archive_entry_set_hardlink(entry, iso9660->previous_pathname.s); archive_entry_unset_size(entry); iso9660->entry_bytes_remaining = 0; return (rd_r); } if ((file->mode & AE_IFMT) != AE_IFDIR && file->offset < iso9660->current_position) { int64_t r64; r64 = __archive_read_seek(a, file->offset, SEEK_SET); if (r64 != (int64_t)file->offset) { /* We can't seek backwards to extract it, so issue * a warning. Note that this can only happen if * this entry was added to the heap after we passed * this offset, that is, only if the directory * mentioning this entry is later than the body of * the entry. Such layouts are very unusual; most * ISO9660 writers lay out and record all directory * information first, then store all file bodies. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order file @%jx (%s) %jd < %jd", (intmax_t)file->number, iso9660->pathname.s, (intmax_t)file->offset, (intmax_t)iso9660->current_position); iso9660->entry_bytes_remaining = 0; return (ARCHIVE_WARN); } iso9660->current_position = (uint64_t)r64; } /* Initialize zisofs variables. */ iso9660->entry_zisofs.pz = file->pz; if (file->pz) { #ifdef HAVE_ZLIB_H struct zisofs *zisofs; zisofs = &iso9660->entry_zisofs; zisofs->initialized = 0; zisofs->pz_log2_bs = file->pz_log2_bs; zisofs->pz_uncompressed_size = file->pz_uncompressed_size; zisofs->pz_offset = 0; zisofs->header_avail = 0; zisofs->header_passed = 0; zisofs->block_pointers_avail = 0; #endif archive_entry_set_size(entry, file->pz_uncompressed_size); } iso9660->previous_number = file->number; if (iso9660->seenJoliet) { memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path, iso9660->utf16be_path_len); iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len; } else archive_strcpy( &iso9660->previous_pathname, iso9660->pathname.s); /* Reset entry_bytes_remaining if the file is multi extent. */ iso9660->entry_content = file->contents.first; if (iso9660->entry_content != NULL) iso9660->entry_bytes_remaining = iso9660->entry_content->size; if (archive_entry_filetype(entry) == AE_IFDIR) { /* Overwrite nlinks by proper link number which is * calculated from number of sub directories. */ archive_entry_set_nlink(entry, 2 + file->subdirs); /* Directory data has been read completely. */ iso9660->entry_bytes_remaining = 0; } if (rd_r != ARCHIVE_OK) return (rd_r); return (ARCHIVE_OK); } static int archive_read_format_iso9660_read_data_skip(struct archive_read *a) { /* Because read_next_header always does an explicit skip * to the next entry, we don't need to do anything here. */ (void)a; /* UNUSED */ return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int zisofs_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct iso9660 *iso9660; struct zisofs *zisofs; const unsigned char *p; size_t avail; ssize_t bytes_read; size_t uncompressed_size; int r; iso9660 = (struct iso9660 *)(a->format->data); zisofs = &iso9660->entry_zisofs; p = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated zisofs file body"); return (ARCHIVE_FATAL); } if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = (ssize_t)iso9660->entry_bytes_remaining; avail = bytes_read; uncompressed_size = 0; if (!zisofs->initialized) { 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_alloc < xsize) { size_t alloc; if (zisofs->block_pointers != NULL) free(zisofs->block_pointers); 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_alloc = alloc; } zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ xsize = (size_t)1UL << zisofs->pz_log2_bs; if (zisofs->uncompressed_buffer_size < xsize) { if (zisofs->uncompressed_buffer != NULL) free(zisofs->uncompressed_buffer); zisofs->uncompressed_buffer = malloc(xsize); if (zisofs->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for zisofs decompression"); return (ARCHIVE_FATAL); } } zisofs->uncompressed_buffer_size = xsize; /* * Read the file header, and check the magic code of zisofs. */ if (zisofs->header_avail < sizeof(zisofs->header)) { xsize = sizeof(zisofs->header) - zisofs->header_avail; if (avail < xsize) xsize = avail; memcpy(zisofs->header + zisofs->header_avail, p, xsize); zisofs->header_avail += xsize; avail -= xsize; p += xsize; } if (!zisofs->header_passed && zisofs->header_avail == sizeof(zisofs->header)) { int err = 0; if (memcmp(zisofs->header, zisofs_magic, sizeof(zisofs_magic)) != 0) err = 1; if (archive_le32dec(zisofs->header + 8) != zisofs->pz_uncompressed_size) err = 1; if (zisofs->header[12] != 4) err = 1; if (zisofs->header[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); } 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; p += 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; } } if (!zisofs->initialized) goto next_data; /* We need more data. */ } /* * 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_read - avail)) { /* TODO: Should we seek offset of current file * by bst ? */ 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) { memset(zisofs->uncompressed_buffer, 0, zisofs->uncompressed_buffer_size); uncompressed_size = zisofs->uncompressed_buffer_size; } 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 = zisofs->uncompressed_buffer; zisofs->stream.avail_out = (uInt)zisofs->uncompressed_buffer_size; 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); } uncompressed_size = zisofs->uncompressed_buffer_size - zisofs->stream.avail_out; avail -= zisofs->stream.next_in - p; zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); } next_data: bytes_read -= avail; *buff = zisofs->uncompressed_buffer; *size = uncompressed_size; *offset = iso9660->entry_sparse_offset; iso9660->entry_sparse_offset += uncompressed_size; iso9660->entry_bytes_remaining -= bytes_read; iso9660->current_position += bytes_read; zisofs->pz_offset += (uint32_t)bytes_read; iso9660->entry_bytes_unconsumed += bytes_read; return (ARCHIVE_OK); } #else /* HAVE_ZLIB_H */ static int zisofs_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { (void)buff;/* UNUSED */ (void)size;/* UNUSED */ (void)offset;/* UNUSED */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "zisofs is not supported on this platform."); return (ARCHIVE_FAILED); } #endif /* HAVE_ZLIB_H */ static int archive_read_format_iso9660_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct iso9660 *iso9660; iso9660 = (struct iso9660 *)(a->format->data); if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); iso9660->entry_bytes_unconsumed = 0; } if (iso9660->entry_bytes_remaining <= 0) { if (iso9660->entry_content != NULL) iso9660->entry_content = iso9660->entry_content->next; if (iso9660->entry_content == NULL) { *buff = NULL; *size = 0; *offset = iso9660->entry_sparse_offset; return (ARCHIVE_EOF); } /* Seek forward to the start of the entry. */ if (iso9660->current_position < iso9660->entry_content->offset) { int64_t step; step = iso9660->entry_content->offset - iso9660->current_position; step = __archive_read_consume(a, step); if (step < 0) return ((int)step); iso9660->current_position = iso9660->entry_content->offset; } if (iso9660->entry_content->offset < iso9660->current_position) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order file (%s) %jd < %jd", iso9660->pathname.s, (intmax_t)iso9660->entry_content->offset, (intmax_t)iso9660->current_position); *buff = NULL; *size = 0; *offset = iso9660->entry_sparse_offset; return (ARCHIVE_WARN); } iso9660->entry_bytes_remaining = iso9660->entry_content->size; } if (iso9660->entry_zisofs.pz) return (zisofs_read_data(a, buff, size, offset)); *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); if (*buff == NULL) return (ARCHIVE_FATAL); if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = (ssize_t)iso9660->entry_bytes_remaining; *size = bytes_read; *offset = iso9660->entry_sparse_offset; iso9660->entry_sparse_offset += bytes_read; iso9660->entry_bytes_remaining -= bytes_read; iso9660->entry_bytes_unconsumed = bytes_read; iso9660->current_position += bytes_read; return (ARCHIVE_OK); } static int archive_read_format_iso9660_cleanup(struct archive_read *a) { struct iso9660 *iso9660; int r = ARCHIVE_OK; iso9660 = (struct iso9660 *)(a->format->data); release_files(iso9660); free(iso9660->read_ce_req.reqs); archive_string_free(&iso9660->pathname); archive_string_free(&iso9660->previous_pathname); free(iso9660->pending_files.files); #ifdef HAVE_ZLIB_H free(iso9660->entry_zisofs.uncompressed_buffer); free(iso9660->entry_zisofs.block_pointers); if (iso9660->entry_zisofs.stream_valid) { if (inflateEnd(&iso9660->entry_zisofs.stream) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } } #endif free(iso9660->utf16be_path); free(iso9660->utf16be_previous_path); free(iso9660); (a->format->data) = NULL; return (r); } /* * This routine parses a single ISO directory record, makes sense * of any extensions, and stores the result in memory. */ static struct file_info * parse_file_info(struct archive_read *a, struct file_info *parent, const unsigned char *isodirrec, size_t reclen) { struct iso9660 *iso9660; struct file_info *file, *filep; size_t name_len; const unsigned char *rr_start, *rr_end; const unsigned char *p; size_t dr_len = 0; uint64_t fsize, offset; int32_t location; int flags; iso9660 = (struct iso9660 *)(a->format->data); if (reclen != 0) dr_len = (size_t)isodirrec[DR_length_offset]; /* * Sanity check that reclen is not zero and dr_len is greater than * reclen but at least 34 */ if (reclen == 0 || reclen < dr_len || dr_len < 34) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid length of directory record"); return (NULL); } name_len = (size_t)isodirrec[DR_name_len_offset]; location = archive_le32dec(isodirrec + DR_extent_offset); fsize = toi(isodirrec + DR_size_offset, DR_size_size); /* Sanity check that name_len doesn't exceed dr_len. */ if (dr_len - 33 < name_len || name_len == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid length of file identifier"); return (NULL); } /* Sanity check that location doesn't exceed volume block. * Don't check lower limit of location; it's possibility * the location has negative value when file type is symbolic * link or file size is zero. As far as I know latest mkisofs * do that. */ if (location > 0 && (location + ((fsize + iso9660->logical_block_size -1) / iso9660->logical_block_size)) > (uint32_t)iso9660->volume_block) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid location of extent of file"); return (NULL); } /* Sanity check that location doesn't have a negative value * when the file is not empty. it's too large. */ if (fsize != 0 && location < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid location of extent of file"); return (NULL); } /* Sanity check that this entry does not create a cycle. */ offset = iso9660->logical_block_size * (uint64_t)location; for (filep = parent; filep != NULL; filep = filep->parent) { if (filep->offset == offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Directory structure contains loop"); return (NULL); } } /* Create a new file entry and copy data from the ISO dir record. */ file = (struct file_info *)calloc(1, sizeof(*file)); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file entry"); return (NULL); } file->parent = parent; file->offset = offset; file->size = fsize; file->mtime = isodate7(isodirrec + DR_date_offset); file->ctime = file->atime = file->mtime; file->rede_files.first = NULL; file->rede_files.last = &(file->rede_files.first); p = isodirrec + DR_name_offset; /* Rockridge extensions (if any) follow name. Compute this * before fidgeting the name_len below. */ rr_start = p + name_len + (name_len & 1 ? 0 : 1); rr_end = isodirrec + dr_len; if (iso9660->seenJoliet) { /* Joliet names are max 64 chars (128 bytes) according to spec, * but genisoimage/mkisofs allows recording longer Joliet * names which are 103 UCS2 characters(206 bytes) by their * option '-joliet-long'. */ if (name_len > 206) name_len = 206; name_len &= ~1; /* trim trailing first version and dot from filename. * * Remember we were in UTF-16BE land! * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both * 16 bits big endian characters on Joliet. * * TODO: sanitize filename? * Joliet allows any UCS-2 char except: * *, /, :, ;, ? and \. */ /* Chop off trailing ';1' from files. */ if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';' && p[name_len-2] == 0 && p[name_len-1] == '1') name_len -= 4; #if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */ /* Chop off trailing '.' from filenames. */ if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.') name_len -= 2; #endif if ((file->utf16be_name = malloc(name_len)) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file name"); goto fail; } memcpy(file->utf16be_name, p, name_len); file->utf16be_bytes = name_len; } else { /* Chop off trailing ';1' from files. */ if (name_len > 2 && p[name_len - 2] == ';' && p[name_len - 1] == '1') name_len -= 2; /* Chop off trailing '.' from filenames. */ if (name_len > 1 && p[name_len - 1] == '.') --name_len; archive_strncpy(&file->name, (const char *)p, name_len); } flags = isodirrec[DR_flags_offset]; if (flags & 0x02) file->mode = AE_IFDIR | 0700; else file->mode = AE_IFREG | 0400; if (flags & 0x80) file->multi_extent = 1; else file->multi_extent = 0; /* * Use a location for the file number, which is treated as an inode * number to find out hardlink target. If Rockridge extensions is * being used, the file number will be overwritten by FILE SERIAL * NUMBER of RRIP "PX" extension. * Note: Old mkisofs did not record that FILE SERIAL NUMBER * in ISO images. - * Note2: xorriso set 0 to the location of a symlink file. + * Note2: xorriso set 0 to the location of a symlink file. */ if (file->size == 0 && location >= 0) { /* If file->size is zero, its location points wrong place, * and so we should not use it for the file number. * When the location has negative value, it can be used * for the file number. */ file->number = -1; /* Do not appear before any directory entries. */ file->offset = -1; } else file->number = (int64_t)(uint32_t)location; /* Rockridge extensions overwrite information from above. */ if (iso9660->opt_support_rockridge) { if (parent == NULL && rr_end - rr_start >= 7) { p = rr_start; if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) { /* * SP extension stores the suspOffset * (Number of bytes to skip between * filename and SUSP records.) * It is mandatory by the SUSP standard * (IEEE 1281). * * It allows SUSP to coexist with * non-SUSP uses of the System * Use Area by placing non-SUSP data * before SUSP data. * * SP extension must be in the root * directory entry, disable all SUSP * processing if not found. */ iso9660->suspOffset = p[6]; iso9660->seenSUSP = 1; rr_start += 7; } } if (iso9660->seenSUSP) { int r; file->name_continues = 0; file->symlink_continues = 0; rr_start += iso9660->suspOffset; r = parse_rockridge(a, file, rr_start, rr_end); if (r != ARCHIVE_OK) goto fail; /* * A file size of symbolic link files in ISO images * made by makefs is not zero and its location is * the same as those of next regular file. That is * the same as hard like file and it causes unexpected - * error. + * error. */ if (file->size > 0 && (file->mode & AE_IFMT) == AE_IFLNK) { file->size = 0; file->number = -1; file->offset = -1; } } else /* If there isn't SUSP, disable parsing * rock ridge extensions. */ iso9660->opt_support_rockridge = 0; } file->nlinks = 1;/* Reset nlink. we'll calculate it later. */ /* Tell file's parent how many children that parent has. */ if (parent != NULL && (flags & 0x02)) parent->subdirs++; if (iso9660->seenRockridge) { if (parent != NULL && parent->parent == NULL && (flags & 0x02) && iso9660->rr_moved == NULL && file->name.s && (strcmp(file->name.s, "rr_moved") == 0 || strcmp(file->name.s, ".rr_moved") == 0)) { iso9660->rr_moved = file; file->rr_moved = 1; file->rr_moved_has_re_only = 1; file->re = 0; parent->subdirs--; } else if (file->re) { /* * Sanity check: file's parent is rr_moved. */ if (parent == NULL || parent->rr_moved == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE"); goto fail; } /* * Sanity check: file does not have "CL" extension. */ if (file->cl_offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE and CL"); goto fail; } /* * Sanity check: The file type must be a directory. */ if ((flags & 0x02) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE"); goto fail; } } else if (parent != NULL && parent->rr_moved) file->rr_moved_has_re_only = 0; else if (parent != NULL && (flags & 0x02) && (parent->re || parent->re_descendant)) file->re_descendant = 1; if (file->cl_offset) { struct file_info *r; if (parent == NULL || parent->parent == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } /* * Sanity check: The file type must be a regular file. */ if ((flags & 0x02) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } parent->subdirs++; /* Overwrite an offset and a number of this "CL" entry * to appear before other dirs. "+1" to those is to * make sure to appear after "RE" entry which this * "CL" entry should be connected with. */ file->offset = file->number = file->cl_offset + 1; /* * Sanity check: cl_offset does not point at its * the parents or itself. */ for (r = parent; r; r = r->parent) { if (r->offset == file->cl_offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } } if (file->cl_offset == file->offset || parent->rr_moved) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } } } #if DEBUG /* DEBUGGING: Warn about attributes I don't yet fully support. */ if ((flags & ~0x02) != 0) { fprintf(stderr, "\n ** Unrecognized flag: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) { fprintf(stderr, "\n ** Unrecognized sequence number: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_file_unit_size_offset) != 0) { fprintf(stderr, "\n ** Unexpected file unit size: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_interleave_offset) != 0) { fprintf(stderr, "\n ** Unexpected interleave: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_ext_attr_length_offset) != 0) { fprintf(stderr, "\n ** Unexpected extended attribute length: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } #endif register_file(iso9660, file); return (file); fail: archive_string_free(&file->name); free(file); return (NULL); } static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *p, const unsigned char *end) { struct iso9660 *iso9660; int entry_seen = 0; iso9660 = (struct iso9660 *)(a->format->data); while (p + 4 <= end /* Enough space for another entry. */ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */ && p[2] >= 4 /* Sanity-check length. */ && p + p[2] <= end) { /* Sanity-check length. */ const unsigned char *data = p + 4; int data_length = p[2] - 4; int version = p[3]; switch(p[0]) { case 'C': if (p[1] == 'E') { if (version == 1 && data_length == 24) { /* * CE extension comprises: * 8 byte sector containing extension * 8 byte offset w/in above sector * 8 byte length of continuation */ int32_t location = archive_le32dec(data); file->ce_offset = archive_le32dec(data+8); file->ce_size = archive_le32dec(data+16); if (register_CE(a, location, file) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } else if (p[1] == 'L') { if (version == 1 && data_length == 8) { file->cl_offset = (uint64_t) iso9660->logical_block_size * (uint64_t)archive_le32dec(data); iso9660->seenRockridge = 1; } } break; case 'N': if (p[1] == 'M') { if (version == 1) { parse_rockridge_NM1(file, data, data_length); iso9660->seenRockridge = 1; } } break; case 'P': /* * PD extension is padding; * contents are always ignored. * * PL extension won't appear; * contents are always ignored. */ if (p[1] == 'N') { if (version == 1 && data_length == 16) { file->rdev = toi(data,4); file->rdev <<= 32; file->rdev |= toi(data + 8, 4); iso9660->seenRockridge = 1; } } else if (p[1] == 'X') { /* * PX extension comprises: * 8 bytes for mode, * 8 bytes for nlinks, * 8 bytes for uid, * 8 bytes for gid, * 8 bytes for inode. */ if (version == 1) { if (data_length >= 8) file->mode = toi(data, 4); if (data_length >= 16) file->nlinks = toi(data + 8, 4); if (data_length >= 24) file->uid = toi(data + 16, 4); if (data_length >= 32) file->gid = toi(data + 24, 4); if (data_length >= 40) file->number = toi(data + 32, 4); iso9660->seenRockridge = 1; } } break; case 'R': if (p[1] == 'E' && version == 1) { file->re = 1; iso9660->seenRockridge = 1; } else if (p[1] == 'R' && version == 1) { /* * RR extension comprises: * one byte flag value * This extension is obsolete, * so contents are always ignored. */ } break; case 'S': if (p[1] == 'L') { if (version == 1) { parse_rockridge_SL1(file, data, data_length); iso9660->seenRockridge = 1; } } else if (p[1] == 'T' && data_length == 0 && version == 1) { /* * ST extension marks end of this * block of SUSP entries. * * It allows SUSP to coexist with * non-SUSP uses of the System * Use Area by placing non-SUSP data * after SUSP data. */ iso9660->seenSUSP = 0; iso9660->seenRockridge = 0; return (ARCHIVE_OK); } break; case 'T': if (p[1] == 'F') { if (version == 1) { parse_rockridge_TF1(file, data, data_length); iso9660->seenRockridge = 1; } } break; case 'Z': if (p[1] == 'F') { if (version == 1) parse_rockridge_ZF1(file, data, data_length); } break; default: break; } p += p[2]; entry_seen = 1; } if (entry_seen) return (ARCHIVE_OK); else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Tried to parse Rockridge extensions, but none found"); return (ARCHIVE_WARN); } } static int register_CE(struct archive_read *a, int32_t location, struct file_info *file) { struct iso9660 *iso9660; struct read_ce_queue *heap; struct read_ce_req *p; uint64_t offset, parent_offset; int hole, parent; iso9660 = (struct iso9660 *)(a->format->data); offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size; if (((file->mode & AE_IFMT) == AE_IFREG && offset >= file->offset) || offset < iso9660->current_position || (((uint64_t)file->ce_offset) + file->ce_size) > (uint64_t)iso9660->logical_block_size || offset + file->ce_offset + file->ce_size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid parameter in SUSP \"CE\" extension"); return (ARCHIVE_FATAL); } /* Expand our CE list as necessary. */ heap = &(iso9660->read_ce_req); if (heap->cnt >= heap->allocated) { int new_size; if (heap->allocated < 16) new_size = 16; else new_size = heap->allocated * 2; /* Overflow might keep us from growing the list. */ if (new_size <= heap->allocated) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } p = calloc(new_size, sizeof(p[0])); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (heap->reqs != NULL) { memcpy(p, heap->reqs, heap->cnt * sizeof(*p)); free(heap->reqs); } heap->reqs = p; heap->allocated = new_size; } /* * Start with hole at end, walk it up tree to find insertion point. */ hole = heap->cnt++; while (hole > 0) { parent = (hole - 1)/2; parent_offset = heap->reqs[parent].offset; if (offset >= parent_offset) { heap->reqs[hole].offset = offset; heap->reqs[hole].file = file; return (ARCHIVE_OK); } /* Move parent into hole <==> move hole up tree. */ heap->reqs[hole] = heap->reqs[parent]; hole = parent; } heap->reqs[0].offset = offset; heap->reqs[0].file = file; return (ARCHIVE_OK); } static void next_CE(struct read_ce_queue *heap) { uint64_t a_offset, b_offset, c_offset; int a, b, c; struct read_ce_req tmp; if (heap->cnt < 1) return; /* * Move the last item in the heap to the root of the tree */ heap->reqs[0] = heap->reqs[--(heap->cnt)]; /* * Rebalance the heap. */ a = 0; /* Starting element and its offset */ a_offset = heap->reqs[a].offset; for (;;) { b = a + a + 1; /* First child */ if (b >= heap->cnt) return; b_offset = heap->reqs[b].offset; c = b + 1; /* Use second child if it is smaller. */ if (c < heap->cnt) { c_offset = heap->reqs[c].offset; if (c_offset < b_offset) { b = c; b_offset = c_offset; } } if (a_offset <= b_offset) return; tmp = heap->reqs[a]; heap->reqs[a] = heap->reqs[b]; heap->reqs[b] = tmp; a = b; } } static int read_CE(struct archive_read *a, struct iso9660 *iso9660) { struct read_ce_queue *heap; const unsigned char *b, *p, *end; struct file_info *file; size_t step; int r; /* Read data which RRIP "CE" extension points. */ heap = &(iso9660->read_ce_req); step = iso9660->logical_block_size; while (heap->cnt && heap->reqs[0].offset == iso9660->current_position) { b = __archive_read_ahead(a, step, NULL); if (b == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } do { file = heap->reqs[0].file; if (file->ce_offset + file->ce_size > step) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed CE information"); return (ARCHIVE_FATAL); } p = b + file->ce_offset; end = p + file->ce_size; next_CE(heap); r = parse_rockridge(a, file, p, end); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } while (heap->cnt && heap->reqs[0].offset == iso9660->current_position); /* NOTE: Do not move this consume's code to front of * do-while loop. Registration of nested CE extension * might cause error because of current position. */ __archive_read_consume(a, step); iso9660->current_position += step; } return (ARCHIVE_OK); } static void parse_rockridge_NM1(struct file_info *file, const unsigned char *data, int data_length) { if (!file->name_continues) archive_string_empty(&file->name); file->name_continues = 0; if (data_length < 1) return; /* * NM version 1 extension comprises: * 1 byte flag, value is one of: * = 0: remainder is name * = 1: remainder is name, next NM entry continues name * = 2: "." * = 4: ".." * = 32: Implementation specific * All other values are reserved. */ switch(data[0]) { case 0: if (data_length < 2) return; archive_strncat(&file->name, (const char *)data + 1, data_length - 1); break; case 1: if (data_length < 2) return; archive_strncat(&file->name, (const char *)data + 1, data_length - 1); file->name_continues = 1; break; case 2: archive_strcat(&file->name, "."); break; case 4: archive_strcat(&file->name, ".."); break; default: return; } } static void parse_rockridge_TF1(struct file_info *file, const unsigned char *data, int data_length) { char flag; /* * TF extension comprises: * one byte flag * create time (optional) * modify time (optional) * access time (optional) * attribute time (optional) * Time format and presence of fields * is controlled by flag bits. */ if (data_length < 1) return; flag = data[0]; ++data; --data_length; if (flag & 0x80) { /* Use 17-byte time format. */ if ((flag & 1) && data_length >= 17) { /* Create time. */ file->birthtime_is_set = 1; file->birthtime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 2) && data_length >= 17) { /* Modify time. */ file->mtime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 4) && data_length >= 17) { /* Access time. */ file->atime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 8) && data_length >= 17) { /* Attribute change time. */ file->ctime = isodate17(data); } } else { /* Use 7-byte time format. */ if ((flag & 1) && data_length >= 7) { /* Create time. */ file->birthtime_is_set = 1; file->birthtime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 2) && data_length >= 7) { /* Modify time. */ file->mtime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 4) && data_length >= 7) { /* Access time. */ file->atime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 8) && data_length >= 7) { /* Attribute change time. */ file->ctime = isodate7(data); } } } static void parse_rockridge_SL1(struct file_info *file, const unsigned char *data, int data_length) { const char *separator = ""; if (!file->symlink_continues || file->symlink.length < 1) archive_string_empty(&file->symlink); file->symlink_continues = 0; /* * Defined flag values: * 0: This is the last SL record for this symbolic link * 1: this symbolic link field continues in next SL entry * All other values are reserved. */ if (data_length < 1) return; switch(*data) { case 0: break; case 1: file->symlink_continues = 1; break; default: return; } ++data; /* Skip flag byte. */ --data_length; /* * SL extension body stores "components". * Basically, this is a complicated way of storing * a POSIX path. It also interferes with using * symlinks for storing non-path data. * * Each component is 2 bytes (flag and length) * possibly followed by name data. */ while (data_length >= 2) { unsigned char flag = *data++; unsigned char nlen = *data++; data_length -= 2; archive_strcat(&file->symlink, separator); separator = "/"; switch(flag) { case 0: /* Usual case, this is text. */ if (data_length < nlen) return; archive_strncat(&file->symlink, (const char *)data, nlen); break; case 0x01: /* Text continues in next component. */ if (data_length < nlen) return; archive_strncat(&file->symlink, (const char *)data, nlen); separator = ""; break; case 0x02: /* Current dir. */ archive_strcat(&file->symlink, "."); break; case 0x04: /* Parent dir. */ archive_strcat(&file->symlink, ".."); break; case 0x08: /* Root of filesystem. */ archive_strcat(&file->symlink, "/"); separator = ""; break; case 0x10: /* Undefined (historically "volume root" */ archive_string_empty(&file->symlink); archive_strcat(&file->symlink, "ROOT"); break; case 0x20: /* Undefined (historically "hostname") */ archive_strcat(&file->symlink, "hostname"); break; default: /* TODO: issue a warning ? */ return; } data += nlen; data_length -= nlen; } } static void parse_rockridge_ZF1(struct file_info *file, const unsigned char *data, int data_length) { if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) { /* paged zlib */ file->pz = 1; file->pz_log2_bs = data[3]; file->pz_uncompressed_size = archive_le32dec(&data[4]); } } static void register_file(struct iso9660 *iso9660, struct file_info *file) { file->use_next = iso9660->use_files; iso9660->use_files = file; } static void release_files(struct iso9660 *iso9660) { struct content *con, *connext; struct file_info *file; file = iso9660->use_files; while (file != NULL) { struct file_info *next = file->use_next; archive_string_free(&file->name); archive_string_free(&file->symlink); free(file->utf16be_name); con = file->contents.first; while (con != NULL) { connext = con->next; free(con); con = connext; } free(file); file = next; } } static int next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, struct file_info **pfile) { struct file_info *file; int r; r = next_cache_entry(a, iso9660, pfile); if (r != ARCHIVE_OK) return (r); file = *pfile; /* Don't waste time seeking for zero-length bodies. */ if (file->size == 0) file->offset = iso9660->current_position; /* flush any remaining bytes from the last round to ensure * we're positioned */ if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); iso9660->entry_bytes_unconsumed = 0; } /* Seek forward to the start of the entry. */ if (iso9660->current_position < file->offset) { int64_t step; step = file->offset - iso9660->current_position; step = __archive_read_consume(a, step); if (step < 0) return ((int)step); iso9660->current_position = file->offset; } /* We found body of file; handle it now. */ return (ARCHIVE_OK); } static int next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, struct file_info **pfile) { struct file_info *file; struct { struct file_info *first; struct file_info **last; } empty_files; int64_t number; int count; file = cache_get_entry(iso9660); if (file != NULL) { *pfile = file; return (ARCHIVE_OK); } for (;;) { struct file_info *re, *d; *pfile = file = next_entry(iso9660); if (file == NULL) { /* * If directory entries all which are descendant of * rr_moved are still remaining, expose their. */ - if (iso9660->re_files.first != NULL && + if (iso9660->re_files.first != NULL && iso9660->rr_moved != NULL && iso9660->rr_moved->rr_moved_has_re_only) /* Expose "rr_moved" entry. */ cache_add_entry(iso9660, iso9660->rr_moved); while ((re = re_get_entry(iso9660)) != NULL) { /* Expose its descendant dirs. */ while ((d = rede_get_entry(re)) != NULL) cache_add_entry(iso9660, d); } if (iso9660->cache_files.first != NULL) return (next_cache_entry(a, iso9660, pfile)); return (ARCHIVE_EOF); } if (file->cl_offset) { struct file_info *first_re = NULL; int nexted_re = 0; /* * Find "RE" dir for the current file, which * has "CL" flag. */ while ((re = re_get_entry(iso9660)) != first_re) { if (first_re == NULL) first_re = re; if (re->offset == file->cl_offset) { re->parent->subdirs--; re->parent = file->parent; re->re = 0; if (re->parent->re_descendant) { nexted_re = 1; re->re_descendant = 1; if (rede_add_entry(re) < 0) goto fatal_rr; /* Move a list of descendants * to a new ancestor. */ while ((d = rede_get_entry( re)) != NULL) if (rede_add_entry(d) < 0) goto fatal_rr; break; } /* Replace the current file * with "RE" dir */ *pfile = file = re; /* Expose its descendant */ while ((d = rede_get_entry( file)) != NULL) cache_add_entry( iso9660, d); break; } else re_add_entry(iso9660, re); } if (nexted_re) { /* * Do not expose this at this time * because we have not gotten its full-path * name yet. */ continue; } } else if ((file->mode & AE_IFMT) == AE_IFDIR) { int r; /* Read file entries in this dir. */ r = read_children(a, file); if (r != ARCHIVE_OK) return (r); /* * Handle a special dir of Rockridge extensions, * "rr_moved". */ if (file->rr_moved) { /* * If this has only the subdirectories which * have "RE" flags, do not expose at this time. */ if (file->rr_moved_has_re_only) continue; /* Otherwise expose "rr_moved" entry. */ } else if (file->re) { /* * Do not expose this at this time * because we have not gotten its full-path * name yet. */ re_add_entry(iso9660, file); continue; } else if (file->re_descendant) { /* * If the top level "RE" entry of this entry * is not exposed, we, accordingly, should not * expose this entry at this time because * we cannot make its proper full-path name. */ if (rede_add_entry(file) == 0) continue; /* Otherwise we can expose this entry because * it seems its top level "RE" has already been * exposed. */ } } break; } if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1) return (ARCHIVE_OK); count = 0; number = file->number; iso9660->cache_files.first = NULL; iso9660->cache_files.last = &(iso9660->cache_files.first); empty_files.first = NULL; empty_files.last = &empty_files.first; /* Collect files which has the same file serial number. * Peek pending_files so that file which number is different * is not put back. */ while (iso9660->pending_files.used > 0 && (iso9660->pending_files.files[0]->number == -1 || iso9660->pending_files.files[0]->number == number)) { if (file->number == -1) { /* This file has the same offset * but it's wrong offset which empty files * and symlink files have. * NOTE: This wrong offset was recorded by * old mkisofs utility. If ISO images is * created by latest mkisofs, this does not * happen. */ file->next = NULL; *empty_files.last = file; empty_files.last = &(file->next); } else { count++; cache_add_entry(iso9660, file); } file = next_entry(iso9660); } if (count == 0) { *pfile = file; return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK); } if (file->number == -1) { file->next = NULL; *empty_files.last = file; empty_files.last = &(file->next); } else { count++; cache_add_entry(iso9660, file); } if (count > 1) { /* The count is the same as number of hardlink, * so much so that each nlinks of files in cache_file * is overwritten by value of the count. */ for (file = iso9660->cache_files.first; file != NULL; file = file->next) file->nlinks = count; } /* If there are empty files, that files are added * to the tail of the cache_files. */ if (empty_files.first != NULL) { *iso9660->cache_files.last = empty_files.first; iso9660->cache_files.last = empty_files.last; } *pfile = cache_get_entry(iso9660); return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK); fatal_rr: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of " "Rockridge extensions: current position = %jd, CL offset = %jd", (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset); return (ARCHIVE_FATAL); } static inline void re_add_entry(struct iso9660 *iso9660, struct file_info *file) { file->re_next = NULL; *iso9660->re_files.last = file; iso9660->re_files.last = &(file->re_next); } static inline struct file_info * re_get_entry(struct iso9660 *iso9660) { struct file_info *file; if ((file = iso9660->re_files.first) != NULL) { iso9660->re_files.first = file->re_next; if (iso9660->re_files.first == NULL) iso9660->re_files.last = &(iso9660->re_files.first); } return (file); } static inline int rede_add_entry(struct file_info *file) { struct file_info *re; /* * Find "RE" entry. */ re = file->parent; while (re != NULL && !re->re) re = re->parent; if (re == NULL) return (-1); file->re_next = NULL; *re->rede_files.last = file; re->rede_files.last = &(file->re_next); return (0); } static inline struct file_info * rede_get_entry(struct file_info *re) { struct file_info *file; if ((file = re->rede_files.first) != NULL) { re->rede_files.first = file->re_next; if (re->rede_files.first == NULL) re->rede_files.last = &(re->rede_files.first); } return (file); } static inline void cache_add_entry(struct iso9660 *iso9660, struct file_info *file) { file->next = NULL; *iso9660->cache_files.last = file; iso9660->cache_files.last = &(file->next); } static inline struct file_info * cache_get_entry(struct iso9660 *iso9660) { struct file_info *file; if ((file = iso9660->cache_files.first) != NULL) { iso9660->cache_files.first = file->next; if (iso9660->cache_files.first == NULL) iso9660->cache_files.last = &(iso9660->cache_files.first); } return (file); } static int heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct file_info *file, uint64_t key) { uint64_t file_key, parent_key; int hole, parent; /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { struct file_info **new_pending_files; int new_size = heap->allocated * 2; if (heap->allocated < 1024) new_size = 1024; /* Overflow might keep us from growing the list. */ if (new_size <= heap->allocated) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } new_pending_files = (struct file_info **) malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (heap->allocated) memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } file_key = file->key = key; /* * Start with hole at end, walk it up tree to find insertion point. */ hole = heap->used++; while (hole > 0) { parent = (hole - 1)/2; parent_key = heap->files[parent]->key; if (file_key >= parent_key) { heap->files[hole] = file; return (ARCHIVE_OK); } /* Move parent into hole <==> move hole up tree. */ heap->files[hole] = heap->files[parent]; hole = parent; } heap->files[0] = file; return (ARCHIVE_OK); } static struct file_info * heap_get_entry(struct heap_queue *heap) { uint64_t a_key, b_key, c_key; int a, b, c; struct file_info *r, *tmp; if (heap->used < 1) return (NULL); /* * The first file in the list is the earliest; we'll return this. */ r = heap->files[0]; /* * Move the last item in the heap to the root of the tree */ heap->files[0] = heap->files[--(heap->used)]; /* * Rebalance the heap. */ a = 0; /* Starting element and its heap key */ a_key = heap->files[a]->key; for (;;) { b = a + a + 1; /* First child */ if (b >= heap->used) return (r); b_key = heap->files[b]->key; c = b + 1; /* Use second child if it is smaller. */ if (c < heap->used) { c_key = heap->files[c]->key; if (c_key < b_key) { b = c; b_key = c_key; } } if (a_key <= b_key) return (r); tmp = heap->files[a]; heap->files[a] = heap->files[b]; heap->files[b] = tmp; a = b; } } static unsigned int toi(const void *p, int n) { const unsigned char *v = (const unsigned char *)p; if (n > 1) return v[0] + 256 * toi(v + 1, n - 1); if (n == 1) return v[0]; return (0); } static time_t isodate7(const unsigned char *v) { struct tm tm; int offset; time_t t; memset(&tm, 0, sizeof(tm)); tm.tm_year = v[0]; tm.tm_mon = v[1] - 1; tm.tm_mday = v[2]; tm.tm_hour = v[3]; tm.tm_min = v[4]; tm.tm_sec = v[5]; /* v[6] is the signed timezone offset, in 1/4-hour increments. */ offset = ((const signed char *)v)[6]; if (offset > -48 && offset < 52) { tm.tm_hour -= offset / 4; tm.tm_min -= (offset % 4) * 15; } t = time_from_tm(&tm); if (t == (time_t)-1) return ((time_t)0); return (t); } static time_t isodate17(const unsigned char *v) { struct tm tm; int offset; time_t t; memset(&tm, 0, sizeof(tm)); tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] - '0') - 1900; tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0'); tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0'); tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0'); tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0'); tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0'); /* v[16] is the signed timezone offset, in 1/4-hour increments. */ offset = ((const signed char *)v)[16]; if (offset > -48 && offset < 52) { tm.tm_hour -= offset / 4; tm.tm_min -= (offset % 4) * 15; } t = time_from_tm(&tm); if (t == (time_t)-1) return ((time_t)0); return (t); } static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif 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 const char * build_pathname(struct archive_string *as, struct file_info *file, int depth) { // Plain ISO9660 only allows 8 dir levels; if we get // to 1000, then something is very, very wrong. if (depth > 1000) { return NULL; } if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) { if (build_pathname(as, file->parent, depth + 1) == NULL) { return NULL; } archive_strcat(as, "/"); } if (archive_strlen(&file->name) == 0) archive_strcat(as, "."); else archive_string_concat(as, &file->name); return (as->s); } static int build_pathname_utf16be(unsigned char *p, size_t max, size_t *len, struct file_info *file) { if (file->parent != NULL && file->parent->utf16be_bytes > 0) { if (build_pathname_utf16be(p, max, len, file->parent) != 0) return (-1); p[*len] = 0; p[*len + 1] = '/'; *len += 2; } if (file->utf16be_bytes == 0) { if (*len + 2 > max) return (-1);/* Path is too long! */ p[*len] = 0; p[*len + 1] = '.'; *len += 2; } else { if (*len + file->utf16be_bytes > max) return (-1);/* Path is too long! */ memcpy(p + *len, file->utf16be_name, file->utf16be_bytes); *len += file->utf16be_bytes; } return (0); } #if DEBUG static void dump_isodirrec(FILE *out, const unsigned char *isodirrec) { fprintf(out, " l %d,", toi(isodirrec + DR_length_offset, DR_length_size)); fprintf(out, " a %d,", toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size)); fprintf(out, " ext 0x%x,", toi(isodirrec + DR_extent_offset, DR_extent_size)); fprintf(out, " s %d,", toi(isodirrec + DR_size_offset, DR_extent_size)); fprintf(out, " f 0x%x,", toi(isodirrec + DR_flags_offset, DR_flags_size)); fprintf(out, " u %d,", toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size)); fprintf(out, " ilv %d,", toi(isodirrec + DR_interleave_offset, DR_interleave_size)); fprintf(out, " seq %d,", toi(isodirrec + DR_volume_sequence_number_offset, DR_volume_sequence_number_size)); fprintf(out, " nl %d:", toi(isodirrec + DR_name_len_offset, DR_name_len_size)); fprintf(out, " `%.*s'", toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset); } #endif diff --git a/contrib/libarchive/libarchive/archive_read_support_format_lha.c b/contrib/libarchive/libarchive/archive_read_support_format_lha.c index bcfd42e1d920..fa907a346408 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_lha.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_lha.c @@ -1,2916 +1,2916 @@ /*- * Copyright (c) 2008-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_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #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_read_private.h" #include "archive_endian.h" #define MAXMATCH 256 /* Maximum match length. */ #define MINMATCH 3 /* Minimum match length. */ /* * Literal table format: * +0 +256 +510 * +---------------+-------------------------+ * | literal code | match length | * | 0 ... 255 | MINMATCH ... MAXMATCH | * +---------------+-------------------------+ * <--- LT_BITLEN_SIZE ---> */ /* Literal table size. */ #define LT_BITLEN_SIZE (UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1) /* Position table size. * Note: this used for both position table and pre literal table.*/ #define PT_BITLEN_SIZE (3 + 16) struct lzh_dec { /* Decoding status. */ int state; /* * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded * data. */ 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; /* * Bit stream reader. */ struct lzh_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; } br; /* * Huffman coding. */ struct huffman { int len_size; int len_avail; int len_bits; 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. */ #define HTBL_BITS 10 int max_bits; int shift_bits; int tbl_bits; int tree_used; int tree_avail; /* Direct access table. */ uint16_t *tbl; /* Binary tree table for extra bits over the direct access. */ struct htree_t { uint16_t left; uint16_t right; } *tree; } lt, pt; int blocks_avail; int pos_pt_len_size; int pos_pt_len_bits; int literal_pt_len_size; int literal_pt_len_bits; int reading_position; int loop; int error; }; struct lzh_stream { const unsigned char *next_in; int avail_in; int64_t total_in; const unsigned char *ref_ptr; int avail_out; int64_t total_out; struct lzh_dec *ds; }; struct lha { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; uint16_t entry_crc_calculated; size_t header_size; /* header size */ unsigned char level; /* header level */ char method[3]; /* compress type */ int64_t compsize; /* compressed data size */ int64_t origsize; /* original file size */ int setflag; #define BIRTHTIME_IS_SET 1 #define ATIME_IS_SET 2 #define UNIX_MODE_IS_SET 4 #define CRC_IS_SET 8 time_t birthtime; long birthtime_tv_nsec; time_t mtime; long mtime_tv_nsec; time_t atime; long atime_tv_nsec; mode_t mode; int64_t uid; int64_t gid; struct archive_string uname; struct archive_string gname; uint16_t header_crc; uint16_t crc; /* dirname and filename could be in different codepages */ struct archive_string_conv *sconv_dir; struct archive_string_conv *sconv_fname; struct archive_string_conv *opt_sconv; struct archive_string dirname; struct archive_string filename; struct archive_wstring ws; unsigned char dos_attr; /* Flag to mark progress that an archive was read their first header.*/ char found_first_header; /* Flag to mark that indicates an empty directory. */ char directory; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; char end_of_entry_cleanup; char entry_is_compressed; char format_name[64]; struct lzh_stream strm; }; /* * LHA header common member offset. */ #define H_METHOD_OFFSET 2 /* Compress type. */ #define H_ATTR_OFFSET 19 /* DOS attribute. */ #define H_LEVEL_OFFSET 20 /* Header Level. */ #define H_SIZE 22 /* Minimum header size. */ static int archive_read_format_lha_bid(struct archive_read *, int); static int archive_read_format_lha_options(struct archive_read *, const char *, const char *); static int archive_read_format_lha_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_lha_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_lha_read_data_skip(struct archive_read *); static int archive_read_format_lha_cleanup(struct archive_read *); static void lha_replace_path_separator(struct lha *, struct archive_entry *); static int lha_read_file_header_0(struct archive_read *, struct lha *); static int lha_read_file_header_1(struct archive_read *, struct lha *); static int lha_read_file_header_2(struct archive_read *, struct lha *); static int lha_read_file_header_3(struct archive_read *, struct lha *); static int lha_read_file_extended_header(struct archive_read *, struct lha *, uint16_t *, int, size_t, size_t *); static size_t lha_check_header_format(const void *); static int lha_skip_sfx(struct archive_read *); static time_t lha_dos_time(const unsigned char *); static time_t lha_win_time(uint64_t, long *); static unsigned char lha_calcsum(unsigned char, const void *, int, size_t); static int lha_parse_linkname(struct archive_wstring *, struct archive_wstring *); static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, size_t *, int64_t *); static void lha_crc16_init(void); static uint16_t lha_crc16(uint16_t, const void *, size_t); static int lzh_decode_init(struct lzh_stream *, const char *); static void lzh_decode_free(struct lzh_stream *); static int lzh_decode(struct lzh_stream *, int); static int lzh_br_fillup(struct lzh_stream *, struct lzh_br *); static int lzh_huffman_init(struct huffman *, size_t, int); static void lzh_huffman_free(struct huffman *); static int lzh_read_pt_bitlen(struct lzh_stream *, int start, int end); static int lzh_make_fake_table(struct huffman *, uint16_t); static int lzh_make_huffman_table(struct huffman *); static inline int lzh_decode_huffman(struct huffman *, unsigned); static int lzh_decode_huffman_tree(struct huffman *, unsigned, int); int archive_read_support_format_lha(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct lha *lha; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_lha"); lha = (struct lha *)calloc(1, sizeof(*lha)); if (lha == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate lha data"); return (ARCHIVE_FATAL); } archive_string_init(&lha->ws); r = __archive_read_register_format(a, lha, "lha", archive_read_format_lha_bid, archive_read_format_lha_options, archive_read_format_lha_read_header, archive_read_format_lha_read_data, archive_read_format_lha_read_data_skip, NULL, archive_read_format_lha_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(lha); return (ARCHIVE_OK); } static size_t lha_check_header_format(const void *h) { const unsigned char *p = h; size_t next_skip_bytes; switch (p[H_METHOD_OFFSET+3]) { /* * "-lh0-" ... "-lh7-" "-lhd-" * "-lzs-" "-lz5-" */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case 'd': case 's': next_skip_bytes = 4; /* b0 == 0 means the end of an LHa archive file. */ if (p[0] == 0) break; if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l' || p[H_METHOD_OFFSET+4] != '-') break; if (p[H_METHOD_OFFSET+2] == 'h') { /* "-lh?-" */ if (p[H_METHOD_OFFSET+3] == 's') break; if (p[H_LEVEL_OFFSET] == 0) return (0); if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20) return (0); } if (p[H_METHOD_OFFSET+2] == 'z') { /* LArc extensions: -lzs-,-lz4- and -lz5- */ if (p[H_LEVEL_OFFSET] != 0) break; if (p[H_METHOD_OFFSET+3] == 's' || p[H_METHOD_OFFSET+3] == '4' || p[H_METHOD_OFFSET+3] == '5') return (0); } break; case 'h': next_skip_bytes = 1; break; case 'z': next_skip_bytes = 1; break; case 'l': next_skip_bytes = 2; break; case '-': next_skip_bytes = 3; break; default : next_skip_bytes = 4; break; } return (next_skip_bytes); } static int archive_read_format_lha_bid(struct archive_read *a, int best_bid) { const char *p; const void *buff; ssize_t bytes_avail, offset, window; size_t next; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 30) return (-1); if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) return (-1); if (lha_check_header_format(p) == 0) return (30); if (p[0] == 'M' && p[1] == 'Z') { /* PE file */ offset = 0; window = 4096; while (offset < (1024 * 20)) { buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < (H_SIZE + 3)) return (0); continue; } p = (const char *)buff + offset; while (p + H_SIZE < (const char *)buff + bytes_avail) { if ((next = lha_check_header_format(p)) == 0) return (30); p += next; } offset = p - (const char *)buff; } } return (0); } static int archive_read_format_lha_options(struct archive_read *a, const char *key, const char *val) { struct lha *lha; int ret = ARCHIVE_FAILED; lha = (struct lha *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lha: hdrcharset option needs a character-set name"); else { lha->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (lha->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 lha_skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; size_t next, skip; ssize_t bytes, window; window = 4096; for (;;) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < (H_SIZE + 3)) goto fatal; continue; } if (bytes < H_SIZE) goto fatal; p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the lha header. */ while (p + H_SIZE < q) { if ((next = lha_check_header_format(p)) == 0) { skip = p - (const char *)h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += next; } skip = p - (const char *)h; __archive_read_consume(a, skip); } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out LHa header"); return (ARCHIVE_FATAL); } static int truncated_error(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa header"); return (ARCHIVE_FATAL); } static int archive_read_format_lha_read_header(struct archive_read *a, struct archive_entry *entry) { struct archive_wstring linkname; struct archive_wstring pathname; struct lha *lha; const unsigned char *p; const char *signature; int err; struct archive_mstring conv_buffer; const wchar_t *conv_buffer_p; lha_crc16_init(); a->archive.archive_format = ARCHIVE_FORMAT_LHA; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "lha"; lha = (struct lha *)(a->format->data); lha->decompress_init = 0; lha->end_of_entry = 0; lha->end_of_entry_cleanup = 0; lha->entry_unconsumed = 0; if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) { /* * LHa archiver added 0 to the tail of its archive file as * the mark of the end of the archive. */ signature = __archive_read_ahead(a, sizeof(signature[0]), NULL); if (signature == NULL || signature[0] == 0) return (ARCHIVE_EOF); return (truncated_error(a)); } signature = (const char *)p; if (lha->found_first_header == 0 && signature[0] == 'M' && signature[1] == 'Z') { /* This is an executable? Must be self-extracting... */ err = lha_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL) return (truncated_error(a)); signature = (const char *)p; } /* signature[0] == 0 means the end of an LHa archive file. */ if (signature[0] == 0) return (ARCHIVE_EOF); /* * Check the header format and method type. */ if (lha_check_header_format(p) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad LHa file"); return (ARCHIVE_FATAL); } /* We've found the first header. */ lha->found_first_header = 1; /* Set a default value and common data */ lha->header_size = 0; lha->level = p[H_LEVEL_OFFSET]; lha->method[0] = p[H_METHOD_OFFSET+1]; lha->method[1] = p[H_METHOD_OFFSET+2]; lha->method[2] = p[H_METHOD_OFFSET+3]; if (memcmp(lha->method, "lhd", 3) == 0) lha->directory = 1; else lha->directory = 0; if (memcmp(lha->method, "lh0", 3) == 0 || memcmp(lha->method, "lz4", 3) == 0) lha->entry_is_compressed = 0; else lha->entry_is_compressed = 1; lha->compsize = 0; lha->origsize = 0; lha->setflag = 0; lha->birthtime = 0; lha->birthtime_tv_nsec = 0; lha->mtime = 0; lha->mtime_tv_nsec = 0; lha->atime = 0; lha->atime_tv_nsec = 0; lha->mode = (lha->directory)? 0777 : 0666; lha->uid = 0; lha->gid = 0; archive_string_empty(&lha->dirname); archive_string_empty(&lha->filename); lha->dos_attr = 0; if (lha->opt_sconv != NULL) { lha->sconv_dir = lha->opt_sconv; lha->sconv_fname = lha->opt_sconv; } else { lha->sconv_dir = NULL; lha->sconv_fname = NULL; } switch (p[H_LEVEL_OFFSET]) { case 0: err = lha_read_file_header_0(a, lha); break; case 1: err = lha_read_file_header_1(a, lha); break; case 2: err = lha_read_file_header_2(a, lha); break; case 3: err = lha_read_file_header_3(a, lha); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]); err = ARCHIVE_FATAL; break; } if (err < ARCHIVE_WARN) return (err); if (!lha->directory && archive_strlen(&lha->filename) == 0) /* The filename has not been set */ return (truncated_error(a)); /* * Make a pathname from a dirname and a filename, after converting to Unicode. * This is because codepages might differ between dirname and filename. */ archive_string_init(&pathname); archive_string_init(&linkname); archive_string_init(&conv_buffer.aes_mbs); archive_string_init(&conv_buffer.aes_mbs_in_locale); archive_string_init(&conv_buffer.aes_utf8); archive_string_init(&conv_buffer.aes_wcs); if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to Unicode.", archive_string_conversion_charset_name(lha->sconv_dir)); err = ARCHIVE_FATAL; } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) err = ARCHIVE_FATAL; if (err == ARCHIVE_FATAL) { archive_mstring_clean(&conv_buffer); archive_wstring_free(&pathname); archive_wstring_free(&linkname); return (err); } archive_wstring_copy(&pathname, &conv_buffer.aes_wcs); archive_string_empty(&conv_buffer.aes_mbs); archive_string_empty(&conv_buffer.aes_mbs_in_locale); archive_string_empty(&conv_buffer.aes_utf8); archive_wstring_empty(&conv_buffer.aes_wcs); if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to Unicode.", archive_string_conversion_charset_name(lha->sconv_fname)); err = ARCHIVE_FATAL; } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) err = ARCHIVE_FATAL; if (err == ARCHIVE_FATAL) { archive_mstring_clean(&conv_buffer); archive_wstring_free(&pathname); archive_wstring_free(&linkname); return (err); } archive_wstring_concat(&pathname, &conv_buffer.aes_wcs); archive_mstring_clean(&conv_buffer); if ((lha->mode & AE_IFMT) == AE_IFLNK) { /* * Extract the symlink-name if it's included in the pathname. */ if (!lha_parse_linkname(&linkname, &pathname)) { /* We couldn't get the symlink-name. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown symlink-name"); archive_wstring_free(&pathname); archive_wstring_free(&linkname); return (ARCHIVE_FAILED); } } else { /* * Make sure a file-type is set. * The mode has been overridden if it is in the extended data. */ lha->mode = (lha->mode & ~AE_IFMT) | ((lha->directory)? AE_IFDIR: AE_IFREG); } if ((lha->setflag & UNIX_MODE_IS_SET) == 0 && (lha->dos_attr & 1) != 0) lha->mode &= ~(0222);/* read only. */ /* * Set basic file parameters. */ archive_entry_copy_pathname_w(entry, pathname.s); archive_wstring_free(&pathname); if (archive_strlen(&linkname) > 0) { archive_entry_copy_symlink_w(entry, linkname.s); } else archive_entry_set_symlink(entry, NULL); archive_wstring_free(&linkname); /* * When a header level is 0, there is a possibility that * a pathname and a symlink has '\' character, a directory * separator in DOS/Windows. So we should convert it to '/'. */ if (p[H_LEVEL_OFFSET] == 0) lha_replace_path_separator(lha, entry); archive_entry_set_mode(entry, lha->mode); archive_entry_set_uid(entry, lha->uid); archive_entry_set_gid(entry, lha->gid); if (archive_strlen(&lha->uname) > 0) archive_entry_set_uname(entry, lha->uname.s); if (archive_strlen(&lha->gname) > 0) archive_entry_set_gname(entry, lha->gname.s); if (lha->setflag & BIRTHTIME_IS_SET) { archive_entry_set_birthtime(entry, lha->birthtime, lha->birthtime_tv_nsec); archive_entry_set_ctime(entry, lha->birthtime, lha->birthtime_tv_nsec); } else { archive_entry_unset_birthtime(entry); archive_entry_unset_ctime(entry); } archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec); if (lha->setflag & ATIME_IS_SET) archive_entry_set_atime(entry, lha->atime, lha->atime_tv_nsec); else archive_entry_unset_atime(entry); if (lha->directory || archive_entry_symlink(entry) != NULL) archive_entry_unset_size(entry); else archive_entry_set_size(entry, lha->origsize); /* * Prepare variables used to read a file content. */ lha->entry_bytes_remaining = lha->compsize; if (lha->entry_bytes_remaining < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa entry size"); return (ARCHIVE_FATAL); } lha->entry_offset = 0; lha->entry_crc_calculated = 0; /* * This file does not have a content. */ if (lha->directory || lha->compsize == 0) lha->end_of_entry = 1; snprintf(lha->format_name, sizeof(lha->format_name), "lha -%c%c%c-", lha->method[0], lha->method[1], lha->method[2]); a->archive.archive_format_name = lha->format_name; return (err); } /* * Replace a DOS path separator '\' by a character '/'. * Some multi-byte character set have a character '\' in its second byte. */ static void lha_replace_path_separator(struct lha *lha, struct archive_entry *entry) { const wchar_t *wp; size_t i; if ((wp = archive_entry_pathname_w(entry)) != NULL) { archive_wstrcpy(&(lha->ws), wp); for (i = 0; i < archive_strlen(&(lha->ws)); i++) { if (lha->ws.s[i] == L'\\') lha->ws.s[i] = L'/'; } archive_entry_copy_pathname_w(entry, lha->ws.s); } if ((wp = archive_entry_symlink_w(entry)) != NULL) { archive_wstrcpy(&(lha->ws), wp); for (i = 0; i < archive_strlen(&(lha->ws)); i++) { if (lha->ws.s[i] == L'\\') lha->ws.s[i] = L'/'; } archive_entry_copy_symlink_w(entry, lha->ws.s); } } /* * Header 0 format * * +0 +1 +2 +7 +11 * +---------------+----------+----------------+-------------------+ * |header size(*1)|header sum|compression type|compressed size(*2)| * +---------------+----------+----------------+-------------------+ * <---------------------(*1)----------* * * +11 +15 +17 +19 +20 +21 * +-----------------+---------+---------+--------------+----------------+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)| * +-----------------+---------+---------+--------------+----------------+ * *--------------------------------(*1)---------------------------------* * * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+2+(*4) * +---------------+---------+----------+----------------+------------------+ * |name length(*3)|file name|file CRC16|extra header(*4)| compressed data | * +---------------+---------+----------+----------------+------------------+ * <--(*3)-> <------(*2)------> * *----------------------(*1)--------------------------> * */ #define H0_HEADER_SIZE_OFFSET 0 #define H0_HEADER_SUM_OFFSET 1 #define H0_COMP_SIZE_OFFSET 7 #define H0_ORIG_SIZE_OFFSET 11 #define H0_DOS_TIME_OFFSET 15 #define H0_NAME_LEN_OFFSET 21 #define H0_FILE_NAME_OFFSET 22 #define H0_FIXED_SIZE 24 static int lha_read_file_header_0(struct archive_read *a, struct lha *lha) { const unsigned char *p; int extdsize, namelen; unsigned char headersum, sum_calculated; if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2; headersum = p[H0_HEADER_SUM_OFFSET]; lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET); lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET); namelen = p[H0_NAME_LEN_OFFSET]; extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen; if ((namelen > 221 || extdsize < 0) && extdsize != -2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen); /* When extdsize == -2, A CRC16 value is not present in the header. */ if (extdsize >= 0) { lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; } sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); /* Read an extended header */ if (extdsize > 0) { /* This extended data is set by 'LHa for UNIX' only. * Maybe fixed size. */ p += H0_FILE_NAME_OFFSET + namelen + 2; if (p[0] == 'U' && extdsize == 12) { /* p[1] is a minor version. */ lha->mtime = archive_le32dec(&p[2]); lha->mode = archive_le16dec(&p[6]); lha->uid = archive_le16dec(&p[8]); lha->gid = archive_le16dec(&p[10]); lha->setflag |= UNIX_MODE_IS_SET; } } __archive_read_consume(a, lha->header_size); if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } /* * Header 1 format * * +0 +1 +2 +7 +11 * +---------------+----------+----------------+-------------+ * |header size(*1)|header sum|compression type|skip size(*2)| * +---------------+----------+----------------+-------------+ * <---------------(*1)----------* * * +11 +15 +17 +19 +20 +21 * +-----------------+---------+---------+--------------+----------------+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)| * +-----------------+---------+---------+--------------+----------------+ * *-------------------------------(*1)----------------------------------* * * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+3 +22+(*3)+3+(*4) * +---------------+---------+----------+-----------+-----------+ * |name length(*3)|file name|file CRC16| creator |padding(*4)| * +---------------+---------+----------+-----------+-----------+ * <--(*3)-> * *----------------------------(*1)----------------------------* * * +22+(*3)+3+(*4) +22+(*3)+3+(*4)+2 +22+(*3)+3+(*4)+2+(*5) * +----------------+---------------------+------------------------+ * |next header size| extended header(*5) | compressed data | * +----------------+---------------------+------------------------+ * *------(*1)-----> <--------------------(*2)--------------------> */ #define H1_HEADER_SIZE_OFFSET 0 #define H1_HEADER_SUM_OFFSET 1 #define H1_COMP_SIZE_OFFSET 7 #define H1_ORIG_SIZE_OFFSET 11 #define H1_DOS_TIME_OFFSET 15 #define H1_NAME_LEN_OFFSET 21 #define H1_FILE_NAME_OFFSET 22 #define H1_FIXED_SIZE 27 static int lha_read_file_header_1(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int i, err, err2; int namelen, padding; unsigned char headersum, sum_calculated; err = ARCHIVE_OK; if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2; headersum = p[H1_HEADER_SUM_OFFSET]; /* Note: An extended header size is included in a compsize. */ lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET); lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET); namelen = p[H1_NAME_LEN_OFFSET]; /* Calculate a padding size. The result will be normally 0 only(?) */ padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen; if (namelen > 230 || padding < 0) goto invalid; if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); for (i = 0; i < namelen; i++) { if (p[i + H1_FILE_NAME_OFFSET] == 0xff) goto invalid;/* Invalid filename. */ } archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen); lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); /* Consume used bytes but not include `next header size' data * since it will be consumed in lha_read_file_extended_header(). */ __archive_read_consume(a, lha->header_size - 2); /* Read extended headers */ err2 = lha_read_file_extended_header(a, lha, NULL, 2, (size_t)(lha->compsize + 2), &extdsize); if (err2 < ARCHIVE_WARN) return (err2); if (err2 < err) err = err2; /* Get a real compressed file size. */ lha->compsize -= extdsize - 2; if (lha->compsize < 0) goto invalid; /* Invalid compressed file size */ if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); return (ARCHIVE_FATAL); } return (err); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } /* * Header 2 format * * +0 +2 +7 +11 +15 * +---------------+----------------+-------------------+-----------------+ * |header size(*1)|compression type|compressed size(*2)|uncompressed size| * +---------------+----------------+-------------------+-----------------+ * <--------------------------------(*1)---------------------------------* * * +15 +19 +20 +21 +23 +24 * +-----------------+------------+----------------+----------+-----------+ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16| creator | * +-----------------+------------+----------------+----------+-----------+ * *---------------------------------(*1)---------------------------------* * * +24 +26 +26+(*3) +26+(*3)+(*4) * +----------------+-------------------+-------------+-------------------+ * |next header size|extended header(*3)| padding(*4) | compressed data | * +----------------+-------------------+-------------+-------------------+ * *--------------------------(*1)-------------------> <------(*2)-------> * */ #define H2_HEADER_SIZE_OFFSET 0 #define H2_COMP_SIZE_OFFSET 7 #define H2_ORIG_SIZE_OFFSET 11 #define H2_TIME_OFFSET 15 #define H2_CRC_OFFSET 21 #define H2_FIXED_SIZE 24 static int lha_read_file_header_2(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int err, padding; uint16_t header_crc; if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET); lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET); lha->mtime = archive_le32dec(p + H2_TIME_OFFSET); lha->crc = archive_le16dec(p + H2_CRC_OFFSET); lha->setflag |= CRC_IS_SET; if (lha->header_size < H2_FIXED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header size"); return (ARCHIVE_FATAL); } header_crc = lha_crc16(0, p, H2_FIXED_SIZE); __archive_read_consume(a, H2_FIXED_SIZE); /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 2, lha->header_size - H2_FIXED_SIZE, &extdsize); if (err < ARCHIVE_WARN) return (err); /* Calculate a padding size. The result will be normally 0 or 1. */ padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize); if (padding > 0) { if ((p = __archive_read_ahead(a, padding, NULL)) == NULL) return (truncated_error(a)); header_crc = lha_crc16(header_crc, p, padding); __archive_read_consume(a, padding); } if (header_crc != lha->header_crc) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header CRC error"); return (ARCHIVE_FATAL); #endif } return (err); } /* * Header 3 format * * +0 +2 +7 +11 +15 * +------------+----------------+-------------------+-----------------+ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size| * +------------+----------------+-------------------+-----------------+ * <-------------------------------(*1)-------------------------------* * * +15 +19 +20 +21 +23 +24 * +-----------------+------------+----------------+----------+-----------+ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16| creator | * +-----------------+------------+----------------+----------+-----------+ * *--------------------------------(*1)----------------------------------* * * +24 +28 +32 +32+(*3) * +---------------+----------------+-------------------+-----------------+ * |header size(*1)|next header size|extended header(*3)| compressed data | * +---------------+----------------+-------------------+-----------------+ * *------------------------(*1)-----------------------> <------(*2)-----> * */ #define H3_FIELD_LEN_OFFSET 0 #define H3_COMP_SIZE_OFFSET 7 #define H3_ORIG_SIZE_OFFSET 11 #define H3_TIME_OFFSET 15 #define H3_CRC_OFFSET 21 #define H3_HEADER_SIZE_OFFSET 24 #define H3_FIXED_SIZE 28 static int lha_read_file_header_3(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int err; uint16_t header_crc; if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4) goto invalid; lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET); lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET); lha->mtime = archive_le32dec(p + H3_TIME_OFFSET); lha->crc = archive_le16dec(p + H3_CRC_OFFSET); lha->setflag |= CRC_IS_SET; if (lha->header_size < H3_FIXED_SIZE + 4) goto invalid; header_crc = lha_crc16(0, p, H3_FIXED_SIZE); __archive_read_consume(a, H3_FIXED_SIZE); /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 4, lha->header_size - H3_FIXED_SIZE, &extdsize); if (err < ARCHIVE_WARN) return (err); if (header_crc != lha->header_crc) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header CRC error"); return (ARCHIVE_FATAL); #endif } return (err); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } /* * Extended header format * * +0 +2 +3 -- used in header 1 and 2 * +0 +4 +5 -- used in header 3 * +--------------+---------+-------------------+--------------+-- * |ex-header size|header id| data |ex-header size| ....... * +--------------+---------+-------------------+--------------+-- * <-------------( ex-header size)------------> <-- next extended header --* * * If the ex-header size is zero, it is the make of the end of extended * headers. * */ static int lha_read_file_extended_header(struct archive_read *a, struct lha *lha, uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size) { const void *h; const unsigned char *extdheader; size_t extdsize; size_t datasize; unsigned int i; unsigned char extdtype; #define EXT_HEADER_CRC 0x00 /* Header CRC and information*/ #define EXT_FILENAME 0x01 /* Filename */ #define EXT_DIRECTORY 0x02 /* Directory name */ #define EXT_DOS_ATTR 0x40 /* MS-DOS attribute */ #define EXT_TIMESTAMP 0x41 /* Windows time stamp */ #define EXT_FILESIZE 0x42 /* Large file size */ #define EXT_TIMEZONE 0x43 /* Time zone */ #define EXT_UTF16_FILENAME 0x44 /* UTF-16 filename */ #define EXT_UTF16_DIRECTORY 0x45 /* UTF-16 directory name */ #define EXT_CODEPAGE 0x46 /* Codepage */ #define EXT_UNIX_MODE 0x50 /* File permission */ #define EXT_UNIX_GID_UID 0x51 /* gid,uid */ #define EXT_UNIX_GNAME 0x52 /* Group name */ #define EXT_UNIX_UNAME 0x53 /* User name */ #define EXT_UNIX_MTIME 0x54 /* Modified time */ #define EXT_OS2_NEW_ATTR 0x7f /* new attribute(OS/2 only) */ #define EXT_NEW_ATTR 0xff /* new attribute */ *total_size = sizefield_length; for (;;) { /* Read an extended header size. */ if ((h = __archive_read_ahead(a, sizefield_length, NULL)) == NULL) return (truncated_error(a)); /* Check if the size is the zero indicates the end of the * extended header. */ if (sizefield_length == sizeof(uint16_t)) extdsize = archive_le16dec(h); else extdsize = archive_le32dec(h); if (extdsize == 0) { /* End of extended header */ if (crc != NULL) *crc = lha_crc16(*crc, h, sizefield_length); __archive_read_consume(a, sizefield_length); return (ARCHIVE_OK); } /* Sanity check to the extended header size. */ if (((uint64_t)*total_size + extdsize) > (uint64_t)limitsize || extdsize <= (size_t)sizefield_length) goto invalid; /* Read the extended header. */ if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL) return (truncated_error(a)); *total_size += extdsize; extdheader = (const unsigned char *)h; /* Get the extended header type. */ extdtype = extdheader[sizefield_length]; /* Calculate an extended data size. */ datasize = extdsize - (1 + sizefield_length); /* Skip an extended header size field and type field. */ extdheader += sizefield_length + 1; if (crc != NULL && extdtype != EXT_HEADER_CRC) *crc = lha_crc16(*crc, h, extdsize); switch (extdtype) { case EXT_HEADER_CRC: /* We only use a header CRC. Following data will not * be used. */ if (datasize >= 2) { lha->header_crc = archive_le16dec(extdheader); if (crc != NULL) { static const char zeros[2] = {0, 0}; *crc = lha_crc16(*crc, h, extdsize - datasize); /* CRC value itself as zero */ *crc = lha_crc16(*crc, zeros, 2); *crc = lha_crc16(*crc, extdheader+2, datasize - 2); } } break; case EXT_FILENAME: if (datasize == 0) { /* maybe directory header */ archive_string_empty(&lha->filename); break; } if (extdheader[0] == '\0') goto invalid; archive_strncpy(&lha->filename, (const char *)extdheader, datasize); break; case EXT_UTF16_FILENAME: if (datasize == 0) { /* maybe directory header */ archive_string_empty(&lha->filename); break; } else if (datasize & 1) { /* UTF-16 characters take always 2 or 4 bytes */ goto invalid; } if (extdheader[0] == '\0') goto invalid; archive_string_empty(&lha->filename); archive_array_append(&lha->filename, (const char *)extdheader, datasize); /* Setup a string conversion for a filename. */ lha->sconv_fname = archive_string_conversion_from_charset(&a->archive, "UTF-16LE", 1); if (lha->sconv_fname == NULL) return (ARCHIVE_FATAL); break; case EXT_DIRECTORY: if (datasize == 0 || extdheader[0] == '\0') /* no directory name data. exit this case. */ goto invalid; archive_strncpy(&lha->dirname, (const char *)extdheader, datasize); /* * Convert directory delimiter from 0xFF * to '/' for local system. */ for (i = 0; i < lha->dirname.length; i++) { if ((unsigned char)lha->dirname.s[i] == 0xFF) lha->dirname.s[i] = '/'; } /* Is last character directory separator? */ if (lha->dirname.s[lha->dirname.length-1] != '/') /* invalid directory data */ goto invalid; break; case EXT_UTF16_DIRECTORY: /* UTF-16 characters take always 2 or 4 bytes */ if (datasize == 0 || (datasize & 1) || extdheader[0] == '\0') { /* no directory name data. exit this case. */ goto invalid; } archive_string_empty(&lha->dirname); archive_array_append(&lha->dirname, (const char *)extdheader, datasize); lha->sconv_dir = archive_string_conversion_from_charset(&a->archive, "UTF-16LE", 1); if (lha->sconv_dir == NULL) return (ARCHIVE_FATAL); else { /* * Convert directory delimiter from 0xFFFF * to '/' for local system. */ uint16_t dirSep; uint16_t d = 1; if (archive_be16dec(&d) == 1) dirSep = 0x2F00; else dirSep = 0x002F; /* UTF-16LE character */ uint16_t *utf16name = (uint16_t *)lha->dirname.s; for (i = 0; i < lha->dirname.length / 2; i++) { if (utf16name[i] == 0xFFFF) { utf16name[i] = dirSep; } } /* Is last character directory separator? */ if (utf16name[lha->dirname.length / 2 - 1] != dirSep) { /* invalid directory data */ goto invalid; } } break; case EXT_DOS_ATTR: if (datasize == 2) lha->dos_attr = (unsigned char) (archive_le16dec(extdheader) & 0xff); break; case EXT_TIMESTAMP: if (datasize == (sizeof(uint64_t) * 3)) { lha->birthtime = lha_win_time( archive_le64dec(extdheader), &lha->birthtime_tv_nsec); extdheader += sizeof(uint64_t); lha->mtime = lha_win_time( archive_le64dec(extdheader), &lha->mtime_tv_nsec); extdheader += sizeof(uint64_t); lha->atime = lha_win_time( archive_le64dec(extdheader), &lha->atime_tv_nsec); lha->setflag |= BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_FILESIZE: if (datasize == sizeof(uint64_t) * 2) { lha->compsize = archive_le64dec(extdheader); extdheader += sizeof(uint64_t); lha->origsize = archive_le64dec(extdheader); } break; case EXT_CODEPAGE: /* Get an archived filename charset from codepage. * This overwrites the charset specified by * hdrcharset option. */ if (datasize == sizeof(uint32_t)) { struct archive_string cp; const char *charset; archive_string_init(&cp); switch (archive_le32dec(extdheader)) { case 65001: /* UTF-8 */ charset = "UTF-8"; break; default: archive_string_sprintf(&cp, "CP%d", (int)archive_le32dec(extdheader)); charset = cp.s; break; } lha->sconv_dir = archive_string_conversion_from_charset( &(a->archive), charset, 1); lha->sconv_fname = archive_string_conversion_from_charset( &(a->archive), charset, 1); archive_string_free(&cp); if (lha->sconv_dir == NULL) return (ARCHIVE_FATAL); if (lha->sconv_fname == NULL) return (ARCHIVE_FATAL); } break; case EXT_UNIX_MODE: if (datasize == sizeof(uint16_t)) { lha->mode = archive_le16dec(extdheader); lha->setflag |= UNIX_MODE_IS_SET; } break; case EXT_UNIX_GID_UID: if (datasize == (sizeof(uint16_t) * 2)) { lha->gid = archive_le16dec(extdheader); lha->uid = archive_le16dec(extdheader+2); } break; case EXT_UNIX_GNAME: if (datasize > 0) archive_strncpy(&lha->gname, (const char *)extdheader, datasize); break; case EXT_UNIX_UNAME: if (datasize > 0) archive_strncpy(&lha->uname, (const char *)extdheader, datasize); break; case EXT_UNIX_MTIME: if (datasize == sizeof(uint32_t)) lha->mtime = archive_le32dec(extdheader); break; case EXT_OS2_NEW_ATTR: /* This extended header is OS/2 depend. */ if (datasize == 16) { lha->dos_attr = (unsigned char) (archive_le16dec(extdheader) & 0xff); lha->mode = archive_le16dec(extdheader+2); lha->gid = archive_le16dec(extdheader+4); lha->uid = archive_le16dec(extdheader+6); lha->birthtime = archive_le32dec(extdheader+8); lha->atime = archive_le32dec(extdheader+12); lha->setflag |= UNIX_MODE_IS_SET | BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_NEW_ATTR: if (datasize == 20) { lha->mode = (mode_t)archive_le32dec(extdheader); lha->gid = archive_le32dec(extdheader+4); lha->uid = archive_le32dec(extdheader+8); lha->birthtime = archive_le32dec(extdheader+12); lha->atime = archive_le32dec(extdheader+16); lha->setflag |= UNIX_MODE_IS_SET | BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_TIMEZONE: /* Not supported */ break; default: break; } __archive_read_consume(a, extdsize); } invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended LHa header"); return (ARCHIVE_FATAL); } static int lha_end_of_entry(struct archive_read *a) { struct lha *lha = (struct lha *)(a->format->data); int r = ARCHIVE_EOF; if (!lha->end_of_entry_cleanup) { if ((lha->setflag & CRC_IS_SET) && lha->crc != lha->entry_crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa data CRC error"); r = ARCHIVE_WARN; } /* End-of-entry cleanup done. */ lha->end_of_entry_cleanup = 1; } return (r); } static int archive_read_format_lha_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); int r; if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); lha->entry_unconsumed = 0; } if (lha->end_of_entry) { *offset = lha->entry_offset; *size = 0; *buff = NULL; return (lha_end_of_entry(a)); } if (lha->entry_is_compressed) r = lha_read_data_lzh(a, buff, size, offset); else /* No compression. */ r = lha_read_data_none(a, buff, size, offset); return (r); } /* * Read a file content in no compression. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * lha->end_of_entry if it consumes all of the data. */ static int lha_read_data_none(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); ssize_t bytes_avail; if (lha->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = lha->entry_offset; lha->end_of_entry = 1; return (ARCHIVE_OK); } /* * 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. */ *buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa file data"); return (ARCHIVE_FATAL); } if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail); *size = bytes_avail; *offset = lha->entry_offset; lha->entry_offset += bytes_avail; lha->entry_bytes_remaining -= bytes_avail; if (lha->entry_bytes_remaining == 0) lha->end_of_entry = 1; lha->entry_unconsumed = bytes_avail; return (ARCHIVE_OK); } /* * Read a file content in LZHUFF encoding. * * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes * all of the data. */ static int lha_read_data_lzh(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); ssize_t bytes_avail; int r; /* If we haven't yet read any data, initialize the decompressor. */ if (!lha->decompress_init) { r = lzh_decode_init(&(lha->strm), lha->method); switch (r) { case ARCHIVE_OK: break; case ARCHIVE_FAILED: /* Unsupported compression. */ *buff = NULL; *size = 0; *offset = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported lzh compression method -%c%c%c-", lha->method[0], lha->method[1], lha->method[2]); /* We know compressed size; just skip it. */ archive_read_format_lha_read_data_skip(a); return (ARCHIVE_WARN); default: archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory " "for lzh decompression"); return (ARCHIVE_FATAL); } /* We've initialized decompression for this stream. */ lha->decompress_init = 1; lha->strm.avail_out = 0; lha->strm.total_out = 0; } /* * 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. */ lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa file body"); return (ARCHIVE_FATAL); } if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; lha->strm.avail_in = (int)bytes_avail; lha->strm.total_in = 0; lha->strm.avail_out = 0; r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining); switch (r) { case ARCHIVE_OK: break; case ARCHIVE_EOF: lha->end_of_entry = 1; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad lzh data"); return (ARCHIVE_FAILED); } lha->entry_unconsumed = lha->strm.total_in; lha->entry_bytes_remaining -= lha->strm.total_in; if (lha->strm.avail_out) { *offset = lha->entry_offset; *size = lha->strm.avail_out; *buff = lha->strm.ref_ptr; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, *size); lha->entry_offset += *size; } else { *offset = lha->entry_offset; *size = 0; *buff = NULL; if (lha->end_of_entry) return (lha_end_of_entry(a)); } return (ARCHIVE_OK); } /* * Skip a file content. */ static int archive_read_format_lha_read_data_skip(struct archive_read *a) { struct lha *lha; int64_t bytes_skipped; lha = (struct lha *)(a->format->data); if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); lha->entry_unconsumed = 0; } /* if we've already read to end of data, we're done. */ if (lha->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 = __archive_read_consume(a, lha->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* This entry is finished and done. */ lha->end_of_entry_cleanup = lha->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_lha_cleanup(struct archive_read *a) { struct lha *lha = (struct lha *)(a->format->data); lzh_decode_free(&(lha->strm)); archive_string_free(&(lha->dirname)); archive_string_free(&(lha->filename)); archive_string_free(&(lha->uname)); archive_string_free(&(lha->gname)); archive_wstring_free(&(lha->ws)); free(lha); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * 'LHa for UNIX' utility has archived a symbolic-link name after * a pathname with '|' character. * This function extracts the symbolic-link name from the pathname. * * example. * 1. a symbolic-name is 'aaa/bb/cc' * 2. a filename is 'xxx/bbb' * then a archived pathname is 'xxx/bbb|aaa/bb/cc' */ static int lha_parse_linkname(struct archive_wstring *linkname, struct archive_wstring *pathname) { wchar_t * linkptr; size_t symlen; linkptr = wcschr(pathname->s, L'|'); if (linkptr != NULL) { symlen = wcslen(linkptr + 1); archive_wstrncpy(linkname, linkptr+1, symlen); *linkptr = 0; pathname->length = wcslen(pathname->s); return (1); } return (0); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t lha_dos_time(const unsigned char *p) { int msTime, msDate; struct tm ts; msTime = archive_le16dec(p); msDate = 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)); } /* Convert an MS-Windows-style date/time into Unix-style time. */ static time_t lha_win_time(uint64_t wintime, long *ns) { #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) if (wintime >= EPOC_TIME) { wintime -= EPOC_TIME; /* 1970-01-01 00:00:00 (UTC) */ if (ns != NULL) *ns = (long)(wintime % 10000000) * 100; return (wintime / 10000000); } else { if (ns != NULL) *ns = 0; return (0); } } static unsigned char lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) { unsigned char const *p = (unsigned char const *)pp; p += offset; for (;size > 0; --size) sum += *p++; return (sum); } static uint16_t crc16tbl[2][256]; static void lha_crc16_init(void) { unsigned int i; static int crc16init = 0; if (crc16init) return; crc16init = 1; for (i = 0; i < 256; i++) { unsigned int j; uint16_t crc = (uint16_t)i; for (j = 8; j; j--) crc = (crc >> 1) ^ ((crc & 1) * 0xA001); crc16tbl[0][i] = crc; } for (i = 0; i < 256; i++) { crc16tbl[1][i] = (crc16tbl[0][i] >> 8) ^ crc16tbl[0][crc16tbl[0][i] & 0xff]; } } static uint16_t lha_crc16(uint16_t crc, const void *pp, size_t len) { const unsigned char *p = (const unsigned char *)pp; const uint16_t *buff; const union { uint32_t i; char c[4]; } u = { 0x01020304 }; if (len == 0) return crc; /* Process unaligned address. */ if (((uintptr_t)p) & (uintptr_t)0x1) { crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; len--; } buff = (const uint16_t *)p; /* * Modern C compiler such as GCC does not unroll automatically yet * without unrolling pragma, and Clang is so. So we should * unroll this loop for its performance. */ for (;len >= 8; len -= 8) { /* This if statement expects compiler optimization will * remove the statement which will not be executed. */ #undef bswap16 #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ # define bswap16(x) _byteswap_ushort(x) #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4) /* GCC 4.8 and later has __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #elif defined(__clang__) /* All clang versions have __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #else # define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) #endif #define CRC16W do { \ if(u.c[0] == 1) { /* Big endian */ \ crc ^= bswap16(*buff); buff++; \ } else \ crc ^= *buff++; \ crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\ } while (0) CRC16W; CRC16W; CRC16W; CRC16W; #undef CRC16W #undef bswap16 } p = (const unsigned char *)buff; for (;len; len--) { crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; } return crc; } /* * Initialize LZHUF decoder. * * Returns ARCHIVE_OK if initialization was successful. * Returns ARCHIVE_FAILED if method is unsupported. * Returns ARCHIVE_FATAL if initialization failed; memory allocation * error occurred. */ static int lzh_decode_init(struct lzh_stream *strm, const char *method) { struct lzh_dec *ds; int w_bits, w_size; 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; if (method == NULL || method[0] != 'l' || method[1] != 'h') return (ARCHIVE_FAILED); switch (method[2]) { case '5': w_bits = 13;/* 8KiB for window */ break; case '6': w_bits = 15;/* 32KiB for window */ break; case '7': w_bits = 16;/* 64KiB for window */ break; default: return (ARCHIVE_FAILED);/* Not supported. */ } ds->error = ARCHIVE_FATAL; /* Expand a window size up to 128 KiB for decompressing process * performance whatever its original window size is. */ ds->w_size = 1U << 17; ds->w_mask = ds->w_size -1; if (ds->w_buff == NULL) { ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); } w_size = 1U << w_bits; memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size); ds->w_pos = 0; ds->state = 0; ds->pos_pt_len_size = w_bits + 1; ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4; ds->literal_pt_len_size = PT_BITLEN_SIZE; ds->literal_pt_len_bits = 5; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->lt.len_bits = 9; if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->error = 0; return (ARCHIVE_OK); } /* * Release LZHUF decoder. */ static void lzh_decode_free(struct lzh_stream *strm) { if (strm->ds == NULL) return; free(strm->ds->w_buff); lzh_huffman_free(&(strm->ds->lt)); lzh_huffman_free(&(strm->ds->pt)); free(strm->ds); strm->ds = NULL; } /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define lzh_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define lzh_br_bits(br, n) \ (((uint16_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define lzh_br_bits_forced(br, n) \ (((uint16_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 lzh_br_read_ahead_0(strm, br, n) \ (lzh_br_has(br, (n)) || lzh_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 lzh_br_read_ahead(strm, br, n) \ (lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n))) /* Notify how many bits we consumed. */ #define lzh_br_consume(br, n) ((br)->cache_avail -= (n)) #define lzh_br_unconsume(br, n) ((br)->cache_avail += (n)) static const uint16_t cache_masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; /* * 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 lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) { int n = CACHE_BITS - br->cache_avail; for (;;) { const int x = n >> 3; if (strm->avail_in >= x) { switch (x) { case 8: br->cache_buffer = ((uint64_t)strm->next_in[0]) << 56 | ((uint64_t)strm->next_in[1]) << 48 | ((uint64_t)strm->next_in[2]) << 40 | ((uint64_t)strm->next_in[3]) << 32 | ((uint32_t)strm->next_in[4]) << 24 | ((uint32_t)strm->next_in[5]) << 16 | ((uint32_t)strm->next_in[6]) << 8 | (uint32_t)strm->next_in[7]; strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); case 7: br->cache_buffer = (br->cache_buffer << 56) | ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | - ((uint32_t)strm->next_in[3]) << 24 | - ((uint32_t)strm->next_in[4]) << 16 | - ((uint32_t)strm->next_in[5]) << 8 | - (uint32_t)strm->next_in[6]; + ((uint64_t)strm->next_in[3]) << 24 | + ((uint64_t)strm->next_in[4]) << 16 | + ((uint64_t)strm->next_in[5]) << 8 | + (uint64_t)strm->next_in[6]; strm->next_in += 7; strm->avail_in -= 7; br->cache_avail += 7 * 8; return (1); case 6: br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[0]) << 40 | ((uint64_t)strm->next_in[1]) << 32 | - ((uint32_t)strm->next_in[2]) << 24 | - ((uint32_t)strm->next_in[3]) << 16 | - ((uint32_t)strm->next_in[4]) << 8 | - (uint32_t)strm->next_in[5]; + ((uint64_t)strm->next_in[2]) << 24 | + ((uint64_t)strm->next_in[3]) << 16 | + ((uint64_t)strm->next_in[4]) << 8 | + (uint64_t)strm->next_in[5]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } } if (strm->avail_in == 0) { /* There is not enough compressed data to fill up the * cache buffer. */ return (0); } br->cache_buffer = (br->cache_buffer << 8) | *strm->next_in++; strm->avail_in--; br->cache_avail += 8; n -= 8; } } /* * Decode LZHUF. * * 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. * 4. 'last' flag is very important, you must set 1 to the flag if there * is no input data. The lha compressed data format does not provide how * to know the compressed data is really finished. * Note: lha command utility check if the total size of output bytes is * reached the uncompressed size recorded in its header. it does not mind * that the decoding process is properly finished. * GNU ZIP can decompress another compressed file made by SCO LZH compress. * it handles EOF as null to fill read buffer with zero until the decoding * process meet 2 bytes of zeros at reading a size of a next chunk, so the * zeros are treated as the mark of the end of the data although the zeros * is dummy, not the file data. */ static int lzh_read_blocks(struct lzh_stream *, int); static int lzh_decode_blocks(struct lzh_stream *, int); #define ST_RD_BLOCK 0 #define ST_RD_PT_1 1 #define ST_RD_PT_2 2 #define ST_RD_PT_3 3 #define ST_RD_PT_4 4 #define ST_RD_LITERAL_1 5 #define ST_RD_LITERAL_2 6 #define ST_RD_LITERAL_3 7 #define ST_RD_POS_DATA_1 8 #define ST_GET_LITERAL 9 #define ST_GET_POS_1 10 #define ST_GET_POS_2 11 #define ST_COPY_DATA 12 static int lzh_decode(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; int avail_in; int r; if (ds->error) return (ds->error); avail_in = strm->avail_in; do { if (ds->state < ST_GET_LITERAL) r = lzh_read_blocks(strm, last); else r = lzh_decode_blocks(strm, last); } while (r == 100); strm->total_in += avail_in - strm->avail_in; return (r); } static void lzh_emit_window(struct lzh_stream *strm, size_t s) { strm->ref_ptr = strm->ds->w_buff; strm->avail_out = (int)s; strm->total_out += s; } static int lzh_read_blocks(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; struct lzh_br *br = &(ds->br); int c = 0, i; unsigned rbits; for (;;) { switch (ds->state) { case ST_RD_BLOCK: /* * Read a block number indicates how many blocks * we will handle. The block is composed of a * literal and a match, sometimes a literal only * in particular, there are no reference data at * the beginning of the decompression. */ if (!lzh_br_read_ahead_0(strm, br, 16)) { if (!last) /* We need following data. */ return (ARCHIVE_OK); if (lzh_br_has(br, 8)) { /* * It seems there are extra bits. * 1. Compressed data is broken. * 2. `last' flag does not properly * set. */ goto failed; } if (ds->w_pos > 0) { lzh_emit_window(strm, ds->w_pos); ds->w_pos = 0; return (ARCHIVE_OK); } /* End of compressed data; we have completely * handled all compressed data. */ return (ARCHIVE_EOF); } ds->blocks_avail = lzh_br_bits(br, 16); if (ds->blocks_avail == 0) goto failed; lzh_br_consume(br, 16); /* * Read a literal table compressed in huffman * coding. */ ds->pt.len_size = ds->literal_pt_len_size; ds->pt.len_bits = ds->literal_pt_len_bits; ds->reading_position = 0; /* FALL THROUGH */ case ST_RD_PT_1: /* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are * used in reading both a literal table and a * position table. */ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_PT_1; return (ARCHIVE_OK); } ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits); lzh_br_consume(br, ds->pt.len_bits); /* FALL THROUGH */ case ST_RD_PT_2: if (ds->pt.len_avail == 0) { /* There is no bitlen. */ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) { if (last) goto failed;/* Truncated data.*/ ds->state = ST_RD_PT_2; return (ARCHIVE_OK); } if (!lzh_make_fake_table(&(ds->pt), lzh_br_bits(br, ds->pt.len_bits))) goto failed;/* Invalid data. */ lzh_br_consume(br, ds->pt.len_bits); if (ds->reading_position) ds->state = ST_GET_LITERAL; else ds->state = ST_RD_LITERAL_1; break; } else if (ds->pt.len_avail > ds->pt.len_size) goto failed;/* Invalid data. */ ds->loop = 0; memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); if (ds->pt.len_avail < 3 || ds->pt.len_size == ds->pos_pt_len_size) { ds->state = ST_RD_PT_4; break; } /* FALL THROUGH */ case ST_RD_PT_3: ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3); if (ds->loop < 3) { if (ds->loop < 0 || last) goto failed;/* Invalid data. */ /* Not completed, get following data. */ ds->state = ST_RD_PT_3; return (ARCHIVE_OK); } /* There are some null in bitlen of the literal. */ if (!lzh_br_read_ahead(strm, br, 2)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_PT_3; return (ARCHIVE_OK); } c = lzh_br_bits(br, 2); lzh_br_consume(br, 2); if (c > ds->pt.len_avail - 3) goto failed;/* Invalid data. */ for (i = 3; c-- > 0 ;) ds->pt.bitlen[i++] = 0; ds->loop = i; /* FALL THROUGH */ case ST_RD_PT_4: ds->loop = lzh_read_pt_bitlen(strm, ds->loop, ds->pt.len_avail); if (ds->loop < ds->pt.len_avail) { if (ds->loop < 0 || last) goto failed;/* Invalid data. */ /* Not completed, get following data. */ ds->state = ST_RD_PT_4; return (ARCHIVE_OK); } if (!lzh_make_huffman_table(&(ds->pt))) goto failed;/* Invalid data */ if (ds->reading_position) { ds->state = ST_GET_LITERAL; break; } /* FALL THROUGH */ case ST_RD_LITERAL_1: if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_LITERAL_1; return (ARCHIVE_OK); } ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits); lzh_br_consume(br, ds->lt.len_bits); /* FALL THROUGH */ case ST_RD_LITERAL_2: if (ds->lt.len_avail == 0) { /* There is no bitlen. */ if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) { if (last) goto failed;/* Truncated data.*/ ds->state = ST_RD_LITERAL_2; return (ARCHIVE_OK); } if (!lzh_make_fake_table(&(ds->lt), lzh_br_bits(br, ds->lt.len_bits))) goto failed;/* Invalid data */ lzh_br_consume(br, ds->lt.len_bits); ds->state = ST_RD_POS_DATA_1; break; } else if (ds->lt.len_avail > ds->lt.len_size) goto failed;/* Invalid data */ ds->loop = 0; memset(ds->lt.freq, 0, sizeof(ds->lt.freq)); /* FALL THROUGH */ case ST_RD_LITERAL_3: i = ds->loop; while (i < ds->lt.len_avail) { if (!lzh_br_read_ahead(strm, br, ds->pt.max_bits)) { if (last) goto failed;/* Truncated data.*/ ds->loop = i; ds->state = ST_RD_LITERAL_3; return (ARCHIVE_OK); } rbits = lzh_br_bits(br, ds->pt.max_bits); c = lzh_decode_huffman(&(ds->pt), rbits); if (c > 2) { /* Note: 'c' will never be more than * eighteen since it's limited by * PT_BITLEN_SIZE, which is being set * to ds->pt.len_size through * ds->literal_pt_len_size. */ lzh_br_consume(br, ds->pt.bitlen[c]); c -= 2; ds->lt.freq[c]++; ds->lt.bitlen[i++] = c; } else if (c == 0) { lzh_br_consume(br, ds->pt.bitlen[c]); ds->lt.bitlen[i++] = 0; } else { /* c == 1 or c == 2 */ int n = (c == 1)?4:9; if (!lzh_br_read_ahead(strm, br, ds->pt.bitlen[c] + n)) { if (last) /* Truncated data. */ goto failed; ds->loop = i; ds->state = ST_RD_LITERAL_3; return (ARCHIVE_OK); } lzh_br_consume(br, ds->pt.bitlen[c]); c = lzh_br_bits(br, n); lzh_br_consume(br, n); c += (n == 4)?3:20; if (i + c > ds->lt.len_avail) goto failed;/* Invalid data */ memset(&(ds->lt.bitlen[i]), 0, c); i += c; } } if (i > ds->lt.len_avail || !lzh_make_huffman_table(&(ds->lt))) goto failed;/* Invalid data */ /* FALL THROUGH */ case ST_RD_POS_DATA_1: /* * Read a position table compressed in huffman * coding. */ ds->pt.len_size = ds->pos_pt_len_size; ds->pt.len_bits = ds->pos_pt_len_bits; ds->reading_position = 1; ds->state = ST_RD_PT_1; break; case ST_GET_LITERAL: return (100); } } failed: return (ds->error = ARCHIVE_FAILED); } static int lzh_decode_blocks(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; struct lzh_br bre = ds->br; struct huffman *lt = &(ds->lt); struct huffman *pt = &(ds->pt); unsigned char *w_buff = ds->w_buff; unsigned char *lt_bitlen = lt->bitlen; unsigned char *pt_bitlen = pt->bitlen; int blocks_avail = ds->blocks_avail, c = 0; int 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 lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits; int state = ds->state; for (;;) { switch (state) { case ST_GET_LITERAL: for (;;) { if (blocks_avail == 0) { /* We have decoded all blocks. * Let's handle next blocks. */ ds->state = ST_RD_BLOCK; ds->br = bre; ds->blocks_avail = 0; ds->w_pos = w_pos; ds->copy_pos = 0; return (100); } /* lzh_br_read_ahead() always try to fill the * cache buffer up. In specific situation we * are close to the end of the data, the cache * buffer will not be full and thus we have to * determine if the cache buffer has some bits * as much as we need after lzh_br_read_ahead() * failed. */ if (!lzh_br_read_ahead(strm, &bre, lt_max_bits)) { if (!last) goto next_data; /* Remaining bits are less than * maximum bits(lt.max_bits) but maybe * it still remains as much as we need, * so we should try to use it with * dummy bits. */ c = lzh_decode_huffman(lt, lzh_br_bits_forced(&bre, lt_max_bits)); lzh_br_consume(&bre, lt_bitlen[c]); if (!lzh_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzh_decode_huffman(lt, lzh_br_bits(&bre, lt_max_bits)); lzh_br_consume(&bre, lt_bitlen[c]); } blocks_avail--; if (c > UCHAR_MAX) /* Current block is a match data. */ break; /* * 'c' is exactly a literal code. */ /* Save a decoded code to reference it * afterward. */ w_buff[w_pos] = c; if (++w_pos >= w_size) { w_pos = 0; lzh_emit_window(strm, w_size); goto next_data; } } /* 'c' is the length of a match pattern we have * already extracted, which has be stored in * window(ds->w_buff). */ copy_len = c - (UCHAR_MAX + 1) + MINMATCH; /* FALL THROUGH */ case ST_GET_POS_1: /* * Get a reference position. */ if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) { if (!last) { state = ST_GET_POS_1; ds->copy_len = copy_len; goto next_data; } copy_pos = lzh_decode_huffman(pt, lzh_br_bits_forced(&bre, pt_max_bits)); lzh_br_consume(&bre, pt_bitlen[copy_pos]); if (!lzh_br_has(&bre, 0)) goto failed;/* Over read. */ } else { copy_pos = lzh_decode_huffman(pt, lzh_br_bits(&bre, pt_max_bits)); lzh_br_consume(&bre, pt_bitlen[copy_pos]); } /* FALL THROUGH */ case ST_GET_POS_2: if (copy_pos > 1) { /* We need an additional adjustment number to * the position. */ int p = copy_pos - 1; if (!lzh_br_read_ahead(strm, &bre, p)) { if (last) goto failed;/* Truncated data.*/ state = ST_GET_POS_2; ds->copy_len = copy_len; ds->copy_pos = copy_pos; goto next_data; } copy_pos = (1 << p) + lzh_br_bits(&bre, p); lzh_br_consume(&bre, p); } /* The position is actually a distance from the last * code we had extracted and thus we have to convert * it to a position of the window. */ copy_pos = (w_pos - copy_pos - 1) & w_mask; /* FALL THROUGH */ case ST_COPY_DATA: /* * Copy `copy_len' bytes as extracted data from * the window into the output buffer. */ for (;;) { 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 ((copy_pos + l < w_pos) || (w_pos + l < copy_pos)) { /* No overlap. */ memcpy(w_buff + w_pos, w_buff + copy_pos, l); } else { const unsigned char *s; unsigned char *d; int li; d = w_buff + w_pos; s = w_buff + copy_pos; for (li = 0; li < l-1;) { d[li] = s[li];li++; d[li] = s[li];li++; } if (li < l) d[li] = s[li]; } w_pos += l; if (w_pos == w_size) { w_pos = 0; lzh_emit_window(strm, w_size); if (copy_len <= l) state = ST_GET_LITERAL; else { state = ST_COPY_DATA; ds->copy_len = copy_len - l; ds->copy_pos = (copy_pos + l) & w_mask; } goto next_data; } if (copy_len <= l) /* A copy of current pattern ended. */ break; copy_len -= l; copy_pos = (copy_pos + l) & w_mask; } state = ST_GET_LITERAL; break; } } failed: return (ds->error = ARCHIVE_FAILED); next_data: ds->br = bre; ds->blocks_avail = blocks_avail; ds->state = state; ds->w_pos = w_pos; return (ARCHIVE_OK); } static int lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) { int bits; if (hf->bitlen == NULL) { hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); } if (hf->tbl == NULL) { if (tbl_bits < HTBL_BITS) bits = tbl_bits; else bits = HTBL_BITS; hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); } if (hf->tree == NULL && tbl_bits > HTBL_BITS) { hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4); hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0])); if (hf->tree == NULL) return (ARCHIVE_FATAL); } hf->len_size = (int)len_size; hf->tbl_bits = tbl_bits; return (ARCHIVE_OK); } static void lzh_huffman_free(struct huffman *hf) { free(hf->bitlen); free(hf->tbl); free(hf->tree); } static const char bitlen_tbl[0x400] = { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 0 }; static int lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end) { struct lzh_dec *ds = strm->ds; struct lzh_br *br = &(ds->br); int c, i; for (i = start; i < end; ) { /* * bit pattern the number we need * 000 -> 0 * 001 -> 1 * 010 -> 2 * ... * 110 -> 6 * 1110 -> 7 * 11110 -> 8 * ... * 1111111111110 -> 16 */ if (!lzh_br_read_ahead(strm, br, 3)) return (i); if ((c = lzh_br_bits(br, 3)) == 7) { if (!lzh_br_read_ahead(strm, br, 13)) return (i); c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF]; if (c) lzh_br_consume(br, c - 3); else return (-1);/* Invalid data. */ } else lzh_br_consume(br, 3); ds->pt.bitlen[i++] = c; ds->pt.freq[c]++; } return (i); } static int lzh_make_fake_table(struct huffman *hf, uint16_t c) { if (c >= hf->len_size) return (0); hf->tbl[0] = c; hf->max_bits = 0; hf->shift_bits = 0; hf->bitlen[hf->tbl[0]] = 0; return (1); } /* * Make a huffman coding table. */ static int lzh_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 diffbits, 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 != 0x10000 || 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; } } if (maxbits > HTBL_BITS) { unsigned htbl_max; uint16_t *p; diffbits = maxbits - HTBL_BITS; for (i = 1; i <= HTBL_BITS; i++) { bitptn[i] >>= diffbits; weight[i] >>= diffbits; } htbl_max = bitptn[HTBL_BITS] + weight[HTBL_BITS] * hf->freq[HTBL_BITS]; p = &(hf->tbl[htbl_max]); while (p < &hf->tbl[1U<shift_bits = diffbits; /* * Make the table. */ tbl_size = 1 << HTBL_BITS; tbl = hf->tbl; bitlen = hf->bitlen; len_avail = hf->len_avail; hf->tree_used = 0; for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; uint16_t bit; int extlen; struct htree_t *ht; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; ptn = bitptn[len]; cnt = weight[len]; if (len <= HTBL_BITS) { /* Calculate next bit pattern */ if ((bitptn[len] = ptn + cnt) > tbl_size) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); if (cnt > 7) { uint16_t *pc; cnt -= 8; pc = &p[cnt]; pc[0] = (uint16_t)i; pc[1] = (uint16_t)i; pc[2] = (uint16_t)i; pc[3] = (uint16_t)i; pc[4] = (uint16_t)i; pc[5] = (uint16_t)i; pc[6] = (uint16_t)i; pc[7] = (uint16_t)i; if (cnt > 7) { cnt -= 8; memcpy(&p[cnt], pc, 8 * sizeof(uint16_t)); pc = &p[cnt]; while (cnt > 15) { cnt -= 16; memcpy(&p[cnt], pc, 16 * sizeof(uint16_t)); } } if (cnt) memcpy(p, pc, cnt * sizeof(uint16_t)); } else { while (cnt > 1) { p[--cnt] = (uint16_t)i; p[--cnt] = (uint16_t)i; } if (cnt) p[--cnt] = (uint16_t)i; } continue; } /* * A bit length is too big to be housed to a direct table, * so we use a tree model for its extra bits. */ bitptn[len] = ptn + cnt; bit = 1U << (diffbits -1); extlen = len - HTBL_BITS; p = &(tbl[ptn >> diffbits]); if (*p == 0) { *p = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { if (*p < len_avail || *p >= (len_avail + hf->tree_used)) return (0);/* Invalid */ ht = &(hf->tree[*p - len_avail]); } while (--extlen > 0) { if (ptn & bit) { if (ht->left < len_avail) { ht->left = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->left - len_avail]); } } else { if (ht->right < len_avail) { ht->right = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->right - len_avail]); } } bit >>= 1; } if (ptn & bit) { if (ht->left != 0) return (0);/* Invalid */ ht->left = (uint16_t)i; } else { if (ht->right != 0) return (0);/* Invalid */ ht->right = (uint16_t)i; } } return (1); } static int lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c) { struct htree_t *ht; int extlen; ht = hf->tree; extlen = hf->shift_bits; while (c >= hf->len_avail) { c -= hf->len_avail; if (extlen-- <= 0 || c >= hf->tree_used) return (0); if (rbits & (1U << extlen)) c = ht[c].left; else c = ht[c].right; } return (c); } static inline int lzh_decode_huffman(struct huffman *hf, unsigned rbits) { int c; /* * At first search an index table for a bit pattern. * If it fails, search a huffman tree for. */ c = hf->tbl[rbits >> hf->shift_bits]; if (c < hf->len_avail || hf->len_avail == 0) return (c); /* This bit pattern needs to be found out at a huffman tree. */ return (lzh_decode_huffman_tree(hf, rbits, c)); } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c index 33a804501693..8fa1ede7adff 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c @@ -1,2150 +1,2156 @@ /*- * 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 determined. */ 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 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 (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) { 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); if (alt != NULL) { 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((unsigned char)*s) && *s != '\t') { 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 && ( #if defined(_WIN32) && !defined(__CYGWIN__) /* * On Windows, attempting to open a file with an * invalid name result in EINVAL (Error 22) */ (errno != ENOENT && errno != EINVAL) #else errno != ENOENT #endif || 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) { + } +#ifdef HAVE_LSTAT + else if (lstat(path, st) == -1) +#else + else if (la_stat(path, st) == -1) +#endif + { 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); return (ARCHIVE_OK); } if (strcmp(key, "cksum") == 0) return (ARCHIVE_OK); break; 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; } break; case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); return (ARCHIVE_OK); } break; case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol(&val, 10)); return (ARCHIVE_OK); } if (strcmp(key, "gname") == 0) { *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); return (ARCHIVE_OK); } break; case 'i': if (strcmp(key, "inode") == 0) { archive_entry_set_ino(entry, mtree_atol(&val, 10)); return (ARCHIVE_OK); } break; case 'l': if (strcmp(key, "link") == 0) { parse_escapes(val, NULL); archive_entry_copy_symlink(entry, val); return (ARCHIVE_OK); } break; case 'm': 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') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic or non-octal mode \"%s\" unsupported", val); return (ARCHIVE_WARN); } *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, (mode_t)mtree_atol(&val, 8)); return (ARCHIVE_OK); } break; case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; archive_entry_set_nlink(entry, (unsigned int)mtree_atol(&val, 10)); return (ARCHIVE_OK); } break; 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) { return parse_digest(a, entry, val, ARCHIVE_ENTRY_DIGEST_RMD160); } break; case 's': 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) { return parse_digest(a, entry, val, ARCHIVE_ENTRY_DIGEST_SHA256); } if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) { return parse_digest(a, entry, val, ARCHIVE_ENTRY_DIGEST_SHA384); } if (strcmp(key, "sha512") == 0 || 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)); return (ARCHIVE_OK); } break; 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. */ return (ARCHIVE_OK); } 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); return (ARCHIVE_OK); } if (strcmp(key, "type") == 0) { switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { *parsed_kws |= MTREE_HAS_TYPE; archive_entry_set_filetype(entry, AE_IFBLK); return (ARCHIVE_OK); } break; case 'c': if (strcmp(val, "char") == 0) { *parsed_kws |= MTREE_HAS_TYPE; archive_entry_set_filetype(entry, AE_IFCHR); return (ARCHIVE_OK); } break; case 'd': if (strcmp(val, "dir") == 0) { *parsed_kws |= MTREE_HAS_TYPE; archive_entry_set_filetype(entry, AE_IFDIR); return (ARCHIVE_OK); } break; case 'f': if (strcmp(val, "fifo") == 0) { *parsed_kws |= MTREE_HAS_TYPE; archive_entry_set_filetype(entry, AE_IFIFO); return (ARCHIVE_OK); } if (strcmp(val, "file") == 0) { *parsed_kws |= MTREE_HAS_TYPE; archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_OK); } break; case 'l': if (strcmp(val, "link") == 0) { *parsed_kws |= MTREE_HAS_TYPE; archive_entry_set_filetype(entry, AE_IFLNK); return (ARCHIVE_OK); } break; default: break; } 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); } break; case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol(&val, 10)); return (ARCHIVE_OK); } if (strcmp(key, "uname") == 0) { *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); return (ARCHIVE_OK); } break; default: break; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); return (ARCHIVE_WARN); } 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; } } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_rar.c b/contrib/libarchive/libarchive/archive_read_support_format_rar.c index 793e8e985214..8f239da9b39d 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_rar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_rar.c @@ -1,3797 +1,3788 @@ /*- * 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 /* Virtual Machine Properties */ #define VM_MEMORY_SIZE 0x40000 #define VM_MEMORY_MASK (VM_MEMORY_SIZE - 1) #define PROGRAM_WORK_SIZE 0x3C000 #define PROGRAM_GLOBAL_SIZE 0x2000 #define PROGRAM_SYSTEM_GLOBAL_ADDRESS PROGRAM_WORK_SIZE #define PROGRAM_SYSTEM_GLOBAL_SIZE 0x40 #define PROGRAM_USER_GLOBAL_ADDRESS (PROGRAM_SYSTEM_GLOBAL_ADDRESS + PROGRAM_SYSTEM_GLOBAL_SIZE) #define PROGRAM_USER_GLOBAL_SIZE (PROGRAM_GLOBAL_SIZE - PROGRAM_SYSTEM_GLOBAL_SIZE) /* * 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_program_code { uint8_t *staticdata; uint32_t staticdatalen; uint8_t *globalbackup; uint32_t globalbackuplen; uint64_t fingerprint; uint32_t usagecount; uint32_t oldfilterlength; struct rar_program_code *next; }; struct rar_filter { struct rar_program_code *prog; uint32_t initialregisters[8]; uint8_t *globaldata; uint32_t globaldatalen; size_t blockstartpos; uint32_t blocklength; uint32_t filteredblockaddress; uint32_t filteredblocklength; struct rar_filter *next; }; struct memory_bit_reader { const uint8_t *bytes; size_t length; size_t offset; uint64_t bits; int available; int at_eof; }; struct rar_virtual_machine { uint32_t registers[8]; uint8_t memory[VM_MEMORY_SIZE + sizeof(uint32_t)]; }; struct rar_filters { struct rar_virtual_machine *vm; struct rar_program_code *progs; struct rar_filter *stack; int64_t filterstart; uint32_t lastfilternum; int64_t lastend; uint8_t *bytes; size_t bytes_ready; }; struct audio_state { int8_t weight[5]; int16_t delta[4]; int8_t lastdelta; int error[11]; int count; uint8_t lastbyte; }; 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; unsigned int lastlength; unsigned int lastoffset; unsigned int oldoffset[4]; unsigned int lastlowoffset; unsigned int numlowoffsetrepeats; char start_new_table; /* Filters */ struct rar_filters filters; /* 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 *, 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 int expand(struct archive_read *, int64_t *); static int copy_from_lzss_window_to_unp(struct archive_read *, const void **, int64_t, int); static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *); static int parse_filter(struct archive_read *, const uint8_t *, uint16_t, uint8_t); static int run_filters(struct archive_read *); static void clear_filters(struct rar_filters *); static struct rar_filter *create_filter(struct rar_program_code *, const uint8_t *, uint32_t, uint32_t[8], size_t, uint32_t); static void delete_filter(struct rar_filter *filter); static struct rar_program_code *compile_program(const uint8_t *, size_t); static void delete_program_code(struct rar_program_code *prog); static uint32_t membr_next_rarvm_number(struct memory_bit_reader *br); static inline uint32_t membr_bits(struct memory_bit_reader *br, int bits); static int membr_fill(struct memory_bit_reader *br, int bits); static int read_filter(struct archive_read *, int64_t *); static int rar_decode_byte(struct archive_read*, uint8_t *); static int execute_filter(struct archive_read*, struct rar_filter *, struct rar_virtual_machine *, size_t); static int copy_from_lzss_window(struct archive_read *, void *, int64_t, int); static inline void vm_write_32(struct rar_virtual_machine*, size_t, uint32_t); static inline uint32_t vm_read_32(struct rar_virtual_machine*, size_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)) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); #endif } __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; if (to_read > 32 * 1024) to_read = 32 * 1024; if ((h = __archive_read_ahead(a, to_read, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file"); return (ARCHIVE_FATAL); } p = h; crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read); __archive_read_consume(a, to_read); skip -= to_read; } if ((crc32_val & 0xffff) != crc32_expected) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); #endif } 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, 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); clear_filters(&rar->filters); 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)) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); #endif } /* 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; rar->filters.filterstart = INT64_MAX; /* 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) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_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) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif 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) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); return (ARCHIVE_FATAL); #endif } 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, size_t looper) { if (looper++ > MAX_COMPRESS_DEPTH) return (ARCHIVE_FATAL); struct rar *rar; int64_t start, end; 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->filters.bytes_ready > 0) { /* Flush unp_buffer first */ if (rar->unp_offset > 0) { *buff = rar->unp_buffer; *size = rar->unp_offset; rar->unp_offset = 0; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; } else { *buff = rar->filters.bytes; *size = rar->filters.bytes_ready; rar->offset += *size; *offset = rar->offset_outgoing; rar->offset_outgoing += *size; rar->filters.bytes_ready -= *size; rar->filters.bytes += *size; } goto ending_block; } 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) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); return (ARCHIVE_FATAL); #endif } 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_to_unp(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->filters.lastend == rar->filters.filterstart) { if (!run_filters(a)) return (ARCHIVE_FATAL); 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, 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; if (rar->filters.filterstart < end) { end = rar->filters.filterstart; } ret = expand(a, &end); if (ret != ARCHIVE_OK) return (ret); rar->bytes_uncopied = end - start; rar->filters.lastend = end; if (rar->filters.lastend != rar->filters.filterstart && 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_to_unp(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; ending_block: /* 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 int 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->filters.filterstart < *end) *end = rar->filters.filterstart; while (1) { if(lzss_position(&rar->lzss) >= *end) { return (ARCHIVE_OK); } if(rar->is_ppmd_block) { *end = lzss_position(&rar->lzss); return (ARCHIVE_OK); } if ((symbol = read_next_symbol(a, &rar->maincode)) < 0) return (ARCHIVE_FATAL); 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); *end = lzss_position(&rar->lzss); return (ARCHIVE_OK); } else { if (parse_codes(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); continue; } } else if(symbol==257) { if (!read_filter(a, end)) return (ARCHIVE_FATAL); continue; } 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; lzss_emit_match(rar, rar->lastoffset, rar->lastlength); } 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, void *buffer, int64_t startpos, int length) { int windowoffs, firstpart; struct rar *rar = (struct rar *)(a->format->data); windowoffs = lzss_offset_for_position(&rar->lzss, startpos); 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(buffer, &rar->lzss.window[windowoffs], firstpart); memcpy(buffer, &rar->lzss.window[0], length - firstpart); } else { memcpy(buffer, &rar->lzss.window[windowoffs], length); } return (ARCHIVE_OK); } static int copy_from_lzss_window_to_unp(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; } static int parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags) { struct rar *rar = (struct rar *)(a->format->data); struct rar_filters *filters = &rar->filters; struct memory_bit_reader br = { 0 }; struct rar_program_code *prog; struct rar_filter *filter, **nextfilter; uint32_t numprogs, num, blocklength, globaldatalen; uint8_t *globaldata; size_t blockstartpos; uint32_t registers[8] = { 0 }; uint32_t i; br.bytes = bytes; br.length = length; numprogs = 0; for (prog = filters->progs; prog; prog = prog->next) numprogs++; if ((flags & 0x80)) { num = membr_next_rarvm_number(&br); if (num == 0) { delete_filter(filters->stack); filters->stack = NULL; delete_program_code(filters->progs); filters->progs = NULL; } else num--; if (num > numprogs) { return 0; } filters->lastfilternum = num; } else num = filters->lastfilternum; prog = filters->progs; for (i = 0; i < num; i++) prog = prog->next; if (prog) prog->usagecount++; blockstartpos = membr_next_rarvm_number(&br) + (size_t)lzss_position(&rar->lzss); if ((flags & 0x40)) blockstartpos += 258; if ((flags & 0x20)) blocklength = membr_next_rarvm_number(&br); else blocklength = prog ? prog->oldfilterlength : 0; registers[3] = PROGRAM_SYSTEM_GLOBAL_ADDRESS; registers[4] = blocklength; registers[5] = prog ? prog->usagecount : 0; registers[7] = VM_MEMORY_SIZE; if ((flags & 0x10)) { uint8_t mask = (uint8_t)membr_bits(&br, 7); for (i = 0; i < 7; i++) if ((mask & (1 << i))) registers[i] = membr_next_rarvm_number(&br); } if (!prog) { uint32_t len = membr_next_rarvm_number(&br); uint8_t *bytecode; struct rar_program_code **next; if (len == 0 || len > 0x10000) return 0; bytecode = malloc(len); if (!bytecode) return 0; for (i = 0; i < len; i++) bytecode[i] = (uint8_t)membr_bits(&br, 8); prog = compile_program(bytecode, len); if (!prog) { free(bytecode); return 0; } free(bytecode); next = &filters->progs; while (*next) next = &(*next)->next; *next = prog; } prog->oldfilterlength = blocklength; globaldata = NULL; globaldatalen = 0; if ((flags & 0x08)) { globaldatalen = membr_next_rarvm_number(&br); if (globaldatalen > PROGRAM_USER_GLOBAL_SIZE) return 0; globaldata = malloc(globaldatalen + PROGRAM_SYSTEM_GLOBAL_SIZE); if (!globaldata) return 0; for (i = 0; i < globaldatalen; i++) globaldata[i + PROGRAM_SYSTEM_GLOBAL_SIZE] = (uint8_t)membr_bits(&br, 8); } if (br.at_eof) { free(globaldata); return 0; } filter = create_filter(prog, globaldata, globaldatalen, registers, blockstartpos, blocklength); free(globaldata); if (!filter) return 0; for (i = 0; i < 7; i++) archive_le32enc(&filter->globaldata[i * 4], registers[i]); archive_le32enc(&filter->globaldata[0x1C], blocklength); archive_le32enc(&filter->globaldata[0x20], 0); archive_le32enc(&filter->globaldata[0x2C], prog->usagecount); nextfilter = &filters->stack; while (*nextfilter) nextfilter = &(*nextfilter)->next; *nextfilter = filter; if (!filters->stack->next) filters->filterstart = blockstartpos; return 1; } static struct rar_filter * create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t globaldatalen, uint32_t registers[8], size_t startpos, uint32_t length) { struct rar_filter *filter; filter = calloc(1, sizeof(*filter)); if (!filter) return NULL; filter->prog = prog; filter->globaldatalen = globaldatalen > PROGRAM_SYSTEM_GLOBAL_SIZE ? globaldatalen : PROGRAM_SYSTEM_GLOBAL_SIZE; filter->globaldata = calloc(1, filter->globaldatalen); if (!filter->globaldata) return NULL; if (globaldata) memcpy(filter->globaldata, globaldata, globaldatalen); if (registers) memcpy(filter->initialregisters, registers, sizeof(filter->initialregisters)); filter->blockstartpos = startpos; filter->blocklength = length; return filter; } static int run_filters(struct archive_read *a) { struct rar *rar = (struct rar *)(a->format->data); struct rar_filters *filters = &rar->filters; struct rar_filter *filter = filters->stack; struct rar_filter *f; size_t start, end; int64_t tend; uint32_t lastfilteraddress; uint32_t lastfilterlength; int ret; if (filters == NULL || filter == NULL) return (0); start = filters->filterstart; end = start + filter->blocklength; filters->filterstart = INT64_MAX; tend = (int64_t)end; ret = expand(a, &tend); if (ret != ARCHIVE_OK) return 0; /* Check if filter stack was modified in expand() */ ret = ARCHIVE_FATAL; f = filters->stack; while (f) { if (f == filter) { ret = ARCHIVE_OK; break; } f = f->next; } if (ret != ARCHIVE_OK) return 0; if (tend < 0) return 0; end = (size_t)tend; if (end != start + filter->blocklength) return 0; if (!filters->vm) { filters->vm = calloc(1, sizeof(*filters->vm)); if (!filters->vm) return 0; } ret = copy_from_lzss_window(a, filters->vm->memory, start, filter->blocklength); if (ret != ARCHIVE_OK) return 0; if (!execute_filter(a, filter, filters->vm, rar->offset)) return 0; lastfilteraddress = filter->filteredblockaddress; lastfilterlength = filter->filteredblocklength; filters->stack = filter->next; filter->next = NULL; delete_filter(filter); while ((filter = filters->stack) != NULL && (int64_t)filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength) { memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength); if (!execute_filter(a, filter, filters->vm, rar->offset)) return 0; lastfilteraddress = filter->filteredblockaddress; lastfilterlength = filter->filteredblocklength; filters->stack = filter->next; filter->next = NULL; delete_filter(filter); } if (filters->stack) { if (filters->stack->blockstartpos < end) return 0; filters->filterstart = filters->stack->blockstartpos; } filters->lastend = end; filters->bytes = &filters->vm->memory[lastfilteraddress]; filters->bytes_ready = lastfilterlength; return 1; } static struct rar_program_code * compile_program(const uint8_t *bytes, size_t length) { struct memory_bit_reader br = { 0 }; struct rar_program_code *prog; // uint32_t instrcount = 0; uint8_t xor; size_t i; xor = 0; for (i = 1; i < length; i++) xor ^= bytes[i]; if (!length || xor != bytes[0]) return NULL; br.bytes = bytes; br.length = length; br.offset = 1; prog = calloc(1, sizeof(*prog)); if (!prog) return NULL; prog->fingerprint = crc32(0, bytes, length) | ((uint64_t)length << 32); if (membr_bits(&br, 1)) { prog->staticdatalen = membr_next_rarvm_number(&br) + 1; prog->staticdata = malloc(prog->staticdatalen); if (!prog->staticdata) { delete_program_code(prog); return NULL; } for (i = 0; i < prog->staticdatalen; i++) prog->staticdata[i] = (uint8_t)membr_bits(&br, 8); } return prog; } static void delete_filter(struct rar_filter *filter) { while (filter) { struct rar_filter *next = filter->next; free(filter->globaldata); free(filter); filter = next; } } static void clear_filters(struct rar_filters *filters) { delete_filter(filters->stack); delete_program_code(filters->progs); free(filters->vm); } static void delete_program_code(struct rar_program_code *prog) { while (prog) { struct rar_program_code *next = prog->next; free(prog->staticdata); free(prog->globalbackup); free(prog); prog = next; } } static uint32_t membr_next_rarvm_number(struct memory_bit_reader *br) { uint32_t val; switch (membr_bits(br, 2)) { case 0: return membr_bits(br, 4); case 1: val = membr_bits(br, 8); if (val >= 16) return val; return 0xFFFFFF00 | (val << 4) | membr_bits(br, 4); case 2: return membr_bits(br, 16); default: return membr_bits(br, 32); } } static inline uint32_t membr_bits(struct memory_bit_reader *br, int bits) { if (bits > br->available && (br->at_eof || !membr_fill(br, bits))) return 0; return (uint32_t)((br->bits >> (br->available -= bits)) & (((uint64_t)1 << bits) - 1)); } static int membr_fill(struct memory_bit_reader *br, int bits) { while (br->available < bits && br->offset < br->length) { br->bits = (br->bits << 8) | br->bytes[br->offset++]; br->available += 8; } if (bits > br->available) { br->at_eof = 1; return 0; } return 1; } static int read_filter(struct archive_read *a, int64_t *end) { struct rar *rar = (struct rar *)(a->format->data); uint8_t flags, val, *code; uint16_t length, i; if (!rar_decode_byte(a, &flags)) return 0; length = (flags & 0x07) + 1; if (length == 7) { if (!rar_decode_byte(a, &val)) return 0; length = val + 7; } else if (length == 8) { if (!rar_decode_byte(a, &val)) return 0; length = val << 8; if (!rar_decode_byte(a, &val)) return 0; length |= val; } code = malloc(length); if (!code) return 0; for (i = 0; i < length; i++) { if (!rar_decode_byte(a, &code[i])) { free(code); return 0; } } if (!parse_filter(a, code, length, flags)) { free(code); return 0; } free(code); if (rar->filters.filterstart < *end) *end = rar->filters.filterstart; return 1; } static int execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm) { uint32_t length = filter->initialregisters[4]; uint32_t numchannels = filter->initialregisters[0]; uint8_t *src, *dst; uint32_t i, idx; if (length > PROGRAM_WORK_SIZE / 2) return 0; src = &vm->memory[0]; dst = &vm->memory[length]; for (i = 0; i < numchannels; i++) { uint8_t lastbyte = 0; for (idx = i; idx < length; idx += numchannels) lastbyte = dst[idx] = lastbyte - *src++; } filter->filteredblockaddress = length; filter->filteredblocklength = length; return 1; } static int execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos, int e9also) { uint32_t length = filter->initialregisters[4]; uint32_t filesize = 0x1000000; uint32_t i; if (length > PROGRAM_WORK_SIZE || length < 4) return 0; for (i = 0; i <= length - 5; i++) { if (vm->memory[i] == 0xE8 || (e9also && vm->memory[i] == 0xE9)) { uint32_t currpos = (uint32_t)pos + i + 1; int32_t address = (int32_t)vm_read_32(vm, i + 1); if (address < 0 && currpos >= (uint32_t)-address) vm_write_32(vm, i + 1, address + filesize); else if (address >= 0 && (uint32_t)address < filesize) vm_write_32(vm, i + 1, address - currpos); i += 4; } } filter->filteredblockaddress = 0; filter->filteredblocklength = length; return 1; } static int execute_filter_rgb(struct rar_filter *filter, struct rar_virtual_machine *vm) { uint32_t stride = filter->initialregisters[0]; uint32_t byteoffset = filter->initialregisters[1]; uint32_t blocklength = filter->initialregisters[4]; uint8_t *src, *dst; uint32_t i, j; if (blocklength > PROGRAM_WORK_SIZE / 2 || stride > blocklength) return 0; src = &vm->memory[0]; dst = &vm->memory[blocklength]; for (i = 0; i < 3; i++) { uint8_t byte = 0; uint8_t *prev = dst + i - stride; for (j = i; j < blocklength; j += 3) { if (prev >= dst) { uint32_t delta1 = abs(prev[3] - prev[0]); uint32_t delta2 = abs(byte - prev[0]); uint32_t delta3 = abs(prev[3] - prev[0] + byte - prev[0]); if (delta1 > delta2 || delta1 > delta3) byte = delta2 <= delta3 ? prev[3] : prev[0]; } byte -= *src++; dst[j] = byte; prev += 3; } } for (i = byteoffset; i < blocklength - 2; i += 3) { dst[i] += dst[i + 1]; dst[i + 2] += dst[i + 1]; } filter->filteredblockaddress = blocklength; filter->filteredblocklength = blocklength; return 1; } static int execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm) { uint32_t length = filter->initialregisters[4]; uint32_t numchannels = filter->initialregisters[0]; uint8_t *src, *dst; uint32_t i, j; if (length > PROGRAM_WORK_SIZE / 2) return 0; src = &vm->memory[0]; dst = &vm->memory[length]; for (i = 0; i < numchannels; i++) { struct audio_state state; memset(&state, 0, sizeof(state)); for (j = i; j < length; j += numchannels) { int8_t delta = (int8_t)*src++; uint8_t predbyte, byte; int prederror; state.delta[2] = state.delta[1]; state.delta[1] = state.lastdelta - state.delta[0]; state.delta[0] = state.lastdelta; predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF; byte = (predbyte - delta) & 0xFF; prederror = delta << 3; state.error[0] += abs(prederror); state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]); state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]); state.error[5] += abs(prederror - state.delta[2]); state.error[6] += abs(prederror + state.delta[2]); state.lastdelta = (int8_t)(byte - state.lastbyte); dst[j] = state.lastbyte = byte; if (!(state.count++ & 0x1F)) { uint8_t k, idx = 0; for (k = 1; k < 7; k++) { if (state.error[k] < state.error[idx]) idx = k; } memset(state.error, 0, sizeof(state.error)); switch (idx) { case 1: if (state.weight[0] >= -16) state.weight[0]--; break; case 2: if (state.weight[0] < 16) state.weight[0]++; break; case 3: if (state.weight[1] >= -16) state.weight[1]--; break; case 4: if (state.weight[1] < 16) state.weight[1]++; break; case 5: if (state.weight[2] >= -16) state.weight[2]--; break; case 6: if (state.weight[2] < 16) state.weight[2]++; break; } } } } filter->filteredblockaddress = length; filter->filteredblocklength = length; return 1; } static int execute_filter(struct archive_read *a, struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos) { if (filter->prog->fingerprint == 0x1D0E06077D) return execute_filter_delta(filter, vm); if (filter->prog->fingerprint == 0x35AD576887) return execute_filter_e8(filter, vm, pos, 0); if (filter->prog->fingerprint == 0x393CD7E57E) return execute_filter_e8(filter, vm, pos, 1); if (filter->prog->fingerprint == 0x951C2C5DC8) return execute_filter_rgb(filter, vm); if (filter->prog->fingerprint == 0xD8BC85E701) return execute_filter_audio(filter, vm); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No support for RAR VM program filter"); return 0; } static int rar_decode_byte(struct archive_read *a, uint8_t *byte) { struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); if (!rar_br_read_ahead(a, br, 8)) return 0; *byte = (uint8_t)rar_br_bits(br, 8); rar_br_consume(br, 8); return 1; } static inline void vm_write_32(struct rar_virtual_machine* vm, size_t offset, uint32_t u32) { archive_le32enc(vm->memory + offset, u32); } static inline uint32_t vm_read_32(struct rar_virtual_machine* vm, size_t offset) { return archive_le32dec(vm->memory + offset); } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_warc.c b/contrib/libarchive/libarchive/archive_read_support_format_warc.c index 27329962d6d1..61ab29ea145d 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_warc.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_warc.c @@ -1,848 +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, 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 (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 +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif 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 */ diff --git a/contrib/libarchive/libarchive/archive_read_support_format_xar.c b/contrib/libarchive/libarchive/archive_read_support_format_xar.c index ec5b06edacd5..ec9cb1981f3d 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_xar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_xar.c @@ -1,3332 +1,3332 @@ /*- * Copyright (c) 2009 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_STDLIB_H #include #endif #if HAVE_LIBXML_XMLREADER_H #include #elif HAVE_BSDXML_H #include #elif HAVE_EXPAT_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_read_private.h" #if (!defined(HAVE_LIBXML_XMLREADER_H) && \ !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\ !defined(HAVE_ZLIB_H) || \ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) /* * xar needs several external libraries. * o libxml2 or expat --- XML parser * o openssl or MD5/SHA1 hash function * o zlib * o bzlib2 (option) * o liblzma (option) */ int archive_read_support_format_xar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_xar"); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Xar not supported on this platform"); return (ARCHIVE_WARN); } #else /* Support xar format */ /* #define DEBUG 1 */ /* #define DEBUG_PRINT_TOC 1 */ #if DEBUG_PRINT_TOC #define PRINT_TOC(d, outbytes) do { \ unsigned char *x = (unsigned char *)(uintptr_t)d; \ unsigned char c = x[outbytes-1]; \ x[outbytes - 1] = 0; \ fprintf(stderr, "%s", x); \ fprintf(stderr, "%c", c); \ x[outbytes - 1] = c; \ } while (0) #else #define PRINT_TOC(d, outbytes) #endif #define HEADER_MAGIC 0x78617221 #define HEADER_SIZE 28 #define HEADER_VERSION 1 #define CKSUM_NONE 0 #define CKSUM_SHA1 1 #define CKSUM_MD5 2 #define MD5_SIZE 16 #define SHA1_SIZE 20 #define MAX_SUM_SIZE 20 enum enctype { NONE, GZIP, BZIP2, LZMA, XZ, }; struct chksumval { int alg; size_t len; unsigned char val[MAX_SUM_SIZE]; }; struct chksumwork { int alg; #ifdef ARCHIVE_HAS_MD5 archive_md5_ctx md5ctx; #endif #ifdef ARCHIVE_HAS_SHA1 archive_sha1_ctx sha1ctx; #endif }; struct xattr { struct xattr *next; struct archive_string name; uint64_t id; uint64_t length; uint64_t offset; uint64_t size; enum enctype encoding; struct chksumval a_sum; struct chksumval e_sum; struct archive_string fstype; }; struct xar_file { struct xar_file *next; struct xar_file *hdnext; struct xar_file *parent; int subdirs; unsigned int has; #define HAS_DATA 0x00001 #define HAS_PATHNAME 0x00002 #define HAS_SYMLINK 0x00004 #define HAS_TIME 0x00008 #define HAS_UID 0x00010 #define HAS_GID 0x00020 #define HAS_MODE 0x00040 #define HAS_TYPE 0x00080 #define HAS_DEV 0x00100 #define HAS_DEVMAJOR 0x00200 #define HAS_DEVMINOR 0x00400 #define HAS_INO 0x00800 #define HAS_FFLAGS 0x01000 #define HAS_XATTR 0x02000 #define HAS_ACL 0x04000 #define HAS_CTIME 0x08000 #define HAS_MTIME 0x10000 #define HAS_ATIME 0x20000 uint64_t id; uint64_t length; uint64_t offset; uint64_t size; enum enctype encoding; struct chksumval a_sum; struct chksumval e_sum; struct archive_string pathname; struct archive_string symlink; time_t ctime; time_t mtime; time_t atime; struct archive_string uname; int64_t uid; struct archive_string gname; int64_t gid; mode_t mode; dev_t dev; dev_t devmajor; dev_t devminor; int64_t ino64; struct archive_string fflags_text; unsigned int link; unsigned int nlink; struct archive_string hardlink; struct xattr *xattr_list; }; struct hdlink { struct hdlink *next; unsigned int id; int cnt; struct xar_file *files; }; struct heap_queue { struct xar_file **files; int allocated; int used; }; enum xmlstatus { INIT, XAR, TOC, TOC_CREATION_TIME, TOC_CHECKSUM, TOC_CHECKSUM_OFFSET, TOC_CHECKSUM_SIZE, TOC_FILE, FILE_DATA, FILE_DATA_LENGTH, FILE_DATA_OFFSET, FILE_DATA_SIZE, FILE_DATA_ENCODING, FILE_DATA_A_CHECKSUM, FILE_DATA_E_CHECKSUM, FILE_DATA_CONTENT, FILE_EA, FILE_EA_LENGTH, FILE_EA_OFFSET, FILE_EA_SIZE, FILE_EA_ENCODING, FILE_EA_A_CHECKSUM, FILE_EA_E_CHECKSUM, FILE_EA_NAME, FILE_EA_FSTYPE, FILE_CTIME, FILE_MTIME, FILE_ATIME, FILE_GROUP, FILE_GID, FILE_USER, FILE_UID, FILE_MODE, FILE_DEVICE, FILE_DEVICE_MAJOR, FILE_DEVICE_MINOR, FILE_DEVICENO, FILE_INODE, FILE_LINK, FILE_TYPE, FILE_NAME, FILE_ACL, FILE_ACL_DEFAULT, FILE_ACL_ACCESS, FILE_ACL_APPLEEXTENDED, /* BSD file flags. */ FILE_FLAGS, FILE_FLAGS_USER_NODUMP, FILE_FLAGS_USER_IMMUTABLE, FILE_FLAGS_USER_APPEND, FILE_FLAGS_USER_OPAQUE, FILE_FLAGS_USER_NOUNLINK, FILE_FLAGS_SYS_ARCHIVED, FILE_FLAGS_SYS_IMMUTABLE, FILE_FLAGS_SYS_APPEND, FILE_FLAGS_SYS_NOUNLINK, FILE_FLAGS_SYS_SNAPSHOT, /* Linux file flags. */ FILE_EXT2, FILE_EXT2_SecureDeletion, FILE_EXT2_Undelete, FILE_EXT2_Compress, FILE_EXT2_Synchronous, FILE_EXT2_Immutable, FILE_EXT2_AppendOnly, FILE_EXT2_NoDump, FILE_EXT2_NoAtime, FILE_EXT2_CompDirty, FILE_EXT2_CompBlock, FILE_EXT2_NoCompBlock, FILE_EXT2_CompError, FILE_EXT2_BTree, FILE_EXT2_HashIndexed, FILE_EXT2_iMagic, FILE_EXT2_Journaled, FILE_EXT2_NoTail, FILE_EXT2_DirSync, FILE_EXT2_TopDir, FILE_EXT2_Reserved, UNKNOWN, }; struct unknown_tag { struct unknown_tag *next; struct archive_string name; }; struct xar { uint64_t offset; /* Current position in the file. */ int64_t total; uint64_t h_base; int end_of_file; #define OUTBUFF_SIZE (1024 * 64) unsigned char *outbuff; enum xmlstatus xmlsts; enum xmlstatus xmlsts_unknown; struct unknown_tag *unknowntags; int base64text; /* * TOC */ uint64_t toc_remaining; uint64_t toc_total; uint64_t toc_chksum_offset; uint64_t toc_chksum_size; /* * For Decoding data. */ enum enctype rd_encoding; z_stream stream; int stream_valid; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream bzstream; int bzstream_valid; #endif #if HAVE_LZMA_H && HAVE_LIBLZMA lzma_stream lzstream; int lzstream_valid; #endif /* * For Checksum data. */ struct chksumwork a_sumwrk; struct chksumwork e_sumwrk; struct xar_file *file; /* current reading file. */ struct xattr *xattr; /* current reading extended attribute. */ struct heap_queue file_queue; struct xar_file *hdlink_orgs; struct hdlink *hdlink_list; int entry_init; uint64_t entry_total; uint64_t entry_remaining; size_t entry_unconsumed; uint64_t entry_size; enum enctype entry_encoding; struct chksumval entry_a_sum; struct chksumval entry_e_sum; struct archive_string_conv *sconv; }; struct xmlattr { struct xmlattr *next; char *name; char *value; }; struct xmlattr_list { struct xmlattr *first; struct xmlattr **last; }; static int xar_bid(struct archive_read *, int); static int xar_read_header(struct archive_read *, struct archive_entry *); static int xar_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int xar_read_data_skip(struct archive_read *); static int xar_cleanup(struct archive_read *); static int move_reading_point(struct archive_read *, uint64_t); static int rd_contents_init(struct archive_read *, enum enctype, int, int); static int rd_contents(struct archive_read *, const void **, size_t *, size_t *, uint64_t); static uint64_t atol10(const char *, size_t); static int64_t atol8(const char *, size_t); static size_t atohex(unsigned char *, size_t, const char *, size_t); static time_t parse_time(const char *p, size_t n); static int heap_add_entry(struct archive_read *a, struct heap_queue *, struct xar_file *); static struct xar_file *heap_get_entry(struct heap_queue *); static int add_link(struct archive_read *, struct xar *, struct xar_file *); static void checksum_init(struct archive_read *, int, int); static void checksum_update(struct archive_read *, const void *, size_t, const void *, size_t); static int checksum_final(struct archive_read *, const void *, size_t, const void *, size_t); static void checksum_cleanup(struct archive_read *); static int decompression_init(struct archive_read *, enum enctype); static int decompress(struct archive_read *, const void **, size_t *, const void *, size_t *); static int decompression_cleanup(struct archive_read *); static void xmlattr_cleanup(struct xmlattr_list *); static int file_new(struct archive_read *, struct xar *, struct xmlattr_list *); static void file_free(struct xar_file *); static int xattr_new(struct archive_read *, struct xar *, struct xmlattr_list *); static void xattr_free(struct xattr *); static int getencoding(struct xmlattr_list *); static int getsumalgorithm(struct xmlattr_list *); static int unknowntag_start(struct archive_read *, struct xar *, const char *); static void unknowntag_end(struct xar *, const char *); static int xml_start(struct archive_read *, const char *, struct xmlattr_list *); static void xml_end(void *, const char *); static void xml_data(void *, const char *, int); static int xml_parse_file_flags(struct xar *, const char *); static int xml_parse_file_ext2(struct xar *, const char *); #if defined(HAVE_LIBXML_XMLREADER_H) static int xml2_xmlattr_setup(struct archive_read *, struct xmlattr_list *, xmlTextReaderPtr); static int xml2_read_cb(void *, char *, int); static int xml2_close_cb(void *); static void xml2_error_hdr(void *, const char *, xmlParserSeverities, xmlTextReaderLocatorPtr); static int xml2_read_toc(struct archive_read *); #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) struct expat_userData { int state; struct archive_read *archive; }; static int expat_xmlattr_setup(struct archive_read *, struct xmlattr_list *, const XML_Char **); static void expat_start_cb(void *, const XML_Char *, const XML_Char **); static void expat_end_cb(void *, const XML_Char *); static void expat_data_cb(void *, const XML_Char *, int); static int expat_read_toc(struct archive_read *); #endif int archive_read_support_format_xar(struct archive *_a) { struct xar *xar; struct archive_read *a = (struct archive_read *)_a; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_xar"); xar = (struct xar *)calloc(1, sizeof(*xar)); if (xar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate xar data"); return (ARCHIVE_FATAL); } /* initialize xar->file_queue */ xar->file_queue.allocated = 0; xar->file_queue.used = 0; xar->file_queue.files = NULL; r = __archive_read_register_format(a, xar, "xar", xar_bid, NULL, xar_read_header, xar_read_data, xar_read_data_skip, NULL, xar_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(xar); return (r); } static int xar_bid(struct archive_read *a, int best_bid) { const unsigned char *b; int bid; (void)best_bid; /* UNUSED */ b = __archive_read_ahead(a, HEADER_SIZE, NULL); if (b == NULL) return (-1); bid = 0; /* * Verify magic code */ if (archive_be32dec(b) != HEADER_MAGIC) return (0); bid += 32; /* * Verify header size */ if (archive_be16dec(b+4) != HEADER_SIZE) return (0); bid += 16; /* * Verify header version */ if (archive_be16dec(b+6) != HEADER_VERSION) return (0); bid += 16; /* * Verify type of checksum */ switch (archive_be32dec(b+24)) { case CKSUM_NONE: case CKSUM_SHA1: case CKSUM_MD5: bid += 32; break; default: return (0); } return (bid); } static int read_toc(struct archive_read *a) { struct xar *xar; struct xar_file *file; const unsigned char *b; uint64_t toc_compressed_size; uint64_t toc_uncompressed_size; uint32_t toc_chksum_alg; ssize_t bytes; int r; xar = (struct xar *)(a->format->data); /* * Read xar header. */ b = __archive_read_ahead(a, HEADER_SIZE, &bytes); if (bytes < 0) return ((int)bytes); if (bytes < HEADER_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated archive header"); return (ARCHIVE_FATAL); } if (archive_be32dec(b) != HEADER_MAGIC) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header magic"); return (ARCHIVE_FATAL); } if (archive_be16dec(b+6) != HEADER_VERSION) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported header version(%d)", archive_be16dec(b+6)); return (ARCHIVE_FATAL); } toc_compressed_size = archive_be64dec(b+8); xar->toc_remaining = toc_compressed_size; toc_uncompressed_size = archive_be64dec(b+16); toc_chksum_alg = archive_be32dec(b+24); __archive_read_consume(a, HEADER_SIZE); xar->offset += HEADER_SIZE; xar->toc_total = 0; /* * Read TOC(Table of Contents). */ /* Initialize reading contents. */ r = move_reading_point(a, HEADER_SIZE); if (r != ARCHIVE_OK) return (r); r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE); if (r != ARCHIVE_OK) return (r); #ifdef HAVE_LIBXML_XMLREADER_H r = xml2_read_toc(a); #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) r = expat_read_toc(a); #endif if (r != ARCHIVE_OK) return (r); /* Set 'The HEAP' base. */ xar->h_base = xar->offset; if (xar->toc_total != toc_uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "TOC uncompressed size error"); return (ARCHIVE_FATAL); } /* * Checksum TOC */ if (toc_chksum_alg != CKSUM_NONE) { r = move_reading_point(a, xar->toc_chksum_offset); if (r != ARCHIVE_OK) return (r); b = __archive_read_ahead(a, (size_t)xar->toc_chksum_size, &bytes); if (bytes < 0) return ((int)bytes); if ((uint64_t)bytes < xar->toc_chksum_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated archive file"); return (ARCHIVE_FATAL); } r = checksum_final(a, b, (size_t)xar->toc_chksum_size, NULL, 0); __archive_read_consume(a, xar->toc_chksum_size); xar->offset += xar->toc_chksum_size; if (r != ARCHIVE_OK) #ifndef DONT_FAIL_ON_CRC_ERROR return (ARCHIVE_FATAL); #endif } /* * Connect hardlinked files. */ for (file = xar->hdlink_orgs; file != NULL; file = file->hdnext) { struct hdlink **hdlink; for (hdlink = &(xar->hdlink_list); *hdlink != NULL; hdlink = &((*hdlink)->next)) { if ((*hdlink)->id == file->id) { struct hdlink *hltmp; struct xar_file *f2; int nlink = (*hdlink)->cnt + 1; file->nlink = nlink; for (f2 = (*hdlink)->files; f2 != NULL; f2 = f2->hdnext) { f2->nlink = nlink; archive_string_copy( &(f2->hardlink), &(file->pathname)); } /* Remove resolved files from hdlist_list. */ hltmp = *hdlink; *hdlink = hltmp->next; free(hltmp); break; } } } a->archive.archive_format = ARCHIVE_FORMAT_XAR; a->archive.archive_format_name = "xar"; return (ARCHIVE_OK); } static int xar_read_header(struct archive_read *a, struct archive_entry *entry) { struct xar *xar; struct xar_file *file; struct xattr *xattr; int r; xar = (struct xar *)(a->format->data); r = ARCHIVE_OK; if (xar->offset == 0) { /* Create a character conversion object. */ if (xar->sconv == NULL) { xar->sconv = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (xar->sconv == NULL) return (ARCHIVE_FATAL); } /* Read TOC. */ r = read_toc(a); if (r != ARCHIVE_OK) return (r); } for (;;) { file = xar->file = heap_get_entry(&(xar->file_queue)); if (file == NULL) { xar->end_of_file = 1; return (ARCHIVE_EOF); } if ((file->mode & AE_IFMT) != AE_IFDIR) break; if (file->has != (HAS_PATHNAME | HAS_TYPE)) break; /* * If a file type is a directory and it does not have * any metadata, do not export. */ file_free(file); } if (file->has & HAS_ATIME) { archive_entry_set_atime(entry, file->atime, 0); } if (file->has & HAS_CTIME) { archive_entry_set_ctime(entry, file->ctime, 0); } if (file->has & HAS_MTIME) { archive_entry_set_mtime(entry, file->mtime, 0); } archive_entry_set_gid(entry, file->gid); if (file->gname.length > 0 && archive_entry_copy_gname_l(entry, file->gname.s, archive_strlen(&(file->gname)), xar->sconv) != 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, "Gname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } archive_entry_set_uid(entry, file->uid); if (file->uname.length > 0 && archive_entry_copy_uname_l(entry, file->uname.s, archive_strlen(&(file->uname)), xar->sconv) != 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, "Uname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } archive_entry_set_mode(entry, file->mode); if (archive_entry_copy_pathname_l(entry, file->pathname.s, archive_strlen(&(file->pathname)), 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, "Pathname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } if (file->symlink.length > 0 && archive_entry_copy_symlink_l(entry, file->symlink.s, archive_strlen(&(file->symlink)), 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, "Linkname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } /* Set proper nlink. */ if ((file->mode & AE_IFMT) == AE_IFDIR) archive_entry_set_nlink(entry, file->subdirs + 2); else archive_entry_set_nlink(entry, file->nlink); archive_entry_set_size(entry, file->size); if (archive_strlen(&(file->hardlink)) > 0) archive_entry_set_hardlink(entry, file->hardlink.s); archive_entry_set_ino64(entry, file->ino64); if (file->has & HAS_DEV) archive_entry_set_dev(entry, file->dev); if (file->has & HAS_DEVMAJOR) archive_entry_set_devmajor(entry, file->devmajor); if (file->has & HAS_DEVMINOR) archive_entry_set_devminor(entry, file->devminor); if (archive_strlen(&(file->fflags_text)) > 0) archive_entry_copy_fflags_text(entry, file->fflags_text.s); xar->entry_init = 1; xar->entry_total = 0; xar->entry_remaining = file->length; xar->entry_size = file->size; xar->entry_encoding = file->encoding; xar->entry_a_sum = file->a_sum; xar->entry_e_sum = file->e_sum; /* * Read extended attributes. */ xattr = file->xattr_list; while (xattr != NULL) { const void *d; size_t outbytes = 0; size_t used = 0; r = move_reading_point(a, xattr->offset); if (r != ARCHIVE_OK) break; r = rd_contents_init(a, xattr->encoding, xattr->a_sum.alg, xattr->e_sum.alg); if (r != ARCHIVE_OK) break; d = NULL; r = rd_contents(a, &d, &outbytes, &used, xattr->length); if (r != ARCHIVE_OK) break; if (outbytes != xattr->size) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompressed size error"); r = ARCHIVE_FATAL; break; } r = checksum_final(a, xattr->a_sum.val, xattr->a_sum.len, xattr->e_sum.val, xattr->e_sum.len); if (r != ARCHIVE_OK) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Xattr checksum error"); r = ARCHIVE_WARN; break; #endif } if (xattr->name.s == NULL) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Xattr name error"); r = ARCHIVE_WARN; break; } archive_entry_xattr_add_entry(entry, xattr->name.s, d, outbytes); xattr = xattr->next; } if (r != ARCHIVE_OK) { file_free(file); return (r); } if (xar->entry_remaining > 0) /* Move reading point to the beginning of current * file contents. */ r = move_reading_point(a, file->offset); else r = ARCHIVE_OK; file_free(file); return (r); } static int xar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct xar *xar; size_t used = 0; int r; xar = (struct xar *)(a->format->data); if (xar->entry_unconsumed) { __archive_read_consume(a, xar->entry_unconsumed); xar->entry_unconsumed = 0; } if (xar->end_of_file || xar->entry_remaining <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } if (xar->entry_init) { r = rd_contents_init(a, xar->entry_encoding, xar->entry_a_sum.alg, xar->entry_e_sum.alg); if (r != ARCHIVE_OK) { xar->entry_remaining = 0; return (r); } xar->entry_init = 0; } *buff = NULL; r = rd_contents(a, buff, size, &used, xar->entry_remaining); if (r != ARCHIVE_OK) goto abort_read_data; *offset = xar->entry_total; xar->entry_total += *size; xar->total += *size; xar->offset += used; xar->entry_remaining -= used; xar->entry_unconsumed = used; if (xar->entry_remaining == 0) { if (xar->entry_total != xar->entry_size) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompressed size error"); r = ARCHIVE_FATAL; goto abort_read_data; } r = checksum_final(a, xar->entry_a_sum.val, xar->entry_a_sum.len, xar->entry_e_sum.val, xar->entry_e_sum.len); if (r != ARCHIVE_OK) goto abort_read_data; } return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = xar->total; return (r); } static int xar_read_data_skip(struct archive_read *a) { struct xar *xar; int64_t bytes_skipped; xar = (struct xar *)(a->format->data); if (xar->end_of_file) return (ARCHIVE_EOF); bytes_skipped = __archive_read_consume(a, xar->entry_remaining + xar->entry_unconsumed); if (bytes_skipped < 0) return (ARCHIVE_FATAL); xar->offset += bytes_skipped; xar->entry_unconsumed = 0; return (ARCHIVE_OK); } static int xar_cleanup(struct archive_read *a) { struct xar *xar; struct hdlink *hdlink; int i; int r; xar = (struct xar *)(a->format->data); checksum_cleanup(a); r = decompression_cleanup(a); hdlink = xar->hdlink_list; while (hdlink != NULL) { struct hdlink *next = hdlink->next; free(hdlink); hdlink = next; } for (i = 0; i < xar->file_queue.used; i++) file_free(xar->file_queue.files[i]); free(xar->file_queue.files); while (xar->unknowntags != NULL) { struct unknown_tag *tag; tag = xar->unknowntags; xar->unknowntags = tag->next; archive_string_free(&(tag->name)); free(tag); } free(xar->outbuff); free(xar); a->format->data = NULL; return (r); } static int move_reading_point(struct archive_read *a, uint64_t offset) { struct xar *xar; xar = (struct xar *)(a->format->data); if (xar->offset - xar->h_base != offset) { /* Seek forward to the start of file contents. */ int64_t step; step = offset - (xar->offset - xar->h_base); if (step > 0) { step = __archive_read_consume(a, step); if (step < 0) return ((int)step); xar->offset += step; } else { int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET); if (pos == ARCHIVE_FAILED) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Cannot seek."); return (ARCHIVE_FAILED); } xar->offset = pos; } } return (ARCHIVE_OK); } static int rd_contents_init(struct archive_read *a, enum enctype encoding, int a_sum_alg, int e_sum_alg) { int r; /* Init decompress library. */ if ((r = decompression_init(a, encoding)) != ARCHIVE_OK) return (r); /* Init checksum library. */ checksum_init(a, a_sum_alg, e_sum_alg); return (ARCHIVE_OK); } static int rd_contents(struct archive_read *a, const void **buff, size_t *size, size_t *used, uint64_t remaining) { const unsigned char *b; ssize_t bytes; /* Get whatever bytes are immediately available. */ b = __archive_read_ahead(a, 1, &bytes); if (bytes < 0) return ((int)bytes); if (bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated archive file"); return (ARCHIVE_FATAL); } if ((uint64_t)bytes > remaining) bytes = (ssize_t)remaining; /* * Decompress contents of file. */ *used = bytes; if (decompress(a, buff, size, b, used) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Update checksum of a compressed data and a extracted data. */ checksum_update(a, b, *used, *buff, *size); return (ARCHIVE_OK); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static uint64_t atol10(const char *p, size_t char_cnt) { uint64_t l; int digit; if (char_cnt == 0) return (0); l = 0; digit = *p - '0'; while (digit >= 0 && digit < 10 && char_cnt-- > 0) { l = (l * 10) + digit; digit = *++p - '0'; } return (l); } static int64_t atol8(const char *p, size_t char_cnt) { int64_t l; int digit; if (char_cnt == 0) return (0); l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else break; p++; l <<= 3; l |= digit; } return (l); } static size_t atohex(unsigned char *b, size_t bsize, const char *p, size_t psize) { size_t fbsize = bsize; while (bsize && psize > 1) { unsigned char x; if (p[0] >= 'a' && p[0] <= 'z') x = (p[0] - 'a' + 0x0a) << 4; else if (p[0] >= 'A' && p[0] <= 'Z') x = (p[0] - 'A' + 0x0a) << 4; else if (p[0] >= '0' && p[0] <= '9') x = (p[0] - '0') << 4; else return (-1); if (p[1] >= 'a' && p[1] <= 'z') x |= p[1] - 'a' + 0x0a; else if (p[1] >= 'A' && p[1] <= 'Z') x |= p[1] - 'A' + 0x0a; else if (p[1] >= '0' && p[1] <= '9') x |= p[1] - '0'; else return (-1); - + *b++ = x; bsize--; p += 2; psize -= 2; } return (fbsize - bsize); } static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif 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. */ mktime(t); /* 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 parse_time(const char *p, size_t n) { struct tm tm; time_t t = 0; int64_t data; memset(&tm, 0, sizeof(tm)); if (n != 20) return (t); data = atol10(p, 4); if (data < 1900) return (t); tm.tm_year = (int)data - 1900; p += 4; if (*p++ != '-') return (t); data = atol10(p, 2); if (data < 1 || data > 12) return (t); tm.tm_mon = (int)data -1; p += 2; if (*p++ != '-') return (t); data = atol10(p, 2); if (data < 1 || data > 31) return (t); tm.tm_mday = (int)data; p += 2; if (*p++ != 'T') return (t); data = atol10(p, 2); if (data < 0 || data > 23) return (t); tm.tm_hour = (int)data; p += 2; if (*p++ != ':') return (t); data = atol10(p, 2); if (data < 0 || data > 59) return (t); tm.tm_min = (int)data; p += 2; if (*p++ != ':') return (t); data = atol10(p, 2); if (data < 0 || data > 60) return (t); tm.tm_sec = (int)data; #if 0 p += 2; if (*p != 'Z') return (t); #endif t = time_from_tm(&tm); return (t); } static int heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct xar_file *file) { uint64_t file_id, parent_id; int hole, parent; /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { struct xar_file **new_pending_files; int new_size; if (heap->allocated < 1024) new_size = 1024; else new_size = heap->allocated * 2; /* Overflow might keep us from growing the list. */ if (new_size <= heap->allocated) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } new_pending_files = (struct xar_file **) malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (heap->allocated) { memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); free(heap->files); } heap->files = new_pending_files; heap->allocated = new_size; } file_id = file->id; /* * Start with hole at end, walk it up tree to find insertion point. */ hole = heap->used++; while (hole > 0) { parent = (hole - 1)/2; parent_id = heap->files[parent]->id; if (file_id >= parent_id) { heap->files[hole] = file; return (ARCHIVE_OK); } /* Move parent into hole <==> move hole up tree. */ heap->files[hole] = heap->files[parent]; hole = parent; } heap->files[0] = file; return (ARCHIVE_OK); } static struct xar_file * heap_get_entry(struct heap_queue *heap) { uint64_t a_id, b_id, c_id; int a, b, c; struct xar_file *r, *tmp; if (heap->used < 1) return (NULL); /* * The first file in the list is the earliest; we'll return this. */ r = heap->files[0]; /* * Move the last item in the heap to the root of the tree */ heap->files[0] = heap->files[--(heap->used)]; /* * Rebalance the heap. */ a = 0; /* Starting element and its heap key */ a_id = heap->files[a]->id; for (;;) { b = a + a + 1; /* First child */ if (b >= heap->used) return (r); b_id = heap->files[b]->id; c = b + 1; /* Use second child if it is smaller. */ if (c < heap->used) { c_id = heap->files[c]->id; if (c_id < b_id) { b = c; b_id = c_id; } } if (a_id <= b_id) return (r); tmp = heap->files[a]; heap->files[a] = heap->files[b]; heap->files[b] = tmp; a = b; } } static int add_link(struct archive_read *a, struct xar *xar, struct xar_file *file) { struct hdlink *hdlink; for (hdlink = xar->hdlink_list; hdlink != NULL; hdlink = hdlink->next) { if (hdlink->id == file->link) { file->hdnext = hdlink->files; hdlink->cnt++; hdlink->files = file; return (ARCHIVE_OK); } } hdlink = malloc(sizeof(*hdlink)); if (hdlink == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } file->hdnext = NULL; hdlink->id = file->link; hdlink->cnt = 1; hdlink->files = file; hdlink->next = xar->hdlink_list; xar->hdlink_list = hdlink; return (ARCHIVE_OK); } static void _checksum_init(struct chksumwork *sumwrk, int 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 int _checksum_final(struct chksumwork *sumwrk, const void *val, size_t len) { unsigned char sum[MAX_SUM_SIZE]; int r = ARCHIVE_OK; switch (sumwrk->alg) { case CKSUM_NONE: break; case CKSUM_SHA1: archive_sha1_final(&(sumwrk->sha1ctx), sum); if (len != SHA1_SIZE || memcmp(val, sum, SHA1_SIZE) != 0) r = ARCHIVE_FAILED; break; case CKSUM_MD5: archive_md5_final(&(sumwrk->md5ctx), sum); if (len != MD5_SIZE || memcmp(val, sum, MD5_SIZE) != 0) r = ARCHIVE_FAILED; break; } return (r); } static void checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg) { struct xar *xar; xar = (struct xar *)(a->format->data); _checksum_init(&(xar->a_sumwrk), a_sum_alg); _checksum_init(&(xar->e_sumwrk), e_sum_alg); } static void checksum_update(struct archive_read *a, const void *abuff, size_t asize, const void *ebuff, size_t esize) { struct xar *xar; xar = (struct xar *)(a->format->data); _checksum_update(&(xar->a_sumwrk), abuff, asize); _checksum_update(&(xar->e_sumwrk), ebuff, esize); } static int checksum_final(struct archive_read *a, const void *a_sum_val, size_t a_sum_len, const void *e_sum_val, size_t e_sum_len) { struct xar *xar; int r; xar = (struct xar *)(a->format->data); r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len); if (r == ARCHIVE_OK) r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len); if (r != ARCHIVE_OK) archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Sumcheck error"); return (r); } static int decompression_init(struct archive_read *a, enum enctype encoding) { struct xar *xar; const char *detail; int r; xar = (struct xar *)(a->format->data); xar->rd_encoding = encoding; switch (encoding) { case NONE: break; case GZIP: if (xar->stream_valid) r = inflateReset(&(xar->stream)); else r = inflateInit(&(xar->stream)); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't initialize zlib stream."); return (ARCHIVE_FATAL); } xar->stream_valid = 1; xar->stream.total_in = 0; xar->stream.total_out = 0; break; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case BZIP2: if (xar->bzstream_valid) { BZ2_bzDecompressEnd(&(xar->bzstream)); xar->bzstream_valid = 0; } r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0); if (r == BZ_MEM_ERROR) r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1); if (r != BZ_OK) { int err = ARCHIVE_ERRNO_MISC; detail = NULL; switch (r) { case BZ_PARAM_ERROR: detail = "invalid setup parameter"; break; case BZ_MEM_ERROR: err = ENOMEM; detail = "out of memory"; break; case BZ_CONFIG_ERROR: detail = "mis-compiled library"; break; } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", detail == NULL ? "??" : detail); xar->bzstream_valid = 0; return (ARCHIVE_FATAL); } xar->bzstream_valid = 1; xar->bzstream.total_in_lo32 = 0; xar->bzstream.total_in_hi32 = 0; xar->bzstream.total_out_lo32 = 0; xar->bzstream.total_out_hi32 = 0; break; #endif #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) #if LZMA_VERSION_MAJOR >= 5 /* Effectively disable the limiter. */ #define LZMA_MEMLIMIT UINT64_MAX #else /* NOTE: This needs to check memory size which running system has. */ #define LZMA_MEMLIMIT (1U << 30) #endif case XZ: case LZMA: if (xar->lzstream_valid) { lzma_end(&(xar->lzstream)); xar->lzstream_valid = 0; } if (xar->entry_encoding == XZ) r = lzma_stream_decoder(&(xar->lzstream), LZMA_MEMLIMIT,/* memlimit */ LZMA_CONCATENATED); else r = lzma_alone_decoder(&(xar->lzstream), LZMA_MEMLIMIT);/* memlimit */ if (r != LZMA_OK) { switch (r) { case LZMA_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Internal error initializing " "compression library: " "Cannot allocate memory"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: " "Invalid or unsupported options"); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "lzma library"); break; } return (ARCHIVE_FATAL); } xar->lzstream_valid = 1; xar->lzstream.total_in = 0; xar->lzstream.total_out = 0; break; #endif /* * Unsupported compression. */ default: #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) case LZMA: case XZ: #endif switch (xar->entry_encoding) { case BZIP2: detail = "bzip2"; break; case LZMA: detail = "lzma"; break; case XZ: detail = "xz"; break; default: detail = "??"; break; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s compression not supported on this platform", detail); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int decompress(struct archive_read *a, const void **buff, size_t *outbytes, const void *b, size_t *used) { struct xar *xar; void *outbuff; size_t avail_in, avail_out; int r; xar = (struct xar *)(a->format->data); avail_in = *used; outbuff = (void *)(uintptr_t)*buff; if (outbuff == NULL) { if (xar->outbuff == NULL) { xar->outbuff = malloc(OUTBUFF_SIZE); if (xar->outbuff == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory for out buffer"); return (ARCHIVE_FATAL); } } outbuff = xar->outbuff; *buff = outbuff; avail_out = OUTBUFF_SIZE; } else avail_out = *outbytes; switch (xar->rd_encoding) { case GZIP: xar->stream.next_in = (Bytef *)(uintptr_t)b; xar->stream.avail_in = avail_in; xar->stream.next_out = (unsigned char *)outbuff; xar->stream.avail_out = avail_out; r = inflate(&(xar->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, "File decompression failed (%d)", r); return (ARCHIVE_FATAL); } *used = avail_in - xar->stream.avail_in; *outbytes = avail_out - xar->stream.avail_out; break; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case BZIP2: xar->bzstream.next_in = (char *)(uintptr_t)b; xar->bzstream.avail_in = avail_in; xar->bzstream.next_out = (char *)outbuff; xar->bzstream.avail_out = avail_out; r = BZ2_bzDecompress(&(xar->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ switch (BZ2_bzDecompressEnd(&(xar->bzstream))) { case BZ_OK: break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FATAL); } xar->bzstream_valid = 0; /* FALLTHROUGH */ case BZ_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FATAL); } *used = avail_in - xar->bzstream.avail_in; *outbytes = avail_out - xar->bzstream.avail_out; break; #endif #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) case LZMA: case XZ: xar->lzstream.next_in = b; xar->lzstream.avail_in = avail_in; xar->lzstream.next_out = (unsigned char *)outbuff; xar->lzstream.avail_out = avail_out; r = lzma_code(&(xar->lzstream), LZMA_RUN); switch (r) { case LZMA_STREAM_END: /* Found end of stream. */ lzma_end(&(xar->lzstream)); xar->lzstream_valid = 0; /* FALLTHROUGH */ case LZMA_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "%s decompression failed(%d)", (xar->entry_encoding == XZ)?"xz":"lzma", r); return (ARCHIVE_FATAL); } *used = avail_in - xar->lzstream.avail_in; *outbytes = avail_out - xar->lzstream.avail_out; break; #endif #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) case LZMA: case XZ: #endif case NONE: default: if (outbuff == xar->outbuff) { *buff = b; *used = avail_in; *outbytes = avail_in; } else { if (avail_out > avail_in) avail_out = avail_in; memcpy(outbuff, b, avail_out); *used = avail_out; *outbytes = avail_out; } break; } return (ARCHIVE_OK); } static int decompression_cleanup(struct archive_read *a) { struct xar *xar; int r; xar = (struct xar *)(a->format->data); r = ARCHIVE_OK; if (xar->stream_valid) { if (inflateEnd(&(xar->stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } } #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (xar->bzstream_valid) { if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up bzip2 decompressor"); r = ARCHIVE_FATAL; } } #endif #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) if (xar->lzstream_valid) lzma_end(&(xar->lzstream)); #elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) if (xar->lzstream_valid) { if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up lzmadec decompressor"); r = ARCHIVE_FATAL; } } #endif return (r); } static void checksum_cleanup(struct archive_read *a) { struct xar *xar; xar = (struct xar *)(a->format->data); _checksum_final(&(xar->a_sumwrk), NULL, 0); _checksum_final(&(xar->e_sumwrk), NULL, 0); } static void xmlattr_cleanup(struct xmlattr_list *list) { struct xmlattr *attr, *next; attr = list->first; while (attr != NULL) { next = attr->next; free(attr->name); free(attr->value); free(attr); attr = next; } list->first = NULL; list->last = &(list->first); } static int file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) { struct xar_file *file; struct xmlattr *attr; file = calloc(1, sizeof(*file)); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } file->parent = xar->file; file->mode = 0777 | AE_IFREG; file->atime = 0; file->mtime = 0; xar->file = file; xar->xattr = NULL; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "id") == 0) file->id = atol10(attr->value, strlen(attr->value)); } file->nlink = 1; if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static void file_free(struct xar_file *file) { struct xattr *xattr; archive_string_free(&(file->pathname)); archive_string_free(&(file->symlink)); archive_string_free(&(file->uname)); archive_string_free(&(file->gname)); archive_string_free(&(file->hardlink)); xattr = file->xattr_list; while (xattr != NULL) { struct xattr *next; next = xattr->next; xattr_free(xattr); xattr = next; } free(file); } static int xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) { struct xattr *xattr, **nx; struct xmlattr *attr; xattr = calloc(1, sizeof(*xattr)); if (xattr == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } xar->xattr = xattr; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "id") == 0) xattr->id = atol10(attr->value, strlen(attr->value)); } /* Chain to xattr list. */ for (nx = &(xar->file->xattr_list); *nx != NULL; nx = &((*nx)->next)) { if (xattr->id < (*nx)->id) break; } xattr->next = *nx; *nx = xattr; return (ARCHIVE_OK); } static void xattr_free(struct xattr *xattr) { archive_string_free(&(xattr->name)); free(xattr); } static int getencoding(struct xmlattr_list *list) { struct xmlattr *attr; enum enctype encoding = NONE; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "style") == 0) { if (strcmp(attr->value, "application/octet-stream") == 0) encoding = NONE; else if (strcmp(attr->value, "application/x-gzip") == 0) encoding = GZIP; else if (strcmp(attr->value, "application/x-bzip2") == 0) encoding = BZIP2; else if (strcmp(attr->value, "application/x-lzma") == 0) encoding = LZMA; else if (strcmp(attr->value, "application/x-xz") == 0) encoding = XZ; } } return (encoding); } static int getsumalgorithm(struct xmlattr_list *list) { struct xmlattr *attr; int alg = CKSUM_NONE; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "style") == 0) { const char *v = attr->value; if ((v[0] == 'S' || v[0] == 's') && (v[1] == 'H' || v[1] == 'h') && (v[2] == 'A' || v[2] == 'a') && v[3] == '1' && v[4] == '\0') alg = CKSUM_SHA1; if ((v[0] == 'M' || v[0] == 'm') && (v[1] == 'D' || v[1] == 'd') && v[2] == '5' && v[3] == '\0') alg = CKSUM_MD5; } } return (alg); } static int unknowntag_start(struct archive_read *a, struct xar *xar, const char *name) { struct unknown_tag *tag; tag = malloc(sizeof(*tag)); if (tag == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } tag->next = xar->unknowntags; archive_string_init(&(tag->name)); archive_strcpy(&(tag->name), name); if (xar->unknowntags == NULL) { #if DEBUG fprintf(stderr, "UNKNOWNTAG_START:%s\n", name); #endif xar->xmlsts_unknown = xar->xmlsts; xar->xmlsts = UNKNOWN; } xar->unknowntags = tag; return (ARCHIVE_OK); } static void unknowntag_end(struct xar *xar, const char *name) { struct unknown_tag *tag; tag = xar->unknowntags; if (tag == NULL || name == NULL) return; if (strcmp(tag->name.s, name) == 0) { xar->unknowntags = tag->next; archive_string_free(&(tag->name)); free(tag); if (xar->unknowntags == NULL) { #if DEBUG fprintf(stderr, "UNKNOWNTAG_END:%s\n", name); #endif xar->xmlsts = xar->xmlsts_unknown; } } } static int xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) { struct xar *xar; struct xmlattr *attr; xar = (struct xar *)(a->format->data); #if DEBUG fprintf(stderr, "xml_sta:[%s]\n", name); for (attr = list->first; attr != NULL; attr = attr->next) fprintf(stderr, " attr:\"%s\"=\"%s\"\n", attr->name, attr->value); #endif xar->base64text = 0; switch (xar->xmlsts) { case INIT: if (strcmp(name, "xar") == 0) xar->xmlsts = XAR; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case XAR: if (strcmp(name, "toc") == 0) xar->xmlsts = TOC; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC: if (strcmp(name, "creation-time") == 0) xar->xmlsts = TOC_CREATION_TIME; else if (strcmp(name, "checksum") == 0) xar->xmlsts = TOC_CHECKSUM; else if (strcmp(name, "file") == 0) { if (file_new(a, xar, list) != ARCHIVE_OK) return (ARCHIVE_FATAL); xar->xmlsts = TOC_FILE; } else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC_CHECKSUM: if (strcmp(name, "offset") == 0) xar->xmlsts = TOC_CHECKSUM_OFFSET; else if (strcmp(name, "size") == 0) xar->xmlsts = TOC_CHECKSUM_SIZE; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC_FILE: if (strcmp(name, "file") == 0) { if (file_new(a, xar, list) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else if (strcmp(name, "data") == 0) xar->xmlsts = FILE_DATA; else if (strcmp(name, "ea") == 0) { if (xattr_new(a, xar, list) != ARCHIVE_OK) return (ARCHIVE_FATAL); xar->xmlsts = FILE_EA; } else if (strcmp(name, "ctime") == 0) xar->xmlsts = FILE_CTIME; else if (strcmp(name, "mtime") == 0) xar->xmlsts = FILE_MTIME; else if (strcmp(name, "atime") == 0) xar->xmlsts = FILE_ATIME; else if (strcmp(name, "group") == 0) xar->xmlsts = FILE_GROUP; else if (strcmp(name, "gid") == 0) xar->xmlsts = FILE_GID; else if (strcmp(name, "user") == 0) xar->xmlsts = FILE_USER; else if (strcmp(name, "uid") == 0) xar->xmlsts = FILE_UID; else if (strcmp(name, "mode") == 0) xar->xmlsts = FILE_MODE; else if (strcmp(name, "device") == 0) xar->xmlsts = FILE_DEVICE; else if (strcmp(name, "deviceno") == 0) xar->xmlsts = FILE_DEVICENO; else if (strcmp(name, "inode") == 0) xar->xmlsts = FILE_INODE; else if (strcmp(name, "link") == 0) xar->xmlsts = FILE_LINK; else if (strcmp(name, "type") == 0) { xar->xmlsts = FILE_TYPE; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "link") != 0) continue; if (strcmp(attr->value, "original") == 0) { xar->file->hdnext = xar->hdlink_orgs; xar->hdlink_orgs = xar->file; } else { xar->file->link = (unsigned)atol10(attr->value, strlen(attr->value)); if (xar->file->link > 0) if (add_link(a, xar, xar->file) != ARCHIVE_OK) { return (ARCHIVE_FATAL); }; } } } else if (strcmp(name, "name") == 0) { xar->xmlsts = FILE_NAME; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "enctype") == 0 && strcmp(attr->value, "base64") == 0) xar->base64text = 1; } } else if (strcmp(name, "acl") == 0) xar->xmlsts = FILE_ACL; else if (strcmp(name, "flags") == 0) xar->xmlsts = FILE_FLAGS; else if (strcmp(name, "ext2") == 0) xar->xmlsts = FILE_EXT2; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_DATA: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_DATA_LENGTH; else if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_DATA_OFFSET; else if (strcmp(name, "size") == 0) xar->xmlsts = FILE_DATA_SIZE; else if (strcmp(name, "encoding") == 0) { xar->xmlsts = FILE_DATA_ENCODING; xar->file->encoding = getencoding(list); } else if (strcmp(name, "archived-checksum") == 0) { xar->xmlsts = FILE_DATA_A_CHECKSUM; xar->file->a_sum.alg = getsumalgorithm(list); } else if (strcmp(name, "extracted-checksum") == 0) { xar->xmlsts = FILE_DATA_E_CHECKSUM; xar->file->e_sum.alg = getsumalgorithm(list); } else if (strcmp(name, "content") == 0) xar->xmlsts = FILE_DATA_CONTENT; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_DEVICE: if (strcmp(name, "major") == 0) xar->xmlsts = FILE_DEVICE_MAJOR; else if (strcmp(name, "minor") == 0) xar->xmlsts = FILE_DEVICE_MINOR; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_DATA_CONTENT: if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_EA: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_EA_LENGTH; else if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_EA_OFFSET; else if (strcmp(name, "size") == 0) xar->xmlsts = FILE_EA_SIZE; else if (strcmp(name, "encoding") == 0) { xar->xmlsts = FILE_EA_ENCODING; xar->xattr->encoding = getencoding(list); } else if (strcmp(name, "archived-checksum") == 0) xar->xmlsts = FILE_EA_A_CHECKSUM; else if (strcmp(name, "extracted-checksum") == 0) xar->xmlsts = FILE_EA_E_CHECKSUM; else if (strcmp(name, "name") == 0) xar->xmlsts = FILE_EA_NAME; else if (strcmp(name, "fstype") == 0) xar->xmlsts = FILE_EA_FSTYPE; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_ACL: if (strcmp(name, "appleextended") == 0) xar->xmlsts = FILE_ACL_APPLEEXTENDED; else if (strcmp(name, "default") == 0) xar->xmlsts = FILE_ACL_DEFAULT; else if (strcmp(name, "access") == 0) xar->xmlsts = FILE_ACL_ACCESS; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_FLAGS: if (!xml_parse_file_flags(xar, name)) if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_EXT2: if (!xml_parse_file_ext2(xar, name)) if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC_CREATION_TIME: case TOC_CHECKSUM_OFFSET: case TOC_CHECKSUM_SIZE: case FILE_DATA_LENGTH: case FILE_DATA_OFFSET: case FILE_DATA_SIZE: case FILE_DATA_ENCODING: case FILE_DATA_A_CHECKSUM: case FILE_DATA_E_CHECKSUM: case FILE_EA_LENGTH: case FILE_EA_OFFSET: case FILE_EA_SIZE: case FILE_EA_ENCODING: case FILE_EA_A_CHECKSUM: case FILE_EA_E_CHECKSUM: case FILE_EA_NAME: case FILE_EA_FSTYPE: case FILE_CTIME: case FILE_MTIME: case FILE_ATIME: case FILE_GROUP: case FILE_GID: case FILE_USER: case FILE_UID: case FILE_INODE: case FILE_DEVICE_MAJOR: case FILE_DEVICE_MINOR: case FILE_DEVICENO: case FILE_MODE: case FILE_TYPE: case FILE_LINK: case FILE_NAME: case FILE_ACL_DEFAULT: case FILE_ACL_ACCESS: case FILE_ACL_APPLEEXTENDED: case FILE_FLAGS_USER_NODUMP: case FILE_FLAGS_USER_IMMUTABLE: case FILE_FLAGS_USER_APPEND: case FILE_FLAGS_USER_OPAQUE: case FILE_FLAGS_USER_NOUNLINK: case FILE_FLAGS_SYS_ARCHIVED: case FILE_FLAGS_SYS_IMMUTABLE: case FILE_FLAGS_SYS_APPEND: case FILE_FLAGS_SYS_NOUNLINK: case FILE_FLAGS_SYS_SNAPSHOT: case FILE_EXT2_SecureDeletion: case FILE_EXT2_Undelete: case FILE_EXT2_Compress: case FILE_EXT2_Synchronous: case FILE_EXT2_Immutable: case FILE_EXT2_AppendOnly: case FILE_EXT2_NoDump: case FILE_EXT2_NoAtime: case FILE_EXT2_CompDirty: case FILE_EXT2_CompBlock: case FILE_EXT2_NoCompBlock: case FILE_EXT2_CompError: case FILE_EXT2_BTree: case FILE_EXT2_HashIndexed: case FILE_EXT2_iMagic: case FILE_EXT2_Journaled: case FILE_EXT2_NoTail: case FILE_EXT2_DirSync: case FILE_EXT2_TopDir: case FILE_EXT2_Reserved: case UNKNOWN: if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; } return (ARCHIVE_OK); } static void xml_end(void *userData, const char *name) { struct archive_read *a; struct xar *xar; a = (struct archive_read *)userData; xar = (struct xar *)(a->format->data); #if DEBUG fprintf(stderr, "xml_end:[%s]\n", name); #endif switch (xar->xmlsts) { case INIT: break; case XAR: if (strcmp(name, "xar") == 0) xar->xmlsts = INIT; break; case TOC: if (strcmp(name, "toc") == 0) xar->xmlsts = XAR; break; case TOC_CREATION_TIME: if (strcmp(name, "creation-time") == 0) xar->xmlsts = TOC; break; case TOC_CHECKSUM: if (strcmp(name, "checksum") == 0) xar->xmlsts = TOC; break; case TOC_CHECKSUM_OFFSET: if (strcmp(name, "offset") == 0) xar->xmlsts = TOC_CHECKSUM; break; case TOC_CHECKSUM_SIZE: if (strcmp(name, "size") == 0) xar->xmlsts = TOC_CHECKSUM; break; case TOC_FILE: if (strcmp(name, "file") == 0) { if (xar->file->parent != NULL && ((xar->file->mode & AE_IFMT) == AE_IFDIR)) xar->file->parent->subdirs++; xar->file = xar->file->parent; if (xar->file == NULL) xar->xmlsts = TOC; } break; case FILE_DATA: if (strcmp(name, "data") == 0) xar->xmlsts = TOC_FILE; break; case FILE_DATA_LENGTH: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_OFFSET: if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_SIZE: if (strcmp(name, "size") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_ENCODING: if (strcmp(name, "encoding") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_A_CHECKSUM: if (strcmp(name, "archived-checksum") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_E_CHECKSUM: if (strcmp(name, "extracted-checksum") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_CONTENT: if (strcmp(name, "content") == 0) xar->xmlsts = FILE_DATA; break; case FILE_EA: if (strcmp(name, "ea") == 0) { xar->xmlsts = TOC_FILE; xar->xattr = NULL; } break; case FILE_EA_LENGTH: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_OFFSET: if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_SIZE: if (strcmp(name, "size") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_ENCODING: if (strcmp(name, "encoding") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_A_CHECKSUM: if (strcmp(name, "archived-checksum") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_E_CHECKSUM: if (strcmp(name, "extracted-checksum") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_NAME: if (strcmp(name, "name") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_FSTYPE: if (strcmp(name, "fstype") == 0) xar->xmlsts = FILE_EA; break; case FILE_CTIME: if (strcmp(name, "ctime") == 0) xar->xmlsts = TOC_FILE; break; case FILE_MTIME: if (strcmp(name, "mtime") == 0) xar->xmlsts = TOC_FILE; break; case FILE_ATIME: if (strcmp(name, "atime") == 0) xar->xmlsts = TOC_FILE; break; case FILE_GROUP: if (strcmp(name, "group") == 0) xar->xmlsts = TOC_FILE; break; case FILE_GID: if (strcmp(name, "gid") == 0) xar->xmlsts = TOC_FILE; break; case FILE_USER: if (strcmp(name, "user") == 0) xar->xmlsts = TOC_FILE; break; case FILE_UID: if (strcmp(name, "uid") == 0) xar->xmlsts = TOC_FILE; break; case FILE_MODE: if (strcmp(name, "mode") == 0) xar->xmlsts = TOC_FILE; break; case FILE_DEVICE: if (strcmp(name, "device") == 0) xar->xmlsts = TOC_FILE; break; case FILE_DEVICE_MAJOR: if (strcmp(name, "major") == 0) xar->xmlsts = FILE_DEVICE; break; case FILE_DEVICE_MINOR: if (strcmp(name, "minor") == 0) xar->xmlsts = FILE_DEVICE; break; case FILE_DEVICENO: if (strcmp(name, "deviceno") == 0) xar->xmlsts = TOC_FILE; break; case FILE_INODE: if (strcmp(name, "inode") == 0) xar->xmlsts = TOC_FILE; break; case FILE_LINK: if (strcmp(name, "link") == 0) xar->xmlsts = TOC_FILE; break; case FILE_TYPE: if (strcmp(name, "type") == 0) xar->xmlsts = TOC_FILE; break; case FILE_NAME: if (strcmp(name, "name") == 0) xar->xmlsts = TOC_FILE; break; case FILE_ACL: if (strcmp(name, "acl") == 0) xar->xmlsts = TOC_FILE; break; case FILE_ACL_DEFAULT: if (strcmp(name, "default") == 0) xar->xmlsts = FILE_ACL; break; case FILE_ACL_ACCESS: if (strcmp(name, "access") == 0) xar->xmlsts = FILE_ACL; break; case FILE_ACL_APPLEEXTENDED: if (strcmp(name, "appleextended") == 0) xar->xmlsts = FILE_ACL; break; case FILE_FLAGS: if (strcmp(name, "flags") == 0) xar->xmlsts = TOC_FILE; break; case FILE_FLAGS_USER_NODUMP: if (strcmp(name, "UserNoDump") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_IMMUTABLE: if (strcmp(name, "UserImmutable") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_APPEND: if (strcmp(name, "UserAppend") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_OPAQUE: if (strcmp(name, "UserOpaque") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_NOUNLINK: if (strcmp(name, "UserNoUnlink") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_ARCHIVED: if (strcmp(name, "SystemArchived") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_IMMUTABLE: if (strcmp(name, "SystemImmutable") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_APPEND: if (strcmp(name, "SystemAppend") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_NOUNLINK: if (strcmp(name, "SystemNoUnlink") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_SNAPSHOT: if (strcmp(name, "SystemSnapshot") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_EXT2: if (strcmp(name, "ext2") == 0) xar->xmlsts = TOC_FILE; break; case FILE_EXT2_SecureDeletion: if (strcmp(name, "SecureDeletion") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Undelete: if (strcmp(name, "Undelete") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Compress: if (strcmp(name, "Compress") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Synchronous: if (strcmp(name, "Synchronous") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Immutable: if (strcmp(name, "Immutable") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_AppendOnly: if (strcmp(name, "AppendOnly") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoDump: if (strcmp(name, "NoDump") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoAtime: if (strcmp(name, "NoAtime") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_CompDirty: if (strcmp(name, "CompDirty") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_CompBlock: if (strcmp(name, "CompBlock") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoCompBlock: if (strcmp(name, "NoCompBlock") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_CompError: if (strcmp(name, "CompError") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_BTree: if (strcmp(name, "BTree") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_HashIndexed: if (strcmp(name, "HashIndexed") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_iMagic: if (strcmp(name, "iMagic") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Journaled: if (strcmp(name, "Journaled") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoTail: if (strcmp(name, "NoTail") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_DirSync: if (strcmp(name, "DirSync") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_TopDir: if (strcmp(name, "TopDir") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Reserved: if (strcmp(name, "Reserved") == 0) xar->xmlsts = FILE_EXT2; break; case UNKNOWN: unknowntag_end(xar, name); break; } } static const int base64[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, 62, -1, -1, -1, 63, /* 20 - 2F */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */ }; static void strappend_base64(struct xar *xar, struct archive_string *as, const char *s, size_t l) { unsigned char buff[256]; unsigned char *out; const unsigned char *b; size_t len; (void)xar; /* UNUSED */ len = 0; out = buff; b = (const unsigned char *)s; while (l > 0) { int n = 0; if (base64[b[0]] < 0 || base64[b[1]] < 0) break; n = base64[*b++] << 18; n |= base64[*b++] << 12; *out++ = n >> 16; len++; l -= 2; if (l > 0) { if (base64[*b] < 0) break; n |= base64[*b++] << 6; *out++ = (n >> 8) & 0xFF; len++; --l; } if (l > 0) { if (base64[*b] < 0) break; n |= base64[*b++]; *out++ = n & 0xFF; len++; --l; } if (len+3 >= sizeof(buff)) { archive_strncat(as, (const char *)buff, len); len = 0; out = buff; } } if (len > 0) archive_strncat(as, (const char *)buff, len); } static int is_string(const char *known, const char *data, size_t len) { if (strlen(known) != len) return -1; return memcmp(data, known, len); } static void xml_data(void *userData, const char *s, int len) { struct archive_read *a; struct xar *xar; a = (struct archive_read *)userData; xar = (struct xar *)(a->format->data); #if DEBUG { char buff[1024]; if (len > (int)(sizeof(buff)-1)) len = (int)(sizeof(buff)-1); strncpy(buff, s, len); buff[len] = 0; fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff); } #endif switch (xar->xmlsts) { case TOC_CHECKSUM_OFFSET: xar->toc_chksum_offset = atol10(s, len); break; case TOC_CHECKSUM_SIZE: xar->toc_chksum_size = atol10(s, len); break; default: break; } if (xar->file == NULL) return; switch (xar->xmlsts) { case FILE_NAME: if (xar->file->parent != NULL) { archive_string_concat(&(xar->file->pathname), &(xar->file->parent->pathname)); archive_strappend_char(&(xar->file->pathname), '/'); } xar->file->has |= HAS_PATHNAME; if (xar->base64text) { strappend_base64(xar, &(xar->file->pathname), s, len); } else archive_strncat(&(xar->file->pathname), s, len); break; case FILE_LINK: xar->file->has |= HAS_SYMLINK; archive_strncpy(&(xar->file->symlink), s, len); break; case FILE_TYPE: if (is_string("file", s, len) == 0 || is_string("hardlink", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFREG; if (is_string("directory", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFDIR; if (is_string("symlink", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFLNK; if (is_string("character special", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFCHR; if (is_string("block special", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFBLK; if (is_string("socket", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFSOCK; if (is_string("fifo", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFIFO; xar->file->has |= HAS_TYPE; break; case FILE_INODE: xar->file->has |= HAS_INO; xar->file->ino64 = atol10(s, len); break; case FILE_DEVICE_MAJOR: xar->file->has |= HAS_DEVMAJOR; xar->file->devmajor = (dev_t)atol10(s, len); break; case FILE_DEVICE_MINOR: xar->file->has |= HAS_DEVMINOR; xar->file->devminor = (dev_t)atol10(s, len); break; case FILE_DEVICENO: xar->file->has |= HAS_DEV; xar->file->dev = (dev_t)atol10(s, len); break; case FILE_MODE: xar->file->has |= HAS_MODE; xar->file->mode = (xar->file->mode & AE_IFMT) | ((mode_t)(atol8(s, len)) & ~AE_IFMT); break; case FILE_GROUP: xar->file->has |= HAS_GID; archive_strncpy(&(xar->file->gname), s, len); break; case FILE_GID: xar->file->has |= HAS_GID; xar->file->gid = atol10(s, len); break; case FILE_USER: xar->file->has |= HAS_UID; archive_strncpy(&(xar->file->uname), s, len); break; case FILE_UID: xar->file->has |= HAS_UID; xar->file->uid = atol10(s, len); break; case FILE_CTIME: xar->file->has |= HAS_TIME | HAS_CTIME; xar->file->ctime = parse_time(s, len); break; case FILE_MTIME: xar->file->has |= HAS_TIME | HAS_MTIME; xar->file->mtime = parse_time(s, len); break; case FILE_ATIME: xar->file->has |= HAS_TIME | HAS_ATIME; xar->file->atime = parse_time(s, len); break; case FILE_DATA_LENGTH: xar->file->has |= HAS_DATA; xar->file->length = atol10(s, len); break; case FILE_DATA_OFFSET: xar->file->has |= HAS_DATA; xar->file->offset = atol10(s, len); break; case FILE_DATA_SIZE: xar->file->has |= HAS_DATA; xar->file->size = atol10(s, len); break; case FILE_DATA_A_CHECKSUM: xar->file->a_sum.len = atohex(xar->file->a_sum.val, sizeof(xar->file->a_sum.val), s, len); break; case FILE_DATA_E_CHECKSUM: xar->file->e_sum.len = atohex(xar->file->e_sum.val, sizeof(xar->file->e_sum.val), s, len); break; case FILE_EA_LENGTH: xar->file->has |= HAS_XATTR; xar->xattr->length = atol10(s, len); break; case FILE_EA_OFFSET: xar->file->has |= HAS_XATTR; xar->xattr->offset = atol10(s, len); break; case FILE_EA_SIZE: xar->file->has |= HAS_XATTR; xar->xattr->size = atol10(s, len); break; case FILE_EA_A_CHECKSUM: xar->file->has |= HAS_XATTR; xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val, sizeof(xar->xattr->a_sum.val), s, len); break; case FILE_EA_E_CHECKSUM: xar->file->has |= HAS_XATTR; xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val, sizeof(xar->xattr->e_sum.val), s, len); break; case FILE_EA_NAME: xar->file->has |= HAS_XATTR; archive_strncpy(&(xar->xattr->name), s, len); break; case FILE_EA_FSTYPE: xar->file->has |= HAS_XATTR; archive_strncpy(&(xar->xattr->fstype), s, len); break; break; case FILE_ACL_DEFAULT: case FILE_ACL_ACCESS: case FILE_ACL_APPLEEXTENDED: xar->file->has |= HAS_ACL; /* TODO */ break; case INIT: case XAR: case TOC: case TOC_CREATION_TIME: case TOC_CHECKSUM: case TOC_CHECKSUM_OFFSET: case TOC_CHECKSUM_SIZE: case TOC_FILE: case FILE_DATA: case FILE_DATA_ENCODING: case FILE_DATA_CONTENT: case FILE_DEVICE: case FILE_EA: case FILE_EA_ENCODING: case FILE_ACL: case FILE_FLAGS: case FILE_FLAGS_USER_NODUMP: case FILE_FLAGS_USER_IMMUTABLE: case FILE_FLAGS_USER_APPEND: case FILE_FLAGS_USER_OPAQUE: case FILE_FLAGS_USER_NOUNLINK: case FILE_FLAGS_SYS_ARCHIVED: case FILE_FLAGS_SYS_IMMUTABLE: case FILE_FLAGS_SYS_APPEND: case FILE_FLAGS_SYS_NOUNLINK: case FILE_FLAGS_SYS_SNAPSHOT: case FILE_EXT2: case FILE_EXT2_SecureDeletion: case FILE_EXT2_Undelete: case FILE_EXT2_Compress: case FILE_EXT2_Synchronous: case FILE_EXT2_Immutable: case FILE_EXT2_AppendOnly: case FILE_EXT2_NoDump: case FILE_EXT2_NoAtime: case FILE_EXT2_CompDirty: case FILE_EXT2_CompBlock: case FILE_EXT2_NoCompBlock: case FILE_EXT2_CompError: case FILE_EXT2_BTree: case FILE_EXT2_HashIndexed: case FILE_EXT2_iMagic: case FILE_EXT2_Journaled: case FILE_EXT2_NoTail: case FILE_EXT2_DirSync: case FILE_EXT2_TopDir: case FILE_EXT2_Reserved: case UNKNOWN: break; } } /* * BSD file flags. */ static int xml_parse_file_flags(struct xar *xar, const char *name) { const char *flag = NULL; if (strcmp(name, "UserNoDump") == 0) { xar->xmlsts = FILE_FLAGS_USER_NODUMP; flag = "nodump"; } else if (strcmp(name, "UserImmutable") == 0) { xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE; flag = "uimmutable"; } else if (strcmp(name, "UserAppend") == 0) { xar->xmlsts = FILE_FLAGS_USER_APPEND; flag = "uappend"; } else if (strcmp(name, "UserOpaque") == 0) { xar->xmlsts = FILE_FLAGS_USER_OPAQUE; flag = "opaque"; } else if (strcmp(name, "UserNoUnlink") == 0) { xar->xmlsts = FILE_FLAGS_USER_NOUNLINK; flag = "nouunlink"; } else if (strcmp(name, "SystemArchived") == 0) { xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED; flag = "archived"; } else if (strcmp(name, "SystemImmutable") == 0) { xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE; flag = "simmutable"; } else if (strcmp(name, "SystemAppend") == 0) { xar->xmlsts = FILE_FLAGS_SYS_APPEND; flag = "sappend"; } else if (strcmp(name, "SystemNoUnlink") == 0) { xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK; flag = "nosunlink"; } else if (strcmp(name, "SystemSnapshot") == 0) { xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT; flag = "snapshot"; } if (flag == NULL) return (0); xar->file->has |= HAS_FFLAGS; if (archive_strlen(&(xar->file->fflags_text)) > 0) archive_strappend_char(&(xar->file->fflags_text), ','); archive_strcat(&(xar->file->fflags_text), flag); return (1); } /* * Linux file flags. */ static int xml_parse_file_ext2(struct xar *xar, const char *name) { const char *flag = NULL; if (strcmp(name, "SecureDeletion") == 0) { xar->xmlsts = FILE_EXT2_SecureDeletion; flag = "securedeletion"; } else if (strcmp(name, "Undelete") == 0) { xar->xmlsts = FILE_EXT2_Undelete; flag = "nouunlink"; } else if (strcmp(name, "Compress") == 0) { xar->xmlsts = FILE_EXT2_Compress; flag = "compress"; } else if (strcmp(name, "Synchronous") == 0) { xar->xmlsts = FILE_EXT2_Synchronous; flag = "sync"; } else if (strcmp(name, "Immutable") == 0) { xar->xmlsts = FILE_EXT2_Immutable; flag = "simmutable"; } else if (strcmp(name, "AppendOnly") == 0) { xar->xmlsts = FILE_EXT2_AppendOnly; flag = "sappend"; } else if (strcmp(name, "NoDump") == 0) { xar->xmlsts = FILE_EXT2_NoDump; flag = "nodump"; } else if (strcmp(name, "NoAtime") == 0) { xar->xmlsts = FILE_EXT2_NoAtime; flag = "noatime"; } else if (strcmp(name, "CompDirty") == 0) { xar->xmlsts = FILE_EXT2_CompDirty; flag = "compdirty"; } else if (strcmp(name, "CompBlock") == 0) { xar->xmlsts = FILE_EXT2_CompBlock; flag = "comprblk"; } else if (strcmp(name, "NoCompBlock") == 0) { xar->xmlsts = FILE_EXT2_NoCompBlock; flag = "nocomprblk"; } else if (strcmp(name, "CompError") == 0) { xar->xmlsts = FILE_EXT2_CompError; flag = "comperr"; } else if (strcmp(name, "BTree") == 0) { xar->xmlsts = FILE_EXT2_BTree; flag = "btree"; } else if (strcmp(name, "HashIndexed") == 0) { xar->xmlsts = FILE_EXT2_HashIndexed; flag = "hashidx"; } else if (strcmp(name, "iMagic") == 0) { xar->xmlsts = FILE_EXT2_iMagic; flag = "imagic"; } else if (strcmp(name, "Journaled") == 0) { xar->xmlsts = FILE_EXT2_Journaled; flag = "journal"; } else if (strcmp(name, "NoTail") == 0) { xar->xmlsts = FILE_EXT2_NoTail; flag = "notail"; } else if (strcmp(name, "DirSync") == 0) { xar->xmlsts = FILE_EXT2_DirSync; flag = "dirsync"; } else if (strcmp(name, "TopDir") == 0) { xar->xmlsts = FILE_EXT2_TopDir; flag = "topdir"; } else if (strcmp(name, "Reserved") == 0) { xar->xmlsts = FILE_EXT2_Reserved; flag = "reserved"; } if (flag == NULL) return (0); if (archive_strlen(&(xar->file->fflags_text)) > 0) archive_strappend_char(&(xar->file->fflags_text), ','); archive_strcat(&(xar->file->fflags_text), flag); return (1); } #ifdef HAVE_LIBXML_XMLREADER_H static int xml2_xmlattr_setup(struct archive_read *a, struct xmlattr_list *list, xmlTextReaderPtr reader) { struct xmlattr *attr; int r; list->first = NULL; list->last = &(list->first); r = xmlTextReaderMoveToFirstAttribute(reader); while (r == 1) { attr = malloc(sizeof*(attr)); if (attr == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } attr->name = strdup( (const char *)xmlTextReaderConstLocalName(reader)); if (attr->name == NULL) { free(attr); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } attr->value = strdup( (const char *)xmlTextReaderConstValue(reader)); if (attr->value == NULL) { free(attr->name); free(attr); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } attr->next = NULL; *list->last = attr; list->last = &(attr->next); r = xmlTextReaderMoveToNextAttribute(reader); } return (r); } static int xml2_read_cb(void *context, char *buffer, int len) { struct archive_read *a; struct xar *xar; const void *d; size_t outbytes; size_t used = 0; int r; a = (struct archive_read *)context; xar = (struct xar *)(a->format->data); if (xar->toc_remaining <= 0) return (0); d = buffer; outbytes = len; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); if (r != ARCHIVE_OK) return (r); __archive_read_consume(a, used); xar->toc_remaining -= used; xar->offset += used; xar->toc_total += outbytes; PRINT_TOC(buffer, len); return ((int)outbytes); } static int xml2_close_cb(void *context) { (void)context; /* UNUSED */ return (0); } static void xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator) { struct archive_read *a; (void)locator; /* UNUSED */ a = (struct archive_read *)arg; switch (severity) { case XML_PARSER_SEVERITY_VALIDITY_WARNING: case XML_PARSER_SEVERITY_WARNING: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "XML Parsing error: %s", msg); break; case XML_PARSER_SEVERITY_VALIDITY_ERROR: case XML_PARSER_SEVERITY_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "XML Parsing error: %s", msg); break; } } static int xml2_read_toc(struct archive_read *a) { xmlTextReaderPtr reader; struct xmlattr_list list; int r; reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0); if (reader == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory for xml parser"); return (ARCHIVE_FATAL); } xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a); while ((r = xmlTextReaderRead(reader)) == 1) { const char *name, *value; int type, empty; type = xmlTextReaderNodeType(reader); name = (const char *)xmlTextReaderConstLocalName(reader); switch (type) { case XML_READER_TYPE_ELEMENT: empty = xmlTextReaderIsEmptyElement(reader); r = xml2_xmlattr_setup(a, &list, reader); if (r == ARCHIVE_OK) r = xml_start(a, name, &list); xmlattr_cleanup(&list); if (r != ARCHIVE_OK) return (r); if (empty) xml_end(a, name); break; case XML_READER_TYPE_END_ELEMENT: xml_end(a, name); break; case XML_READER_TYPE_TEXT: value = (const char *)xmlTextReaderConstValue(reader); xml_data(a, value, strlen(value)); break; case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: default: break; } if (r < 0) break; } xmlFreeTextReader(reader); xmlCleanupParser(); return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL); } #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) static int expat_xmlattr_setup(struct archive_read *a, struct xmlattr_list *list, const XML_Char **atts) { struct xmlattr *attr; char *name, *value; list->first = NULL; list->last = &(list->first); if (atts == NULL) return (ARCHIVE_OK); while (atts[0] != NULL && atts[1] != NULL) { attr = malloc(sizeof*(attr)); name = strdup(atts[0]); value = strdup(atts[1]); if (attr == NULL || name == NULL || value == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); free(attr); free(name); free(value); return (ARCHIVE_FATAL); } attr->name = name; attr->value = value; attr->next = NULL; *list->last = attr; list->last = &(attr->next); atts += 2; } return (ARCHIVE_OK); } static void expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts) { struct expat_userData *ud = (struct expat_userData *)userData; struct archive_read *a = ud->archive; struct xmlattr_list list; int r; r = expat_xmlattr_setup(a, &list, atts); if (r == ARCHIVE_OK) r = xml_start(a, (const char *)name, &list); xmlattr_cleanup(&list); ud->state = r; } static void expat_end_cb(void *userData, const XML_Char *name) { struct expat_userData *ud = (struct expat_userData *)userData; xml_end(ud->archive, (const char *)name); } static void expat_data_cb(void *userData, const XML_Char *s, int len) { struct expat_userData *ud = (struct expat_userData *)userData; xml_data(ud->archive, s, len); } static int expat_read_toc(struct archive_read *a) { struct xar *xar; XML_Parser parser; struct expat_userData ud; ud.state = ARCHIVE_OK; ud.archive = a; xar = (struct xar *)(a->format->data); /* Initialize XML Parser library. */ parser = XML_ParserCreate(NULL); if (parser == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory for xml parser"); return (ARCHIVE_FATAL); } XML_SetUserData(parser, &ud); XML_SetElementHandler(parser, expat_start_cb, expat_end_cb); XML_SetCharacterDataHandler(parser, expat_data_cb); xar->xmlsts = INIT; while (xar->toc_remaining && ud.state == ARCHIVE_OK) { enum XML_Status xr; const void *d; size_t outbytes; size_t used; int r; d = NULL; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); if (r != ARCHIVE_OK) return (r); xar->toc_remaining -= used; xar->offset += used; xar->toc_total += outbytes; PRINT_TOC(d, outbytes); xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0); __archive_read_consume(a, used); if (xr == XML_STATUS_ERROR) { XML_ParserFree(parser); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "XML Parsing failed"); return (ARCHIVE_FATAL); } } XML_ParserFree(parser); return (ud.state); } #endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */ #endif /* Support xar format */ diff --git a/contrib/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libarchive/libarchive/archive_read_support_format_zip.c index 75df44ba93aa..0e05ea59ce67 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_zip.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_zip.c @@ -1,4270 +1,4270 @@ /*- * 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 #ifdef HAVE_ZSTD_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; int64_t central_directory_offset_adjusted; 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 #if HAVE_ZSTD_H && HAVE_LIBZSTD ZSTD_DStream *zstdstream; char zstdstream_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)) #ifdef HAVE_ZLIB_H static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); #endif #if HAVE_LZMA_H && HAVE_LIBLZMA static int zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); #endif /* 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) */ {93, "zstd"}, /* Zstandard (zstd) Compression */ {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; } /* * 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 == 0xffffffff) { 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); // 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; const void *uncompressed_buffer = NULL; switch (zip->entry->compression) { #if HAVE_ZLIB_H case 8: /* Deflate compression. */ zip->entry_bytes_remaining = zip_entry->compressed_size; status = zip_read_data_deflate(a, &uncompressed_buffer, &linkname_full_length, NULL); break; #endif #if HAVE_LZMA_H && HAVE_LIBLZMA case 14: /* ZIPx LZMA compression. */ /*(see zip file format specification, section 4.4.5)*/ zip->entry_bytes_remaining = zip_entry->compressed_size; status = zip_read_data_zipx_lzma_alone(a, &uncompressed_buffer, &linkname_full_length, NULL); break; #endif default: /* Unsupported compression. */ break; } if (status == ARCHIVE_OK) { p = uncompressed_buffer; } 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; } } else { 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; } 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_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_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(zip->entry_bytes_remaining < 9 || (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=%" 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: %" 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) + + 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 #if HAVE_ZSTD_H && HAVE_LIBZSTD static int zipx_zstd_init(struct archive_read *a, struct zip *zip) { size_t r; /* Deallocate already existing Zstd decompression context if it * exists. */ if(zip->zstdstream_valid) { ZSTD_freeDStream(zip->zstdstream); zip->zstdstream_valid = 0; } /* Allocate a new Zstd decompression context. */ zip->zstdstream = ZSTD_createDStream(); r = ZSTD_initDStream(zip->zstdstream); if (ZSTD_isError(r)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error initializing zstd decompressor: %s", ZSTD_getErrorName(r)); return ARCHIVE_FAILED; } /* Mark the zstdstream field to be released in cleanup phase. */ zip->zstdstream_valid = 1; /* (Re)allocate the buffer that will contain decompressed bytes. */ free(zip->uncompressed_buffer); zip->uncompressed_buffer_size = ZSTD_DStreamOutSize(); zip->uncompressed_buffer = (uint8_t*) malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for Zstd decompression"); return ARCHIVE_FATAL; } /* Initialization done. */ zip->decompress_init = 1; return ARCHIVE_OK; } static int zip_read_data_zipx_zstd(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; size_t ret; uint64_t total_out; ZSTD_outBuffer out; ZSTD_inBuffer in; (void) offset; /* UNUSED */ /* Initialize decompression context if we're here for the first time. */ if(!zip->decompress_init) { r = zipx_zstd_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 zstd file body"); return (ARCHIVE_FATAL); } in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); if(in_bytes < 1) { /* zstd 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 zstd file body"); return (ARCHIVE_FATAL); } /* Setup buffer boundaries */ in.src = compressed_buff; in.size = in_bytes; in.pos = 0; out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 }; /* Perform the decompression. */ ret = ZSTD_decompressStream(zip->zstdstream, &out, &in); if (ZSTD_isError(ret)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error during zstd decompression: %s", ZSTD_getErrorName(ret)); return (ARCHIVE_FATAL); } /* Check end of the stream. */ if (ret == 0) { if ((in.pos == in.size) && (out.pos < out.size)) { zip->end_of_entry = 1; ZSTD_freeDStream(zip->zstdstream); zip->zstdstream_valid = 0; } } /* Update the pointers so decompressor can continue decoding. */ to_consume = in.pos; __archive_read_consume(a, to_consume); total_out = out.pos; 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 #if HAVE_ZSTD_H && HAVE_LIBZSTD case 93: /* ZIPx Zstd compression. */ r = zip_read_data_zipx_zstd(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 #if HAVE_ZSTD_H && HAVE_LIBZSTD if (zip->zstdstream_valid) { ZSTD_freeDStream(zip->zstdstream); } #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) { uint16_t disk_num; uint32_t cd_size, cd_offset; disk_num = archive_le16dec(p + 4); cd_size = archive_le32dec(p + 12); cd_offset = archive_le32dec(p + 16); /* Sanity-check the EOCD we've found. */ /* This must be the first volume. */ if (disk_num != 0) return 0; /* Central directory must be on this volume. */ if (disk_num != 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 (cd_offset + cd_size > current_offset) return 0; /* Save the central directory location for later use. */ zip->central_directory_offset = cd_offset; zip->central_directory_offset_adjusted = current_offset - cd_size; /* 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); /* TODO: Needs scanning backwards to find the eocd64 instead of assuming */ zip->central_directory_offset_adjusted = zip->central_directory_offset; 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_adjusted, 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:*/ diff --git a/contrib/libarchive/libarchive/archive_string.c b/contrib/libarchive/libarchive/archive_string.c index 0fd918f47909..65999c001763 100644 --- a/contrib/libarchive/libarchive/archive_string.c +++ b/contrib/libarchive/libarchive/archive_string.c @@ -1,4240 +1,4244 @@ /*- * 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 - (int)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__) +# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GetOEMCP() CP_OEMCP +# endif + 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; /* 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; } /* 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 *a, struct archive_mstring *aes, const char **p, size_t *length, struct archive_string_conv *sc) { int ret = 0; #if defined(_WIN32) && !defined(__CYGWIN__) int r; /* * 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 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) { 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); } diff --git a/contrib/libarchive/libarchive/archive_util.c b/contrib/libarchive/libarchive/archive_util.c index 086e85d9df8e..83fcaac82003 100644 --- a/contrib/libarchive/libarchive/archive_util.c +++ b/contrib/libarchive/libarchive/archive_util.c @@ -1,655 +1,704 @@ /*- * 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__) +#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#elif defined(HAVE_WINCRYPT_H) #include #endif +#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; +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCRYPT_ALG_HANDLE hAlg = NULL; +#else + HCRYPTPROV hProv = (HCRYPTPROV)NULL; +#endif 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 defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, + NULL, 0))) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } +#else if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } +#endif for (;;) { wchar_t *p; HANDLE h; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif /* Generate a random file name through CryptGenRandom(). */ p = xp; +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p, + (DWORD)(ep - p)*sizeof(wchar_t), 0))) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } +#else if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } +#endif 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; } +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = attr & 0xFFFF; + createExParams.dwFileFlags = attr & 0xFFF00000; + h = CreateFile2(ws, + GENERIC_READ | GENERIC_WRITE | DELETE, + 0,/* Not share */ + CREATE_NEW, + &createExParams); +#else h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ NULL, CREATE_NEW,/* Create a new file only */ attr, NULL); +#endif 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 defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (hAlg != NULL) + BCryptCloseAlgorithmProvider(hAlg, 0); +#else if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); +#endif 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); } diff --git a/contrib/libarchive/libarchive/archive_write.c b/contrib/libarchive/libarchive/archive_write.c index 3d3ba41f523e..452836076dfa 100644 --- a/contrib/libarchive/libarchive/archive_write.c +++ b/contrib/libarchive/libarchive/archive_write.c @@ -1,828 +1,859 @@ /*- * 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 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 const struct archive_vtable archive_write_vtable = { .archive_close = _archive_write_close, .archive_filter_bytes = _archive_filter_bytes, .archive_filter_code = _archive_filter_code, .archive_filter_name = _archive_filter_name, .archive_filter_count = _archive_write_filter_count, .archive_free = _archive_write_free, .archive_write_header = _archive_write_header, .archive_write_finish_entry = _archive_write_finish_entry, .archive_write_data = _archive_write_data, }; /* * 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)); if (f == NULL) return (NULL); 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)); } +static int +__archive_write_filters_flush(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) { + if (f->flush != NULL && f->bytes_written > 0) { + ret1 = (f->flush)(f); + if (ret1 < ret) + ret = ret1; + if (ret1 < ARCHIVE_WARN) + f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; + } + } + return (ret); +} + 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; size_t to_write; char *p; 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; } p = state->buffer; to_write = block_length; while (to_write > 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, p, to_write); if (bytes_written <= 0) { ret = ARCHIVE_FATAL; break; } if ((size_t)bytes_written > to_write) { archive_set_error(&(a->archive), -1, "write overrun"); ret = ARCHIVE_FATAL; break; } p += bytes_written; to_write -= bytes_written; } } if (a->client_closer) (*a->client_closer)(&a->archive, a->client_data); free(state->buffer); free(state); /* 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_open2(struct archive *_a, void *client_data, archive_open_callback *opener, archive_write_callback *writer, 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); if (client_filter == NULL) return (ARCHIVE_FATAL); 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); } + /* Flush filters at boundary. */ + r2 = __archive_write_filters_flush(a); + 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; + /* 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; } diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_compress.c b/contrib/libarchive/libarchive/archive_write_add_filter_compress.c index d404fae7dba4..3ed269fce943 100644 --- a/contrib/libarchive/libarchive/archive_write_add_filter_compress.c +++ b/contrib/libarchive/libarchive/archive_write_add_filter_compress.c @@ -1,447 +1,447 @@ /*- * Copyright (c) 2008 Joerg Sonnenberger * 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. */ /*- * Copyright (c) 1985, 1986, 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Diomidis Spinellis and James A. Woods, derived from original * work by Spencer Thomas and Joseph Orost. * * 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. * 3. 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. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_compress.c 201111 2009-12-28 03:33:05Z kientzle $"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_write_private.h" #define HSIZE 69001 /* 95% occupancy */ #define HSHIFT 8 /* 8 - trunc(log2(HSIZE / 65536)) */ #define CHECK_GAP 10000 /* Ratio check interval. */ #define MAXCODE(bits) ((1 << (bits)) - 1) /* * the next two codes should not be changed lightly, as they must not * lie within the contiguous general code space. */ #define FIRST 257 /* First free entry. */ #define CLEAR 256 /* Table clear output code. */ struct private_data { int64_t in_count, out_count, checkpoint; int code_len; /* Number of bits/code. */ int cur_maxcode; /* Maximum code, given n_bits. */ int max_maxcode; /* Should NEVER generate this code. */ int hashtab [HSIZE]; unsigned short codetab [HSIZE]; int first_free; /* First unused entry. */ int compress_ratio; int cur_code, cur_fcode; int bit_offset; unsigned char bit_buf; unsigned char *compressed; size_t compressed_buffer_size; size_t compressed_offset; }; static int archive_compressor_compress_open(struct archive_write_filter *); static int archive_compressor_compress_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_compress_close(struct archive_write_filter *); static int archive_compressor_compress_free(struct archive_write_filter *); #if ARCHIVE_VERSION_NUMBER < 4000000 int archive_write_set_compression_compress(struct archive *a) { __archive_write_filters_free(a); return (archive_write_add_filter_compress(a)); } #endif /* * Add a compress filter to this write handle. */ int archive_write_add_filter_compress(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f = __archive_write_allocate_filter(_a); archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_compress"); f->open = &archive_compressor_compress_open; f->code = ARCHIVE_FILTER_COMPRESS; f->name = "compress"; return (ARCHIVE_OK); } /* * Setup callback. */ static int archive_compressor_compress_open(struct archive_write_filter *f) { struct private_data *state; size_t bs = 65536, bpb; f->code = ARCHIVE_FILTER_COMPRESS; f->name = "compress"; state = (struct private_data *)calloc(1, sizeof(*state)); if (state == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression"); return (ARCHIVE_FATAL); } 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; } state->compressed_buffer_size = bs; state->compressed = malloc(state->compressed_buffer_size); if (state->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); free(state); return (ARCHIVE_FATAL); } f->write = archive_compressor_compress_write; f->close = archive_compressor_compress_close; f->free = archive_compressor_compress_free; state->max_maxcode = 0x10000; /* Should NEVER generate this code. */ state->in_count = 0; /* Length of input. */ state->bit_buf = 0; state->bit_offset = 0; state->out_count = 3; /* Includes 3-byte header mojo. */ state->compress_ratio = 0; state->checkpoint = CHECK_GAP; state->code_len = 9; state->cur_maxcode = MAXCODE(state->code_len); state->first_free = FIRST; memset(state->hashtab, 0xff, sizeof(state->hashtab)); /* Prime output buffer with a gzip header. */ state->compressed[0] = 0x1f; /* Compress */ state->compressed[1] = 0x9d; state->compressed[2] = 0x90; /* Block mode, 16bit max */ state->compressed_offset = 3; f->data = state; return (0); } /*- * Output the given code. * Inputs: * code: A n_bits-bit integer. If == -1, then EOF. This assumes * that n_bits <= (long)wordsize - 1. * Outputs: * Outputs code to the file. * Assumptions: * Chars are 8 bits long. * Algorithm: * Maintain a BITS character long buffer (so that 8 codes will * fit in it exactly). Use the VAX insv instruction to insert each * code in turn. When the buffer fills up empty it and start over. */ static const unsigned char rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; static int output_byte(struct archive_write_filter *f, unsigned char c) { struct private_data *state = f->data; state->compressed[state->compressed_offset++] = c; ++state->out_count; if (state->compressed_buffer_size == state->compressed_offset) { int ret = __archive_write_filter(f->next_filter, state->compressed, state->compressed_buffer_size); if (ret != ARCHIVE_OK) return ARCHIVE_FATAL; state->compressed_offset = 0; } return ARCHIVE_OK; } static int output_code(struct archive_write_filter *f, int ocode) { struct private_data *state = f->data; int bits, ret, clear_flg, bit_offset; clear_flg = ocode == CLEAR; /* * Since ocode is always >= 8 bits, only need to mask the first * hunk on the left. */ bit_offset = state->bit_offset % 8; state->bit_buf |= (ocode << bit_offset) & 0xff; output_byte(f, state->bit_buf); bits = state->code_len - (8 - bit_offset); ocode >>= 8 - bit_offset; /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { output_byte(f, ocode & 0xff); ocode >>= 8; bits -= 8; } /* Last bits. */ state->bit_offset += state->code_len; state->bit_buf = ocode & rmask[bits]; if (state->bit_offset == state->code_len * 8) state->bit_offset = 0; /* * If the next entry is going to be too big for the ocode size, * then increase it, if possible. */ if (clear_flg || state->first_free > state->cur_maxcode) { /* * Write the whole buffer, because the input side won't * discover the size increase until after it has read it. */ if (state->bit_offset > 0) { while (state->bit_offset < state->code_len * 8) { ret = output_byte(f, state->bit_buf); if (ret != ARCHIVE_OK) return ret; state->bit_offset += 8; state->bit_buf = 0; } } state->bit_buf = 0; state->bit_offset = 0; if (clear_flg) { state->code_len = 9; state->cur_maxcode = MAXCODE(state->code_len); } else { state->code_len++; if (state->code_len == 16) state->cur_maxcode = state->max_maxcode; else state->cur_maxcode = MAXCODE(state->code_len); } } return (ARCHIVE_OK); } static int output_flush(struct archive_write_filter *f) { struct private_data *state = f->data; int ret; /* At EOF, write the rest of the buffer. */ if (state->bit_offset % 8) { state->code_len = (state->bit_offset % 8 + 7) / 8; ret = output_byte(f, state->bit_buf); if (ret != ARCHIVE_OK) return ret; } return (ARCHIVE_OK); } /* * Write data to the compressed stream. */ static int archive_compressor_compress_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *state = (struct private_data *)f->data; int i; int ratio; int c, disp, ret; const unsigned char *bp; if (length == 0) return ARCHIVE_OK; bp = buff; if (state->in_count == 0) { state->cur_code = *bp++; ++state->in_count; --length; } while (length--) { c = *bp++; state->in_count++; - state->cur_fcode = (c << 16) + state->cur_code; + state->cur_fcode = (c << 16) | state->cur_code; i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */ if (state->hashtab[i] == state->cur_fcode) { state->cur_code = state->codetab[i]; continue; } if (state->hashtab[i] < 0) /* Empty slot. */ goto nomatch; /* Secondary hash (after G. Knott). */ if (i == 0) disp = 1; else disp = HSIZE - i; probe: if ((i -= disp) < 0) i += HSIZE; if (state->hashtab[i] == state->cur_fcode) { state->cur_code = state->codetab[i]; continue; } if (state->hashtab[i] >= 0) goto probe; nomatch: ret = output_code(f, state->cur_code); if (ret != ARCHIVE_OK) return ret; state->cur_code = c; if (state->first_free < state->max_maxcode) { state->codetab[i] = state->first_free++; /* code -> hashtable */ state->hashtab[i] = state->cur_fcode; continue; } if (state->in_count < state->checkpoint) continue; state->checkpoint = state->in_count + CHECK_GAP; if (state->in_count <= 0x007fffff && state->out_count != 0) ratio = (int)(state->in_count * 256 / state->out_count); else if ((ratio = (int)(state->out_count / 256)) == 0) ratio = 0x7fffffff; else ratio = (int)(state->in_count / ratio); if (ratio > state->compress_ratio) state->compress_ratio = ratio; else { state->compress_ratio = 0; memset(state->hashtab, 0xff, sizeof(state->hashtab)); state->first_free = FIRST; ret = output_code(f, CLEAR); if (ret != ARCHIVE_OK) return ret; } } return (ARCHIVE_OK); } /* * Finish the compression... */ static int archive_compressor_compress_close(struct archive_write_filter *f) { struct private_data *state = (struct private_data *)f->data; int ret; ret = output_code(f, state->cur_code); if (ret != ARCHIVE_OK) return ret; ret = output_flush(f); if (ret != ARCHIVE_OK) return ret; /* Write the last block */ ret = __archive_write_filter(f->next_filter, state->compressed, state->compressed_offset); return (ret); } static int archive_compressor_compress_free(struct archive_write_filter *f) { struct private_data *state = (struct private_data *)f->data; free(state->compressed); free(state); return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c b/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c index e85b7669c50d..f32258b460eb 100644 --- a/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c +++ b/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c @@ -1,418 +1,467 @@ /*- * Copyright (c) 2017 Sean Purcell * 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_STDINT_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ZSTD_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" /* Don't compile this if we don't have zstd.h */ struct private_data { int compression_level; - int threads; + int threads; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + enum { + running, + finishing, + resetting, + } state; + int frame_per_file; + size_t min_frame_size; + size_t max_frame_size; + size_t cur_frame; + size_t cur_frame_in; + size_t cur_frame_out; + size_t total_in; ZSTD_CStream *cstream; - int64_t total_in; ZSTD_outBuffer out; #else struct archive_write_program_data *pdata; #endif }; /* If we don't have the library use default range values (zstdcli.c v1.4.0) */ #define CLEVEL_MIN -99 #define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */ #define CLEVEL_DEFAULT 3 #define CLEVEL_STD_MAX 19 /* without using --ultra */ #define CLEVEL_MAX 22 #define MINVER_NEGCLEVEL 10304 #define MINVER_MINCLEVEL 10306 static int archive_compressor_zstd_options(struct archive_write_filter *, const char *, const char *); static int archive_compressor_zstd_open(struct archive_write_filter *); static int archive_compressor_zstd_write(struct archive_write_filter *, const void *, size_t); +static int archive_compressor_zstd_flush(struct archive_write_filter *); static int archive_compressor_zstd_close(struct archive_write_filter *); static int archive_compressor_zstd_free(struct archive_write_filter *); #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR static int drive_compressor(struct archive_write_filter *, struct private_data *, int, const void *, size_t); #endif /* * Add a zstd compression filter to this write handle. */ int archive_write_add_filter_zstd(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f = __archive_write_allocate_filter(_a); struct private_data *data; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd"); data = calloc(1, sizeof(*data)); if (data == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } f->data = data; f->open = &archive_compressor_zstd_open; f->options = &archive_compressor_zstd_options; + f->flush = &archive_compressor_zstd_flush; f->close = &archive_compressor_zstd_close; f->free = &archive_compressor_zstd_free; f->code = ARCHIVE_FILTER_ZSTD; f->name = "zstd"; data->compression_level = CLEVEL_DEFAULT; data->threads = 0; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + data->frame_per_file = 0; + data->min_frame_size = 0; + data->max_frame_size = SIZE_MAX; + data->cur_frame_in = 0; + data->cur_frame_out = 0; data->cstream = ZSTD_createCStream(); if (data->cstream == NULL) { free(data); archive_set_error(&a->archive, ENOMEM, "Failed to allocate zstd compressor object"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); #else data->pdata = __archive_write_program_allocate("zstd"); if (data->pdata == NULL) { free(data); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Using external zstd program"); return (ARCHIVE_WARN); #endif } static int archive_compressor_zstd_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR ZSTD_freeCStream(data->cstream); free(data->out.dst); #else __archive_write_program_free(data->pdata); #endif free(data); f->data = NULL; return (ARCHIVE_OK); } -static int string_is_numeric (const char* value) +static int string_to_number(const char *string, intmax_t *numberp) { - size_t len = strlen(value); - size_t i; - - if (len == 0) { - return (ARCHIVE_WARN); - } - else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) { - return (ARCHIVE_WARN); - } - else if (!(value[0] >= '0' && value[0] <= '9') && - value[0] != '-' && value[0] != '+') { - return (ARCHIVE_WARN); - } - - for (i = 1; i < len; i++) { - if (!(value[i] >= '0' && value[i] <= '9')) { - return (ARCHIVE_WARN); - } - } - - return (ARCHIVE_OK); + char *end; + + if (string == NULL || *string == '\0') + return (ARCHIVE_WARN); + *numberp = strtoimax(string, &end, 10); + if (end == string || *end != '\0' || errno == EOVERFLOW) { + *numberp = 0; + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); } /* * Set write options. */ static int archive_compressor_zstd_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) { - int level = atoi(value); + intmax_t level; + if (string_to_number(value, &level) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } /* If we don't have the library, hard-code the max level */ int minimum = CLEVEL_MIN; int maximum = CLEVEL_MAX; - if (string_is_numeric(value) != ARCHIVE_OK) { - return (ARCHIVE_WARN); - } #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR maximum = ZSTD_maxCLevel(); #if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) { minimum = ZSTD_minCLevel(); } else #endif if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) { minimum = CLEVEL_STD_MIN; } #endif if (level < minimum || level > maximum) { return (ARCHIVE_WARN); } data->compression_level = level; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { - int threads = atoi(value); - if (string_is_numeric(value) != ARCHIVE_OK) { + intmax_t threads; + if (string_to_number(value, &threads) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - - int minimum = 0; - - if (threads < minimum) { + if (threads < 0) { return (ARCHIVE_WARN); } - data->threads = threads; return (ARCHIVE_OK); +#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + } else if (strcmp(key, "frame-per-file") == 0) { + data->frame_per_file = 1; + return (ARCHIVE_OK); + } else if (strcmp(key, "min-frame-size") == 0) { + intmax_t min_frame_size; + if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } + if (min_frame_size < 0) { + return (ARCHIVE_WARN); + } + data->min_frame_size = min_frame_size; + return (ARCHIVE_OK); + } else if (strcmp(key, "max-frame-size") == 0) { + intmax_t max_frame_size; + if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } + if (max_frame_size < 1024) { + return (ARCHIVE_WARN); + } + data->max_frame_size = max_frame_size; + return (ARCHIVE_OK); +#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); } #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR /* * Setup callback. */ static int archive_compressor_zstd_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; if (data->out.dst == NULL) { size_t bs = ZSTD_CStreamOutSize(), 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->out.size = bs; data->out.pos = 0; data->out.dst = (unsigned char *)malloc(data->out.size); if (data->out.dst == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } f->write = archive_compressor_zstd_write; if (ZSTD_isError(ZSTD_initCStream(data->cstream, data->compression_level))) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing zstd compressor object"); return (ARCHIVE_FATAL); } ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads); return (ARCHIVE_OK); } /* * Write data to the compressed stream. */ static int archive_compressor_zstd_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; + return (drive_compressor(f, data, 0, buff, length)); +} - if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK) - return (ret); +/* + * Flush the compressed stream. + */ +static int +archive_compressor_zstd_flush(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; - return (ARCHIVE_OK); + if (data->frame_per_file && data->state == running && + data->cur_frame_out > data->min_frame_size) + data->state = finishing; + return (drive_compressor(f, data, 1, NULL, 0)); } /* * Finish the compression... */ static int archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - /* Finish zstd frame */ - return drive_compressor(f, data, 1, NULL, 0); + if (data->state == running) + data->state = finishing; + return (drive_compressor(f, data, 1, NULL, 0)); } /* * 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, const void *src, size_t length) + struct private_data *data, int flush, const void *src, size_t length) { - ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 }; + ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 }; + size_t ipos, opos, zstdret = 0; + int ret; for (;;) { - if (data->out.pos == data->out.size) { - const int ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.size); + ipos = in.pos; + opos = data->out.pos; + switch (data->state) { + case running: + if (in.pos == in.size) + return (ARCHIVE_OK); + zstdret = ZSTD_compressStream(data->cstream, + &data->out, &in); + if (ZSTD_isError(zstdret)) + goto zstd_fatal; + break; + case finishing: + zstdret = ZSTD_endStream(data->cstream, &data->out); + if (ZSTD_isError(zstdret)) + goto zstd_fatal; + if (zstdret == 0) + data->state = resetting; + break; + case resetting: + ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only); + data->cur_frame++; + data->cur_frame_in = 0; + data->cur_frame_out = 0; + data->state = running; + break; + } + data->total_in += in.pos - ipos; + data->cur_frame_in += in.pos - ipos; + data->cur_frame_out += data->out.pos - opos; + if (data->state == running && + data->cur_frame_in >= data->max_frame_size) { + data->state = finishing; + } + if (data->out.pos == data->out.size || + (flush && data->out.pos > 0)) { + ret = __archive_write_filter(f->next_filter, + data->out.dst, data->out.pos); if (ret != ARCHIVE_OK) - return (ARCHIVE_FATAL); + goto fatal; data->out.pos = 0; } - - /* If there's nothing to do, we're done. */ - if (!finishing && in.pos == in.size) - return (ARCHIVE_OK); - - { - const size_t zstdret = !finishing ? - ZSTD_compressStream(data->cstream, &data->out, &in) - : ZSTD_endStream(data->cstream, &data->out); - - if (ZSTD_isError(zstdret)) { - archive_set_error(f->archive, - ARCHIVE_ERRNO_MISC, - "Zstd compression failed: %s", - ZSTD_getErrorName(zstdret)); - return (ARCHIVE_FATAL); - } - - /* If we're finishing, 0 means nothing left to flush */ - if (finishing && zstdret == 0) { - const int ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.pos); - return (ret); - } - } } +zstd_fatal: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Zstd compression failed: %s", + ZSTD_getErrorName(zstdret)); +fatal: + return (ARCHIVE_FATAL); } #else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */ static int archive_compressor_zstd_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; struct archive_string as; int r; archive_string_init(&as); /* --no-check matches library default */ archive_strcpy(&as, "zstd --no-check"); if (data->compression_level < CLEVEL_STD_MIN) { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " --fast=%d", -data->compression_level); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " --fast=%d", -data->compression_level); } else { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " -%d", data->compression_level); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " -%d", data->compression_level); } if (data->compression_level > CLEVEL_STD_MAX) { archive_strcat(&as, " --ultra"); } if (data->threads != 0) { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " --threads=%d", data->threads); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " --threads=%d", data->threads); } f->write = archive_compressor_zstd_write; r = __archive_write_program_open(f, data->pdata, as.s); archive_string_free(&as); return (r); } static int archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_write(f, data->pdata, buff, length); } +static int +archive_compressor_zstd_flush(struct archive_write_filter *f) +{ + (void)f; /* UNUSED */ + + return (ARCHIVE_OK); +} + static int archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_close(f, data->pdata); } #endif /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */ diff --git a/contrib/libarchive/libarchive/archive_write_disk_posix.c b/contrib/libarchive/libarchive/archive_write_disk_posix.c index 1a36e6708c49..9d52dbfb3941 100644 --- a/contrib/libarchive/libarchive/archive_write_disk_posix.c +++ b/contrib/libarchive/libarchive/archive_write_disk_posix.c @@ -1,4711 +1,4758 @@ /*- * 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; __LA_MODE_T filetype; 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 int la_verify_filetype(mode_t, __LA_MODE_T); static void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, int, 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 void close_file_descriptor(struct archive_write_disk *); 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 la_verify_filetype(mode_t mode, __LA_MODE_T filetype) { int ret = 0; switch (filetype) { case AE_IFREG: ret = (S_ISREG(mode)); break; case AE_IFDIR: ret = (S_ISDIR(mode)); break; case AE_IFLNK: ret = (S_ISLNK(mode)); break; case AE_IFSOCK: ret = (S_ISSOCK(mode)); break; case AE_IFCHR: ret = (S_ISCHR(mode)); break; case AE_IFBLK: ret = (S_ISBLK(mode)); break; case AE_IFIFO: ret = (S_ISFIFO(mode)); break; default: break; } return (ret); } 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) { +#ifdef HAVE_LSTAT + if (lstat(a->name, &a->st) == 0) +#else + if (la_stat(a->name, &a->st) == 0) +#endif + { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static const struct archive_vtable archive_write_disk_vtable = { .archive_close = _archive_write_disk_close, .archive_filter_bytes = _archive_write_disk_filter_bytes, .archive_free = _archive_write_disk_free, .archive_write_header = _archive_write_disk_header, .archive_write_finish_entry = _archive_write_disk_finish_entry, .archive_write_data = _archive_write_disk_data, .archive_write_data_block = _archive_write_disk_data_block, }; 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->filetype = archive_entry_filetype(entry); 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->filetype = archive_entry_filetype(entry); 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->filetype = archive_entry_filetype(entry); 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->filetype = archive_entry_filetype(entry); 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->filetype = archive_entry_filetype(entry); 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) + close_file_descriptor(a); 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"); + close_file_descriptor(a); 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); + if ((ret = lazy_stat(a)) != ARCHIVE_OK) { + close_file_descriptor(a); + 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"); + close_file_descriptor(a); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); + close_file_descriptor(a); 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); } a->path_safe.s[0] = 0; #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)) +#ifdef HAVE_LSTAT r = lstat(a->name, &a->st); +#else + r = la_stat(a->name, &a->st); +#endif 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, 1); 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); #ifdef HAVE_LINKAT r = linkat(AT_FDCWD, linkname, AT_FDCWD, a->name, 0) ? errno : 0; #else r = link(linkname, a->name) ? errno : 0; #endif /* * 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; struct stat st; char *c; int fd, ret, openflags; 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. */ /* We must strip trailing slashes from the path to avoid dereferencing symbolic links to directories */ c = p->name; while (*c != '\0') c++; while (c != p->name && *(c - 1) == '/') { c--; *c = '\0'; } if (p->fixup == 0) goto skip_fixup_entry; else { /* * We need to verify if the type of the file * we are going to open matches the file type * of the fixup entry. */ openflags = O_BINARY | O_NOFOLLOW | O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) if (p->filetype == AE_IFDIR) openflags |= O_DIRECTORY; #endif fd = open(p->name, openflags); #if defined(O_DIRECTORY) /* * If we support O_DIRECTORY and open was * successful we can skip the file type check * for directories. For other file types * we need to verify via fstat() or lstat() */ if (fd == -1 || p->filetype != AE_IFDIR) { #if HAVE_FSTAT if (fd > 0 && ( fstat(fd, &st) != 0 || la_verify_filetype(st.st_mode, p->filetype) == 0)) { goto skip_fixup_entry; } else #endif - if (lstat(p->name, &st) != 0 || + if ( +#ifdef HAVE_LSTAT + lstat(p->name, &st) != 0 || +#else + la_stat(p->name, &st) != 0 || +#endif la_verify_filetype(st.st_mode, p->filetype) == 0) { goto skip_fixup_entry; } } #else #if HAVE_FSTAT if (fd > 0 && ( fstat(fd, &st) != 0 || la_verify_filetype(st.st_mode, p->filetype) == 0)) { goto skip_fixup_entry; } else #endif - if (lstat(p->name, &st) != 0 || + if ( +#ifdef HAVE_LSTAT + lstat(p->name, &st) != 0 || +#else + la_stat(p->name, &st) != 0 || +#endif la_verify_filetype(st.st_mode, p->filetype) == 0) { goto skip_fixup_entry; } #endif } 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 & 07777); else #endif #ifdef HAVE_LCHMOD lchmod(p->name, p->mode & 07777); #else chmod(p->name, p->mode & 07777); #endif } 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); skip_fixup_entry: 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->filetype = 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, int checking_linkname) { #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)a_eno; /* UNUSED */ + (void)a_estr; /* UNUSED */ (void)flags; /* UNUSED */ (void)checking_linkname; /* UNUSED */ return (ARCHIVE_OK); #else int res = ARCHIVE_OK; char *tail; char *head; int last; char c = '\0'; 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 +#elif defined(HAVE_LSTAT) r = lstat(head, &st); +#else + r = la_stat(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 && checking_linkname) { #ifdef HAVE_LINKAT /* * Hardlinks to symlinks are safe to write * if linkat() is supported as it does not * follow symlinks. */ res = ARCHIVE_OK; #else /* * We return ARCHIVE_FAILED here as we are * not able to safely write hardlinks * to symlinks. */ tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Cannot write hardlink to symlink ", path); res = ARCHIVE_FAILED; #endif break; } else 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, 0); 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; /* UNUSED */ (void)atime_nsec; /* UNUSED */ + (void)mtime; /* 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->filetype = archive_entry_filetype(a->entry); 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 | O_NOFOLLOW); __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 || + if ( +#ifdef HAVE_LSTAT + lstat(datafork.s, &st) == -1 || +#else + la_stat(datafork.s, &st) == -1 || +#endif (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 +/* + * Close the file descriptor if one is open. + */ +static void close_file_descriptor(struct archive_write_disk* a) +{ + if (a->fd >= 0) { + close(a->fd); + a->fd = -1; + } +} + + #endif /* !_WIN32 || __CYGWIN__ */ diff --git a/contrib/libarchive/libarchive/archive_write_private.h b/contrib/libarchive/libarchive/archive_write_private.h index 98518a39c39b..3a11208122ea 100644 --- a/contrib/libarchive/libarchive/archive_write_private.h +++ b/contrib/libarchive/libarchive/archive_write_private.h @@ -1,165 +1,166 @@ /*- * 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 (*flush)(struct archive_write_filter *); 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 diff --git a/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c b/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c index b3e41d4ab997..2a3ae07fa2b2 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c @@ -1,8165 +1,8161 @@ /*- * 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; unsigned 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". */ unsigned 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] + * 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. */ + /* 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 { unsigned int detect_magic:1; unsigned int making:1; unsigned 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 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 +#if HAVE_LOCALTIME_S + localtime_s(tm, t); +#elif 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)); - } +#if defined(HAVE_CTIME_S) + ctime_s(buf, sizeof(buf), &(iso9660->birth_time)); #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 __LA_LIBC_CC _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 __LA_LIBC_CC _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; unsigned int initialized:1; unsigned 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 */ diff --git a/contrib/libarchive/libarchive/archive_write_set_format_pax.c b/contrib/libarchive/libarchive/archive_write_set_format_pax.c index 2d3c7086e1f9..8ce1d18c61e5 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_pax.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_pax.c @@ -1,2063 +1,2078 @@ /*- * Copyright (c) 2003-2007 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$"); #ifdef HAVE_ERRNO_H #include #endif #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" struct sparse_block { struct sparse_block *next; int is_hole; uint64_t offset; uint64_t remaining; }; struct pax { uint64_t entry_bytes_remaining; uint64_t entry_padding; struct archive_string l_url_encoded_name; struct archive_string pax_header; struct archive_string sparse_map; size_t sparse_map_padding; struct sparse_block *sparse_list; struct sparse_block *sparse_tail; struct archive_string_conv *sconv_utf8; int opt_binary; unsigned flags; #define WRITE_SCHILY_XATTR (1 << 0) #define WRITE_LIBARCHIVE_XATTR (1 << 1) }; static void add_pax_attr(struct archive_string *, const char *key, const char *value); static void add_pax_attr_binary(struct archive_string *, const char *key, const char *value, size_t value_len); static void add_pax_attr_int(struct archive_string *, const char *key, int64_t value); static void add_pax_attr_time(struct archive_string *, const char *key, int64_t sec, unsigned long nanos); static int add_pax_acl(struct archive_write *, struct archive_entry *, struct pax *, int); static ssize_t archive_write_pax_data(struct archive_write *, const void *, size_t); static int archive_write_pax_close(struct archive_write *); static int archive_write_pax_free(struct archive_write *); static int archive_write_pax_finish_entry(struct archive_write *); static int archive_write_pax_header(struct archive_write *, struct archive_entry *); static int archive_write_pax_options(struct archive_write *, const char *, const char *); static char *base64_encode(const char *src, size_t len); static char *build_gnu_sparse_name(char *dest, const char *src); static char *build_pax_attribute_name(char *dest, const char *src); static char *build_ustar_entry_name(char *dest, const char *src, size_t src_length, const char *insert); static char *format_int(char *dest, int64_t); static int has_non_ASCII(const char *); static void sparse_list_clear(struct pax *); static int sparse_list_add(struct pax *, int64_t, int64_t); static char *url_encode(const char *in); +static time_t get_ustar_max_mtime(void); /* * Set output format to 'restricted pax' format. * * This is the same as normal 'pax', but tries to suppress * the pax header whenever possible. This is the default for * bsdtar, for instance. */ int archive_write_set_format_pax_restricted(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_pax_restricted"); r = archive_write_set_format_pax(&a->archive); a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; a->archive.archive_format_name = "restricted POSIX pax interchange"; return (r); } /* * Set output format to 'pax' format. */ int archive_write_set_format_pax(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct pax *pax; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_pax"); if (a->format_free != NULL) (a->format_free)(a); pax = (struct pax *)calloc(1, sizeof(*pax)); if (pax == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); return (ARCHIVE_FATAL); } pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR; a->format_data = pax; a->format_name = "pax"; a->format_options = archive_write_pax_options; a->format_write_header = archive_write_pax_header; a->format_write_data = archive_write_pax_data; a->format_close = archive_write_pax_close; a->format_free = archive_write_pax_free; a->format_finish_entry = archive_write_pax_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange"; return (ARCHIVE_OK); } static int archive_write_pax_options(struct archive_write *a, const char *key, const char *val) { struct pax *pax = (struct pax *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { /* * The character-set we can use are defined in * IEEE Std 1003.1-2001 */ if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: hdrcharset option needs a character-set name"); else if (strcmp(val, "BINARY") == 0 || strcmp(val, "binary") == 0) { /* * Specify binary mode. We will not convert * filenames, uname and gname to any charsets. */ pax->opt_binary = 1; ret = ARCHIVE_OK; } else if (strcmp(val, "UTF-8") == 0) { /* * Specify UTF-8 character-set to be used for * filenames. This is almost the test that * running platform supports the string conversion. * Especially libarchive_test needs this trick for * its test. */ pax->sconv_utf8 = archive_string_conversion_to_charset( &(a->archive), "UTF-8", 0); if (pax->sconv_utf8 == NULL) ret = ARCHIVE_FATAL; else ret = ARCHIVE_OK; } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: invalid charset name"); return (ret); } else if (strcmp(key, "xattrheader") == 0) { if (val == NULL || val[0] == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: xattrheader requires a value"); } else if (strcmp(val, "ALL") == 0 || strcmp(val, "all") == 0) { pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR; ret = ARCHIVE_OK; } else if (strcmp(val, "SCHILY") == 0 || strcmp(val, "schily") == 0) { pax->flags |= WRITE_SCHILY_XATTR; pax->flags &= ~WRITE_LIBARCHIVE_XATTR; ret = ARCHIVE_OK; } else if (strcmp(val, "LIBARCHIVE") == 0 || strcmp(val, "libarchive") == 0) { pax->flags |= WRITE_LIBARCHIVE_XATTR; pax->flags &= ~WRITE_SCHILY_XATTR; ret = ARCHIVE_OK; } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: invalid xattr header name"); 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); } /* * Note: This code assumes that 'nanos' has the same sign as 'sec', * which implies that sec=-1, nanos=200000000 represents -1.2 seconds * and not -0.8 seconds. This is a pretty pedantic point, as we're * unlikely to encounter many real files created before Jan 1, 1970, * much less ones with timestamps recorded to sub-second resolution. */ static void add_pax_attr_time(struct archive_string *as, const char *key, int64_t sec, unsigned long nanos) { int digit, i; char *t; /* * Note that each byte contributes fewer than 3 base-10 * digits, so this will always be big enough. */ char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)]; tmp[sizeof(tmp) - 1] = 0; t = tmp + sizeof(tmp) - 1; /* Skip trailing zeros in the fractional part. */ for (digit = 0, i = 10; i > 0 && digit == 0; i--) { digit = nanos % 10; nanos /= 10; } /* Only format the fraction if it's non-zero. */ if (i > 0) { while (i > 0) { *--t = "0123456789"[digit]; digit = nanos % 10; nanos /= 10; i--; } *--t = '.'; } t = format_int(t, sec); add_pax_attr(as, key, t); } static char * format_int(char *t, int64_t i) { uint64_t ui; if (i < 0) ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i); else ui = i; do { *--t = "0123456789"[ui % 10]; } while (ui /= 10); if (i < 0) *--t = '-'; return (t); } static void add_pax_attr_int(struct archive_string *as, const char *key, int64_t value) { char tmp[1 + 3 * sizeof(value)]; tmp[sizeof(tmp) - 1] = 0; add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value)); } /* * Add a key/value attribute to the pax header. This function handles * the length field and various other syntactic requirements. */ static void add_pax_attr(struct archive_string *as, const char *key, const char *value) { add_pax_attr_binary(as, key, value, strlen(value)); } /* * Add a key/value attribute to the pax header. This function handles * binary values. */ static void add_pax_attr_binary(struct archive_string *as, const char *key, const char *value, size_t value_len) { int digits, i, len, next_ten; char tmp[1 + 3 * sizeof(int)]; /* < 3 base-10 digits per byte */ /*- * PAX attributes have the following layout: * <=> */ len = 1 + (int)strlen(key) + 1 + (int)value_len + 1; /* * The field includes the length of the field, so * computing the correct length is tricky. I start by * counting the number of base-10 digits in 'len' and * computing the next higher power of 10. */ next_ten = 1; digits = 0; i = len; while (i > 0) { i = i / 10; digits++; next_ten = next_ten * 10; } /* * For example, if string without the length field is 99 * chars, then adding the 2 digit length "99" will force the * total length past 100, requiring an extra digit. The next * statement adjusts for this effect. */ if (len + digits >= next_ten) digits++; /* Now, we have the right length so we can build the line. */ tmp[sizeof(tmp) - 1] = 0; /* Null-terminate the work area. */ archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits)); archive_strappend_char(as, ' '); archive_strcat(as, key); archive_strappend_char(as, '='); archive_array_append(as, value, value_len); archive_strappend_char(as, '\n'); } static void archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name, const void *value, size_t value_len) { struct archive_string s; char *encoded_value; if (encoded_name == NULL) return; if (pax->flags & WRITE_LIBARCHIVE_XATTR) { encoded_value = base64_encode((const char *)value, value_len); if (encoded_value != NULL) { archive_string_init(&s); archive_strcpy(&s, "LIBARCHIVE.xattr."); archive_strcat(&s, encoded_name); add_pax_attr(&(pax->pax_header), s.s, encoded_value); archive_string_free(&s); } free(encoded_value); } if (pax->flags & WRITE_SCHILY_XATTR) { archive_string_init(&s); archive_strcpy(&s, "SCHILY.xattr."); archive_strcat(&s, encoded_name); add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len); archive_string_free(&s); } } static int archive_write_pax_header_xattrs(struct archive_write *a, struct pax *pax, struct archive_entry *entry) { int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; char *url_encoded_name = NULL, *encoded_name = NULL; size_t size; int r; archive_entry_xattr_next(entry, &name, &value, &size); url_encoded_name = url_encode(name); if (url_encoded_name == NULL) goto malloc_error; else { /* Convert narrow-character to UTF-8. */ r = archive_strcpy_l(&(pax->l_url_encoded_name), url_encoded_name, pax->sconv_utf8); free(url_encoded_name); /* Done with this. */ if (r == 0) encoded_name = pax->l_url_encoded_name.s; else if (r == -1) goto malloc_error; else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Error encoding pax extended attribute"); return (ARCHIVE_FAILED); } } archive_write_pax_header_xattr(pax, encoded_name, value, size); } return (ARCHIVE_OK); malloc_error: archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } static int get_entry_hardlink(struct archive_write *a, struct archive_entry *entry, const char **name, size_t *length, struct archive_string_conv *sc) { int r; r = archive_entry_hardlink_l(entry, name, length, sc); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int get_entry_pathname(struct archive_write *a, struct archive_entry *entry, const char **name, size_t *length, struct archive_string_conv *sc) { int r; r = archive_entry_pathname_l(entry, name, length, sc); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int get_entry_uname(struct archive_write *a, struct archive_entry *entry, const char **name, size_t *length, struct archive_string_conv *sc) { int r; r = archive_entry_uname_l(entry, name, length, sc); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); return (ARCHIVE_FATAL); } return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int get_entry_gname(struct archive_write *a, struct archive_entry *entry, const char **name, size_t *length, struct archive_string_conv *sc) { int r; r = archive_entry_gname_l(entry, name, length, sc); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); return (ARCHIVE_FATAL); } return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int get_entry_symlink(struct archive_write *a, struct archive_entry *entry, const char **name, size_t *length, struct archive_string_conv *sc) { int r; r = archive_entry_symlink_l(entry, name, length, sc); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* Add ACL to pax header */ static int add_pax_acl(struct archive_write *a, struct archive_entry *entry, struct pax *pax, int flags) { char *p; const char *attr; int acl_types; acl_types = archive_entry_acl_types(entry); if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) attr = "SCHILY.acl.ace"; else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) attr = "SCHILY.acl.access"; else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) attr = "SCHILY.acl.default"; else return (ARCHIVE_FATAL); p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8); if (p == NULL) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "%s %s", "Can't allocate memory for ", attr); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s", "Can't translate ", attr, " to UTF-8"); return(ARCHIVE_WARN); } if (*p != '\0') { add_pax_attr(&(pax->pax_header), attr, p); } free(p); return(ARCHIVE_OK); } /* * TODO: Consider adding 'comment' and 'charset' fields to * archive_entry so that clients can specify them. Also, consider * adding generic key/value tags so clients can add arbitrary * key/value data. * * TODO: Break up this 700-line function!!!! Yowza! */ static int archive_write_pax_header(struct archive_write *a, struct archive_entry *entry_original) { struct archive_entry *entry_main; const char *p; const char *suffix; int need_extension, r, ret; int acl_types; int sparse_count; uint64_t sparse_total, real_size; struct pax *pax; const char *hardlink; const char *path = NULL, *linkpath = NULL; const char *uname = NULL, *gname = NULL; const void *mac_metadata; size_t mac_metadata_size; struct archive_string_conv *sconv; size_t hardlink_length, path_length, linkpath_length; size_t uname_length, gname_length; char paxbuff[512]; char ustarbuff[512]; char ustar_entry_name[256]; char pax_entry_name[256]; char gnu_sparse_name[256]; struct archive_string entry_name; ret = ARCHIVE_OK; need_extension = 0; pax = (struct pax *)a->format_data; + const time_t ustar_max_mtime = get_ustar_max_mtime(); + /* Sanity check. */ if (archive_entry_pathname(entry_original) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); } /* * Choose a header encoding. */ if (pax->opt_binary) sconv = NULL;/* Binary mode. */ else { /* Header encoding is UTF-8. */ if (pax->sconv_utf8 == NULL) { /* Initialize the string conversion object * we must need */ pax->sconv_utf8 = archive_string_conversion_to_charset( &(a->archive), "UTF-8", 1); if (pax->sconv_utf8 == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FAILED); } sconv = pax->sconv_utf8; } r = get_entry_hardlink(a, entry_original, &hardlink, &hardlink_length, sconv); if (r == ARCHIVE_FATAL) return (r); else if (r != ARCHIVE_OK) { r = get_entry_hardlink(a, entry_original, &hardlink, &hardlink_length, NULL); if (r == ARCHIVE_FATAL) return (r); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", hardlink, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; sconv = NULL;/* The header charset switches to binary mode. */ } /* Make sure this is a type of entry that we can handle here */ if (hardlink == NULL) { switch (archive_entry_filetype(entry_original)) { case AE_IFBLK: case AE_IFCHR: case AE_IFIFO: case AE_IFLNK: case AE_IFREG: break; case AE_IFDIR: { /* * Ensure a trailing '/'. Modify the original * entry so the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry_original); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w( entry_original, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry_original); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname( entry_original, as.s); archive_string_free(&as); } break; } default: /* AE_IFSOCK and unknown */ __archive_write_entry_filetype_unsupported( &a->archive, entry_original, "pax"); return (ARCHIVE_FAILED); } } /* * If Mac OS metadata blob is here, recurse to write that * as a separate entry. This is really a pretty poor design: * In particular, it doubles the overhead for long filenames. * TODO: Help Apple folks design something better and figure * out how to transition from this legacy format. * * Note that this code is present on every platform; clients * on non-Mac are unlikely to ever provide this data, but * applications that copy entries from one archive to another * should not lose data just because the local filesystem * can't store it. */ mac_metadata = archive_entry_mac_metadata(entry_original, &mac_metadata_size); if (mac_metadata != NULL) { const char *oname; char *name, *bname; size_t name_length; struct archive_entry *extra = archive_entry_new2(&a->archive); oname = archive_entry_pathname(entry_original); name_length = strlen(oname); name = malloc(name_length + 3); if (name == NULL || extra == NULL) { /* XXX error message */ archive_entry_free(extra); free(name); return (ARCHIVE_FAILED); } strcpy(name, oname); /* Find last '/'; strip trailing '/' characters */ bname = strrchr(name, '/'); while (bname != NULL && bname[1] == '\0') { *bname = '\0'; bname = strrchr(name, '/'); } if (bname == NULL) { memmove(name + 2, name, name_length + 1); memmove(name, "._", 2); } else { bname += 1; memmove(bname + 2, bname, strlen(bname) + 1); memmove(bname, "._", 2); } archive_entry_copy_pathname(extra, name); free(name); archive_entry_set_size(extra, mac_metadata_size); archive_entry_set_filetype(extra, AE_IFREG); archive_entry_set_perm(extra, archive_entry_perm(entry_original)); archive_entry_set_mtime(extra, archive_entry_mtime(entry_original), archive_entry_mtime_nsec(entry_original)); archive_entry_set_gid(extra, archive_entry_gid(entry_original)); archive_entry_set_gname(extra, archive_entry_gname(entry_original)); archive_entry_set_uid(extra, archive_entry_uid(entry_original)); archive_entry_set_uname(extra, archive_entry_uname(entry_original)); /* Recurse to write the special copyfile entry. */ r = archive_write_pax_header(a, extra); archive_entry_free(extra); if (r < ARCHIVE_WARN) return (r); if (r < ret) ret = r; r = (int)archive_write_pax_data(a, mac_metadata, mac_metadata_size); if (r < ARCHIVE_WARN) return (r); if (r < ret) ret = r; r = archive_write_pax_finish_entry(a); if (r < ARCHIVE_WARN) return (r); if (r < ret) ret = r; } /* Copy entry so we can modify it as needed. */ #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_original); if (entry_main == entry_original) entry_main = archive_entry_clone(entry_original); #else entry_main = archive_entry_clone(entry_original); #endif if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data"); return(ARCHIVE_FATAL); } archive_string_empty(&(pax->pax_header)); /* Blank our work area. */ archive_string_empty(&(pax->sparse_map)); sparse_total = 0; sparse_list_clear(pax); if (hardlink == NULL && archive_entry_filetype(entry_main) == AE_IFREG) sparse_count = archive_entry_sparse_reset(entry_main); else sparse_count = 0; if (sparse_count) { int64_t offset, length, last_offset = 0; /* Get the last entry of sparse block. */ while (archive_entry_sparse_next( entry_main, &offset, &length) == ARCHIVE_OK) last_offset = offset + length; /* If the last sparse block does not reach the end of file, * We have to add a empty sparse block as the last entry to * manage storing file data. */ if (last_offset < archive_entry_size(entry_main)) archive_entry_sparse_add_entry(entry_main, archive_entry_size(entry_main), 0); sparse_count = archive_entry_sparse_reset(entry_main); } /* * First, check the name fields and see if any of them * require binary coding. If any of them does, then all of * them do. */ r = get_entry_pathname(a, entry_main, &path, &path_length, sconv); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } else if (r != ARCHIVE_OK) { r = get_entry_pathname(a, entry_main, &path, &path_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", path, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; sconv = NULL;/* The header charset switches to binary mode. */ } r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } else if (r != ARCHIVE_OK) { r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", uname, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; sconv = NULL;/* The header charset switches to binary mode. */ } r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } else if (r != ARCHIVE_OK) { r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", gname, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; sconv = NULL;/* The header charset switches to binary mode. */ } linkpath = hardlink; linkpath_length = hardlink_length; if (linkpath == NULL) { r = get_entry_symlink(a, entry_main, &linkpath, &linkpath_length, sconv); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } else if (r != ARCHIVE_OK) { r = get_entry_symlink(a, entry_main, &linkpath, &linkpath_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", linkpath, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; sconv = NULL; } } /* If any string conversions failed, get all attributes * in binary-mode. */ if (sconv == NULL && !pax->opt_binary) { if (hardlink != NULL) { r = get_entry_hardlink(a, entry_main, &hardlink, &hardlink_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } linkpath = hardlink; linkpath_length = hardlink_length; } r = get_entry_pathname(a, entry_main, &path, &path_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL); if (r == ARCHIVE_FATAL) { archive_entry_free(entry_main); return (r); } } /* Store the header encoding first, to be nice to readers. */ if (sconv == NULL) add_pax_attr(&(pax->pax_header), "hdrcharset", "BINARY"); /* * If name is too long, or has non-ASCII characters, add * 'path' to pax extended attrs. (Note that an unconvertible * name must have non-ASCII characters.) */ if (has_non_ASCII(path)) { /* We have non-ASCII characters. */ add_pax_attr(&(pax->pax_header), "path", path); archive_entry_set_pathname(entry_main, build_ustar_entry_name(ustar_entry_name, path, path_length, NULL)); need_extension = 1; } else { /* We have an all-ASCII path; we'd like to just store * it in the ustar header if it will fit. Yes, this * duplicates some of the logic in * archive_write_set_format_ustar.c */ if (path_length <= 100) { /* Fits in the old 100-char tar name field. */ } else { /* Find largest suffix that will fit. */ /* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */ suffix = strchr(path + path_length - 100 - 1, '/'); /* Don't attempt an empty prefix. */ if (suffix == path) suffix = strchr(suffix + 1, '/'); /* We can put it in the ustar header if it's * all ASCII and it's either <= 100 characters * or can be split at a '/' into a prefix <= * 155 chars and a suffix <= 100 chars. (Note * the strchr() above will return NULL exactly * when the path can't be split.) */ if (suffix == NULL /* Suffix > 100 chars. */ || suffix[1] == '\0' /* empty suffix */ || suffix - path > 155) /* Prefix > 155 chars */ { add_pax_attr(&(pax->pax_header), "path", path); archive_entry_set_pathname(entry_main, build_ustar_entry_name(ustar_entry_name, path, path_length, NULL)); need_extension = 1; } } } if (linkpath != NULL) { /* If link name is too long or has non-ASCII characters, add * 'linkpath' to pax extended attrs. */ if (linkpath_length > 100 || has_non_ASCII(linkpath)) { add_pax_attr(&(pax->pax_header), "linkpath", linkpath); if (linkpath_length > 100) { if (hardlink != NULL) archive_entry_set_hardlink(entry_main, "././@LongHardLink"); else archive_entry_set_symlink(entry_main, "././@LongSymLink"); } need_extension = 1; } } /* Save a pathname since it will be renamed if `entry_main` has * sparse blocks. */ archive_string_init(&entry_name); archive_strcpy(&entry_name, archive_entry_pathname(entry_main)); /* If file size is too large, we need pax extended attrs. */ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) { need_extension = 1; } /* If numeric GID is too large, add 'gid' to pax extended attrs. */ if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "gid", archive_entry_gid(entry_main)); need_extension = 1; } /* If group name is too large or has non-ASCII characters, add * 'gname' to pax extended attrs. */ if (gname != NULL) { if (gname_length > 31 || has_non_ASCII(gname)) { add_pax_attr(&(pax->pax_header), "gname", gname); need_extension = 1; } } /* If numeric UID is too large, add 'uid' to pax extended attrs. */ if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "uid", archive_entry_uid(entry_main)); need_extension = 1; } /* Add 'uname' to pax extended attrs if necessary. */ if (uname != NULL) { if (uname_length > 31 || has_non_ASCII(uname)) { add_pax_attr(&(pax->pax_header), "uname", uname); need_extension = 1; } } /* * POSIX/SUSv3 doesn't provide a standard key for large device * numbers. I use the same keys here that Joerg Schilling * used for 'star.' (Which, somewhat confusingly, are called * "devXXX" even though they code "rdev" values.) No doubt, * other implementations use other keys. Note that there's no * reason we can't write the same information into a number of * different keys. * * Of course, this is only needed for block or char device entries. */ if (archive_entry_filetype(entry_main) == AE_IFBLK || archive_entry_filetype(entry_main) == AE_IFCHR) { /* * If rdevmajor is too large, add 'SCHILY.devmajor' to * extended attributes. */ int rdevmajor, rdevminor; rdevmajor = archive_entry_rdevmajor(entry_main); rdevminor = archive_entry_rdevminor(entry_main); if (rdevmajor >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor", rdevmajor); /* * Non-strict formatting below means we don't * have to truncate here. Not truncating improves * the chance that some more modern tar archivers * (such as GNU tar 1.13) can restore the full * value even if they don't understand the pax * extended attributes. See my rant below about * file size fields for additional details. */ /* archive_entry_set_rdevmajor(entry_main, rdevmajor & ((1 << 18) - 1)); */ need_extension = 1; } /* * If devminor is too large, add 'SCHILY.devminor' to * extended attributes. */ if (rdevminor >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor", rdevminor); /* Truncation is not necessary here, either. */ /* archive_entry_set_rdevminor(entry_main, rdevminor & ((1 << 18) - 1)); */ need_extension = 1; } } /* - * Technically, the mtime field in the ustar header can - * support 33 bits, but many platforms use signed 32-bit time - * values. The cutoff of 0x7fffffff here is a compromise. * Yes, this check is duplicated just below; this helps to * avoid writing an mtime attribute just to handle a * high-resolution timestamp in "restricted pax" mode. */ if (!need_extension && ((archive_entry_mtime(entry_main) < 0) - || (archive_entry_mtime(entry_main) >= 0x7fffffff))) + || (archive_entry_mtime(entry_main) >= ustar_max_mtime))) need_extension = 1; /* I use a star-compatible file flag attribute. */ p = archive_entry_fflags_text(entry_main); if (!need_extension && p != NULL && *p != '\0') need_extension = 1; /* If there are extended attributes, we need an extension */ if (!need_extension && archive_entry_xattr_count(entry_original) > 0) need_extension = 1; /* If there are sparse info, we need an extension */ if (!need_extension && sparse_count > 0) need_extension = 1; acl_types = archive_entry_acl_types(entry_original); /* If there are any ACL entries, we need an extension */ if (!need_extension && acl_types != 0) need_extension = 1; /* If the symlink type is defined, we need an extension */ if (!need_extension && archive_entry_symlink_type(entry_main) > 0) need_extension = 1; /* * Libarchive used to include these in extended headers for * restricted pax format, but that confused people who * expected ustar-like time semantics. So now we only include * them in full pax format. */ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED) { if (archive_entry_ctime(entry_main) != 0 || archive_entry_ctime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "ctime", archive_entry_ctime(entry_main), archive_entry_ctime_nsec(entry_main)); if (archive_entry_atime(entry_main) != 0 || archive_entry_atime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "atime", archive_entry_atime(entry_main), archive_entry_atime_nsec(entry_main)); /* Store birth/creationtime only if it's earlier than mtime */ if (archive_entry_birthtime_is_set(entry_main) && archive_entry_birthtime(entry_main) < archive_entry_mtime(entry_main)) add_pax_attr_time(&(pax->pax_header), "LIBARCHIVE.creationtime", archive_entry_birthtime(entry_main), archive_entry_birthtime_nsec(entry_main)); } /* * The following items are handled differently in "pax * restricted" format. In particular, in "pax restricted" * format they won't be added unless need_extension is * already set (we're already generating an extended header, so * may as well include these). */ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED || need_extension) { if (archive_entry_mtime(entry_main) < 0 || - archive_entry_mtime(entry_main) >= 0x7fffffff || + archive_entry_mtime(entry_main) >= ustar_max_mtime || archive_entry_mtime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "mtime", archive_entry_mtime(entry_main), archive_entry_mtime_nsec(entry_main)); /* I use a star-compatible file flag attribute. */ p = archive_entry_fflags_text(entry_main); if (p != NULL && *p != '\0') add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p); /* I use star-compatible ACL attributes. */ if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { ret = add_pax_acl(a, entry_original, pax, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_COMPACT); if (ret == ARCHIVE_FATAL) { archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } } if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { ret = add_pax_acl(a, entry_original, pax, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA); if (ret == ARCHIVE_FATAL) { archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } } if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) { ret = add_pax_acl(a, entry_original, pax, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA); if (ret == ARCHIVE_FATAL) { archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } } /* We use GNU-tar-compatible sparse attributes. */ if (sparse_count > 0) { int64_t soffset, slength; add_pax_attr_int(&(pax->pax_header), "GNU.sparse.major", 1); add_pax_attr_int(&(pax->pax_header), "GNU.sparse.minor", 0); /* * Make sure to store the original path, since * truncation to ustar limit happened already. */ add_pax_attr(&(pax->pax_header), "GNU.sparse.name", path); add_pax_attr_int(&(pax->pax_header), "GNU.sparse.realsize", archive_entry_size(entry_main)); /* Rename the file name which will be used for * ustar header to a special name, which GNU * PAX Format 1.0 requires */ archive_entry_set_pathname(entry_main, build_gnu_sparse_name(gnu_sparse_name, entry_name.s)); /* * - Make a sparse map, which will precede a file data. * - Get the total size of available data of sparse. */ archive_string_sprintf(&(pax->sparse_map), "%d\n", sparse_count); while (archive_entry_sparse_next(entry_main, &soffset, &slength) == ARCHIVE_OK) { archive_string_sprintf(&(pax->sparse_map), "%jd\n%jd\n", (intmax_t)soffset, (intmax_t)slength); sparse_total += slength; if (sparse_list_add(pax, soffset, slength) != ARCHIVE_OK) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } } } /* Store extended attributes */ if (archive_write_pax_header_xattrs(a, pax, entry_original) == ARCHIVE_FATAL) { archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } /* Store extended symlink information */ if (archive_entry_symlink_type(entry_main) == AE_SYMLINK_TYPE_FILE) { add_pax_attr(&(pax->pax_header), "LIBARCHIVE.symlinktype", "file"); } else if (archive_entry_symlink_type(entry_main) == AE_SYMLINK_TYPE_DIRECTORY) { add_pax_attr(&(pax->pax_header), "LIBARCHIVE.symlinktype", "dir"); } } /* Only regular files have data. */ if (archive_entry_filetype(entry_main) != AE_IFREG) archive_entry_set_size(entry_main, 0); /* * Pax-restricted does not store data for hardlinks, in order * to improve compatibility with ustar. */ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && hardlink != NULL) archive_entry_set_size(entry_main, 0); /* * XXX Full pax interchange format does permit a hardlink * entry to have data associated with it. I'm not supporting * that here because the client expects me to tell them whether * or not this format expects data for hardlinks. If I * don't check here, then every pax archive will end up with * duplicated data for hardlinks. Someday, there may be * need to select this behavior, in which case the following * will need to be revisited. XXX */ if (hardlink != NULL) archive_entry_set_size(entry_main, 0); /* Save a real file size. */ real_size = archive_entry_size(entry_main); /* * Overwrite a file size by the total size of sparse blocks and * the size of sparse map info. That file size is the length of * the data, which we will exactly store into an archive file. */ if (archive_strlen(&(pax->sparse_map))) { size_t mapsize = archive_strlen(&(pax->sparse_map)); pax->sparse_map_padding = 0x1ff & (-(ssize_t)mapsize); archive_entry_set_size(entry_main, mapsize + pax->sparse_map_padding + sparse_total); } /* If file size is too large, add 'size' to pax extended attrs. */ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) { add_pax_attr_int(&(pax->pax_header), "size", archive_entry_size(entry_main)); } /* Format 'ustar' header for main entry. * * The trouble with file size: If the reader can't understand * the file size, they may not be able to locate the next * entry and the rest of the archive is toast. Pax-compliant * readers are supposed to ignore the file size in the main * header, so the question becomes how to maximize portability * for readers that don't support pax attribute extensions. * For maximum compatibility, I permit numeric extensions in * the main header so that the file size stored will always be * correct, even if it's in a format that only some * implementations understand. The technique used here is: * * a) If possible, follow the standard exactly. This handles * files up to 8 gigabytes minus 1. * * b) If that fails, try octal but omit the field terminator. * That handles files up to 64 gigabytes minus 1. * * c) Otherwise, use base-256 extensions. That handles files * up to 2^63 in this implementation, with the potential to * go up to 2^94. That should hold us for a while. ;-) * * The non-strict formatter uses similar logic for other * numeric fields, though they're less critical. */ if (__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0, NULL) == ARCHIVE_FATAL) { archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } /* If we built any extended attributes, write that entry first. */ if (archive_strlen(&(pax->pax_header)) > 0) { struct archive_entry *pax_attr_entry; time_t s; int64_t uid, gid; int mode; pax_attr_entry = archive_entry_new2(&a->archive); p = entry_name.s; archive_entry_set_pathname(pax_attr_entry, build_pax_attribute_name(pax_entry_name, p)); archive_entry_set_size(pax_attr_entry, archive_strlen(&(pax->pax_header))); /* Copy uid/gid (but clip to ustar limits). */ uid = archive_entry_uid(entry_main); if (uid >= 1 << 18) uid = (1 << 18) - 1; archive_entry_set_uid(pax_attr_entry, uid); gid = archive_entry_gid(entry_main); if (gid >= 1 << 18) gid = (1 << 18) - 1; archive_entry_set_gid(pax_attr_entry, gid); /* Copy mode over (but not setuid/setgid bits) */ mode = archive_entry_mode(entry_main); #ifdef S_ISUID mode &= ~S_ISUID; #endif #ifdef S_ISGID mode &= ~S_ISGID; #endif #ifdef S_ISVTX mode &= ~S_ISVTX; #endif archive_entry_set_mode(pax_attr_entry, mode); /* Copy uname/gname. */ archive_entry_set_uname(pax_attr_entry, archive_entry_uname(entry_main)); archive_entry_set_gname(pax_attr_entry, archive_entry_gname(entry_main)); /* Copy mtime, but clip to ustar limits. */ s = archive_entry_mtime(entry_main); if (s < 0) { s = 0; } - if (s >= 0x7fffffff) { s = 0x7fffffff; } + if (s > ustar_max_mtime) { s = ustar_max_mtime; } archive_entry_set_mtime(pax_attr_entry, s, 0); /* Standard ustar doesn't support atime. */ archive_entry_set_atime(pax_attr_entry, 0, 0); /* Standard ustar doesn't support ctime. */ archive_entry_set_ctime(pax_attr_entry, 0, 0); r = __archive_write_format_header_ustar(a, paxbuff, pax_attr_entry, 'x', 1, NULL); archive_entry_free(pax_attr_entry); /* Note that the 'x' header shouldn't ever fail to format */ if (r < ARCHIVE_WARN) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "archive_write_pax_header: " "'x' header failed?! This can't happen.\n"); archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } else if (r < ret) ret = r; r = __archive_write_output(a, paxbuff, 512); if (r != ARCHIVE_OK) { sparse_list_clear(pax); pax->entry_bytes_remaining = 0; pax->entry_padding = 0; archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)); pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); r = __archive_write_output(a, pax->pax_header.s, archive_strlen(&(pax->pax_header))); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } /* Pad out the end of the entry. */ r = __archive_write_nulls(a, (size_t)pax->entry_padding); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); } pax->entry_bytes_remaining = pax->entry_padding = 0; } /* Write the header for main entry. */ r = __archive_write_output(a, ustarbuff, 512); if (r != ARCHIVE_OK) { archive_entry_free(entry_main); archive_string_free(&entry_name); return (r); } /* * Inform the client of the on-disk size we're using, so * they can avoid unnecessarily writing a body for something * that we're just going to ignore. */ archive_entry_set_size(entry_original, real_size); if (pax->sparse_list == NULL && real_size > 0) { /* This is not a sparse file but we handle its data as * a sparse block. */ sparse_list_add(pax, 0, real_size); sparse_total = real_size; } pax->entry_padding = 0x1ff & (-(int64_t)sparse_total); archive_entry_free(entry_main); archive_string_free(&entry_name); return (ret); } /* * We need a valid name for the regular 'ustar' entry. This routine * tries to hack something more-or-less reasonable. * * The approach here tries to preserve leading dir names. We do so by * working with four sections: * 1) "prefix" directory names, * 2) "suffix" directory names, * 3) inserted dir name (optional), * 4) filename. * * These sections must satisfy the following requirements: * * Parts 1 & 2 together form an initial portion of the dir name. * * Part 3 is specified by the caller. (It should not contain a leading * or trailing '/'.) * * Part 4 forms an initial portion of the base filename. * * The filename must be <= 99 chars to fit the ustar 'name' field. * * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld. * * Part 1 must be <= 155 chars to fit the ustar 'prefix' field. * * If the original name ends in a '/', the new name must also end in a '/' * * Trailing '/.' sequences may be stripped. * * Note: Recall that the ustar format does not store the '/' separating * parts 1 & 2, but does store the '/' separating parts 2 & 3. */ static char * build_ustar_entry_name(char *dest, const char *src, size_t src_length, const char *insert) { const char *prefix, *prefix_end; const char *suffix, *suffix_end; const char *filename, *filename_end; char *p; int need_slash = 0; /* Was there a trailing slash? */ size_t suffix_length = 99; size_t insert_length; /* Length of additional dir element to be added. */ if (insert == NULL) insert_length = 0; else /* +2 here allows for '/' before and after the insert. */ insert_length = strlen(insert) + 2; /* Step 0: Quick bailout in a common case. */ if (src_length < 100 && insert == NULL) { strncpy(dest, src, src_length); dest[src_length] = '\0'; return (dest); } /* Step 1: Locate filename and enforce the length restriction. */ filename_end = src + src_length; /* Remove trailing '/' chars and '/.' pairs. */ for (;;) { if (filename_end > src && filename_end[-1] == '/') { filename_end --; need_slash = 1; /* Remember to restore trailing '/'. */ continue; } if (filename_end > src + 1 && filename_end[-1] == '.' && filename_end[-2] == '/') { filename_end -= 2; need_slash = 1; /* "foo/." will become "foo/" */ continue; } break; } if (need_slash) suffix_length--; /* Find start of filename. */ filename = filename_end - 1; while ((filename > src) && (*filename != '/')) filename --; if ((*filename == '/') && (filename < filename_end - 1)) filename ++; /* Adjust filename_end so that filename + insert fits in 99 chars. */ suffix_length -= insert_length; if (filename_end > filename + suffix_length) filename_end = filename + suffix_length; /* Calculate max size for "suffix" section (#3 above). */ suffix_length -= filename_end - filename; /* Step 2: Locate the "prefix" section of the dirname, including * trailing '/'. */ prefix = src; prefix_end = prefix + 155; if (prefix_end > filename) prefix_end = filename; while (prefix_end > prefix && *prefix_end != '/') prefix_end--; if ((prefix_end < filename) && (*prefix_end == '/')) prefix_end++; /* Step 3: Locate the "suffix" section of the dirname, * including trailing '/'. */ suffix = prefix_end; suffix_end = suffix + suffix_length; /* Enforce limit. */ if (suffix_end > filename) suffix_end = filename; if (suffix_end < suffix) suffix_end = suffix; while (suffix_end > suffix && *suffix_end != '/') suffix_end--; if ((suffix_end < filename) && (*suffix_end == '/')) suffix_end++; /* Step 4: Build the new name. */ /* The OpenBSD strlcpy function is safer, but less portable. */ /* Rather than maintain two versions, just use the strncpy version. */ p = dest; if (prefix_end > prefix) { strncpy(p, prefix, prefix_end - prefix); p += prefix_end - prefix; } if (suffix_end > suffix) { strncpy(p, suffix, suffix_end - suffix); p += suffix_end - suffix; } if (insert != NULL) { /* Note: assume insert does not have leading or trailing '/' */ strcpy(p, insert); p += strlen(insert); *p++ = '/'; } strncpy(p, filename, filename_end - filename); p += filename_end - filename; if (need_slash) *p++ = '/'; *p = '\0'; return (dest); } /* * The ustar header for the pax extended attributes must have a * reasonable name: SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename' * where 'pid' is the PID of the archiving process. Unfortunately, * that makes testing a pain since the output varies for each run, * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename' * for now. (Someday, I'll make this settable. Then I can use the * SUS recommendation as default and test harnesses can override it * to get predictable results.) * * Joerg Schilling has argued that this is unnecessary because, in * practice, if the pax extended attributes get extracted as regular * files, no one is going to bother reading those attributes to * manually restore them. Based on this, 'star' uses * /tmp/PaxHeader/'basename' as the ustar header name. This is a * tempting argument, in part because it's simpler than the SUSv3 * recommendation, but I'm not entirely convinced. I'm also * uncomfortable with the fact that "/tmp" is a Unix-ism. * * The following routine leverages build_ustar_entry_name() above and * so is simpler than you might think. It just needs to provide the * additional path element and handle a few pathological cases). */ static char * build_pax_attribute_name(char *dest, const char *src) { char buff[64]; const char *p; /* Handle the null filename case. */ if (src == NULL || *src == '\0') { strcpy(dest, "PaxHeader/blank"); return (dest); } /* Prune final '/' and other unwanted final elements. */ p = src + strlen(src); for (;;) { /* Ends in "/", remove the '/' */ if (p > src && p[-1] == '/') { --p; continue; } /* Ends in "/.", remove the '.' */ if (p > src + 1 && p[-1] == '.' && p[-2] == '/') { --p; continue; } break; } /* Pathological case: After above, there was nothing left. * This includes "/." "/./." "/.//./." etc. */ if (p == src) { strcpy(dest, "/PaxHeader/rootdir"); return (dest); } /* Convert unadorned "." into a suitable filename. */ if (*src == '.' && p == src + 1) { strcpy(dest, "PaxHeader/currentdir"); return (dest); } /* * TODO: Push this string into the 'pax' structure to avoid * recomputing it every time. That will also open the door * to having clients override it. */ #if HAVE_GETPID && 0 /* Disable this for now; see above comment. */ snprintf(buff, sizeof(buff), "PaxHeader.%d", getpid()); #else /* If the platform can't fetch the pid, don't include it. */ strcpy(buff, "PaxHeader"); #endif /* General case: build a ustar-compatible name adding * "/PaxHeader/". */ build_ustar_entry_name(dest, src, p - src, buff); return (dest); } /* * GNU PAX Format 1.0 requires the special name, which pattern is: * /GNUSparseFile./ * * Since reproducible archives are more important, use 0 as pid. * * This function is used for only Sparse file, a file type of which * is regular file. */ static char * build_gnu_sparse_name(char *dest, const char *src) { const char *p; /* Handle the null filename case. */ if (src == NULL || *src == '\0') { strcpy(dest, "GNUSparseFile/blank"); return (dest); } /* Prune final '/' and other unwanted final elements. */ p = src + strlen(src); for (;;) { /* Ends in "/", remove the '/' */ if (p > src && p[-1] == '/') { --p; continue; } /* Ends in "/.", remove the '.' */ if (p > src + 1 && p[-1] == '.' && p[-2] == '/') { --p; continue; } break; } /* General case: build a ustar-compatible name adding * "/GNUSparseFile/". */ build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0"); return (dest); } /* Write two null blocks for the end of archive */ static int archive_write_pax_close(struct archive_write *a) { return (__archive_write_nulls(a, 512 * 2)); } static int archive_write_pax_free(struct archive_write *a) { struct pax *pax; pax = (struct pax *)a->format_data; if (pax == NULL) return (ARCHIVE_OK); archive_string_free(&pax->pax_header); archive_string_free(&pax->sparse_map); archive_string_free(&pax->l_url_encoded_name); sparse_list_clear(pax); free(pax); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_pax_finish_entry(struct archive_write *a) { struct pax *pax; uint64_t remaining; int ret; pax = (struct pax *)a->format_data; remaining = pax->entry_bytes_remaining; if (remaining == 0) { while (pax->sparse_list) { struct sparse_block *sb; if (!pax->sparse_list->is_hole) remaining += pax->sparse_list->remaining; sb = pax->sparse_list->next; free(pax->sparse_list); pax->sparse_list = sb; } } ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding)); pax->entry_bytes_remaining = pax->entry_padding = 0; return (ret); } static ssize_t archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) { struct pax *pax; size_t ws; size_t total; int ret; pax = (struct pax *)a->format_data; /* * According to GNU PAX format 1.0, write a sparse map * before the body. */ if (archive_strlen(&(pax->sparse_map))) { ret = __archive_write_output(a, pax->sparse_map.s, archive_strlen(&(pax->sparse_map))); if (ret != ARCHIVE_OK) return (ret); ret = __archive_write_nulls(a, pax->sparse_map_padding); if (ret != ARCHIVE_OK) return (ret); archive_string_empty(&(pax->sparse_map)); } total = 0; while (total < s) { const unsigned char *p; while (pax->sparse_list != NULL && pax->sparse_list->remaining == 0) { struct sparse_block *sb = pax->sparse_list->next; free(pax->sparse_list); pax->sparse_list = sb; } if (pax->sparse_list == NULL) return (total); p = ((const unsigned char *)buff) + total; ws = s - total; if (ws > pax->sparse_list->remaining) ws = (size_t)pax->sparse_list->remaining; if (pax->sparse_list->is_hole) { /* Current block is hole thus we do not write * the body. */ pax->sparse_list->remaining -= ws; total += ws; continue; } ret = __archive_write_output(a, p, ws); pax->sparse_list->remaining -= ws; total += ws; if (ret != ARCHIVE_OK) return (ret); } return (total); } static int has_non_ASCII(const char *_p) { const unsigned char *p = (const unsigned char *)_p; if (p == NULL) return (1); while (*p != '\0' && *p < 128) p++; return (*p != '\0'); } /* * Used by extended attribute support; encodes the name * so that there will be no '=' characters in the result. */ static char * url_encode(const char *in) { const char *s; char *d; size_t out_len = 0; char *out; for (s = in; *s != '\0'; s++) { if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { if (SIZE_MAX - out_len < 4) return (NULL); out_len += 3; } else { if (SIZE_MAX - out_len < 2) return (NULL); out_len++; } } out = (char *)malloc(out_len + 1); if (out == NULL) return (NULL); for (s = in, d = out; *s != '\0'; s++) { /* encode any non-printable ASCII character or '%' or '=' */ if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { /* URL encoding is '%' followed by two hex digits */ *d++ = '%'; *d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)]; *d++ = "0123456789ABCDEF"[0x0f & *s]; } else { *d++ = *s; } } *d = '\0'; return (out); } /* * Encode a sequence of bytes into a C string using base-64 encoding. * * Returns a null-terminated C string allocated with malloc(); caller * is responsible for freeing the result. */ static char * base64_encode(const char *s, size_t len) { static const char digits[64] = { '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','0','1','2','3','4','5','6','7', '8','9','+','/' }; int v; char *d, *out; /* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */ out = (char *)malloc((len * 4 + 2) / 3 + 1); if (out == NULL) return (NULL); d = out; /* Convert each group of 3 bytes into 4 characters. */ while (len >= 3) { v = (((int)s[0] << 16) & 0xff0000) | (((int)s[1] << 8) & 0xff00) | (((int)s[2]) & 0x00ff); s += 3; len -= 3; *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; *d++ = digits[(v >> 6) & 0x3f]; *d++ = digits[(v) & 0x3f]; } /* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */ switch (len) { case 0: break; case 1: v = (((int)s[0] << 16) & 0xff0000); *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; break; case 2: v = (((int)s[0] << 16) & 0xff0000) | (((int)s[1] << 8) & 0xff00); *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; *d++ = digits[(v >> 6) & 0x3f]; break; } /* Add trailing NUL character so output is a valid C string. */ *d = '\0'; return (out); } static void sparse_list_clear(struct pax *pax) { while (pax->sparse_list != NULL) { struct sparse_block *sb = pax->sparse_list; pax->sparse_list = sb->next; free(sb); } pax->sparse_tail = NULL; } static int _sparse_list_add_block(struct pax *pax, int64_t offset, int64_t length, int is_hole) { struct sparse_block *sb; sb = (struct sparse_block *)malloc(sizeof(*sb)); if (sb == NULL) return (ARCHIVE_FATAL); sb->next = NULL; sb->is_hole = is_hole; sb->offset = offset; sb->remaining = length; if (pax->sparse_list == NULL || pax->sparse_tail == NULL) pax->sparse_list = pax->sparse_tail = sb; else { pax->sparse_tail->next = sb; pax->sparse_tail = sb; } return (ARCHIVE_OK); } static int sparse_list_add(struct pax *pax, int64_t offset, int64_t length) { int64_t last_offset; int r; if (pax->sparse_tail == NULL) last_offset = 0; else { last_offset = pax->sparse_tail->offset + pax->sparse_tail->remaining; } if (last_offset < offset) { /* Add a hole block. */ r = _sparse_list_add_block(pax, last_offset, offset - last_offset, 1); if (r != ARCHIVE_OK) return (r); } /* Add data block. */ return (_sparse_list_add_block(pax, offset, length, 0)); } +static time_t +get_ustar_max_mtime(void) +{ + /* + * Technically, the mtime field in the ustar header can + * support 33 bits. We are using all of them to keep + * tar/test/test_option_C_mtree.c simple and passing after 2038. + * For platforms that use signed 32-bit time values we + * use the 32-bit maximum. + */ + if (sizeof(time_t) > sizeof(int32_t)) + return (time_t)0x1ffffffff; + else + return (time_t)0x7fffffff; +} diff --git a/contrib/libarchive/libarchive/archive_write_set_format_warc.c b/contrib/libarchive/libarchive/archive_write_set_format_warc.c index 46b05734121c..0ef003e2fc94 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_warc.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_warc.c @@ -1,453 +1,444 @@ /*- * Copyright (c) 2014 Sebastian Freundt * Author: 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$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" struct warc_s { unsigned int omit_warcinfo:1; time_t now; mode_t typ; unsigned int rng; /* populated size */ uint64_t populz; }; static const char warcinfo[] = "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n" "format: WARC file version 1.0\r\n"; 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 { warc_type_t type; const char *tgturi; const char *recid; time_t rtime; time_t mtime; const char *cnttyp; uint64_t cntlen; } warc_essential_hdr_t; typedef struct { unsigned int u[4U]; } warc_uuid_t; static int _warc_options(struct archive_write*, const char *key, const char *v); static int _warc_header(struct archive_write *a, struct archive_entry *entry); static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz); static int _warc_finish_entry(struct archive_write *a); static int _warc_close(struct archive_write *a); static int _warc_free(struct archive_write *a); /* private routines */ static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t); static int _gen_uuid(warc_uuid_t *tgt); /* * Set output format to ISO 28500 (aka WARC) format. */ int archive_write_set_format_warc(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct warc_s *w; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_warc"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) { (a->format_free)(a); } w = malloc(sizeof(*w)); if (w == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } /* by default we're emitting a file wide header */ w->omit_warcinfo = 0U; /* obtain current time for date fields */ w->now = time(NULL); /* reset file type info */ w->typ = 0; /* also initialise our rng */ w->rng = (unsigned int)w->now; a->format_data = w; a->format_name = "WARC/1.0"; a->format_options = _warc_options; a->format_write_header = _warc_header; a->format_write_data = _warc_data; a->format_close = _warc_close; a->format_free = _warc_free; a->format_finish_entry = _warc_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_WARC; a->archive.archive_format_name = "WARC/1.0"; return (ARCHIVE_OK); } /* archive methods */ static int _warc_options(struct archive_write *a, const char *key, const char *val) { struct warc_s *w = a->format_data; if (strcmp(key, "omit-warcinfo") == 0) { if (val == NULL || strcmp(val, "true") == 0) { /* great */ w->omit_warcinfo = 1U; 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 _warc_header(struct archive_write *a, struct archive_entry *entry) { struct warc_s *w = a->format_data; struct archive_string hdr; #define MAX_HDR_SIZE 512 /* check whether warcinfo record needs outputting */ if (!w->omit_warcinfo) { ssize_t r; warc_essential_hdr_t wi = { WT_INFO, /*uri*/NULL, /*urn*/NULL, /*rtm*/0, /*mtm*/0, /*cty*/"application/warc-fields", /*len*/sizeof(warcinfo) - 1U, }; wi.rtime = w->now; wi.mtime = w->now; archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi); if (r >= 0) { /* jackpot! */ /* now also use HDR buffer for the actual warcinfo */ archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1); /* append end-of-record indicator */ archive_strncat(&hdr, "\r\n\r\n", 4); /* write to output stream */ __archive_write_output(a, hdr.s, archive_strlen(&hdr)); } /* indicate we're done with file header writing */ w->omit_warcinfo = 1U; archive_string_free(&hdr); } if (archive_entry_pathname(entry) == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } w->typ = archive_entry_filetype(entry); w->populz = 0U; if (w->typ == AE_IFREG) { warc_essential_hdr_t rh = { WT_RSRC, /*uri*/NULL, /*urn*/NULL, /*rtm*/0, /*mtm*/0, /*cty*/NULL, /*len*/0, }; ssize_t r; rh.tgturi = archive_entry_pathname(entry); rh.rtime = w->now; rh.mtime = archive_entry_mtime(entry); rh.cntlen = (size_t)archive_entry_size(entry); archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh); if (r < 0) { /* don't bother */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cannot archive file"); return (ARCHIVE_WARN); } /* otherwise append to output stream */ __archive_write_output(a, hdr.s, r); /* and let subsequent calls to _data() know about the size */ w->populz = rh.cntlen; archive_string_free(&hdr); return (ARCHIVE_OK); } /* just resort to erroring as per Tim's advice */ __archive_write_entry_filetype_unsupported( &a->archive, entry, "WARC"); return (ARCHIVE_FAILED); } static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t len) { struct warc_s *w = a->format_data; if (w->typ == AE_IFREG) { int rc; /* never write more bytes than announced */ if (len > w->populz) { len = (size_t)w->populz; } /* now then, out we put the whole shebang */ rc = __archive_write_output(a, buf, len); if (rc != ARCHIVE_OK) { return rc; } } return len; } static int _warc_finish_entry(struct archive_write *a) { static const char _eor[] = "\r\n\r\n"; struct warc_s *w = a->format_data; if (w->typ == AE_IFREG) { int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U); if (rc != ARCHIVE_OK) { return rc; } } /* reset type info */ w->typ = 0; return (ARCHIVE_OK); } static int _warc_close(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int _warc_free(struct archive_write *a) { struct warc_s *w = a->format_data; free(w); a->format_data = NULL; return (ARCHIVE_OK); } /* private routines */ static void xstrftime(struct archive_string *as, const char *fmt, time_t t) { /** like strftime(3) but for time_t objects */ struct tm *rt; -#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) struct tm timeHere; -#endif -#if defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; #endif char strtime[100]; size_t len; -#ifdef HAVE_GMTIME_R - if ((rt = gmtime_r(&t, &timeHere)) == NULL) - return; -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - terr = _gmtime64_s(&timeHere, &tmptime); - if (terr) - rt = NULL; - else - rt = &timeHere; +#if defined(HAVE_GMTIME_S) + rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere; +#elif defined(HAVE_GMTIME_R) + rt = gmtime_r(&t, &timeHere); #else - if ((rt = gmtime(&t)) == NULL) - return; + rt = gmtime(&t); #endif + if (!rt) + return; /* leave the hard yacker to our role model strftime() */ len = strftime(strtime, sizeof(strtime)-1, fmt, rt); archive_strncat(as, strtime, len); } static ssize_t _popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr) { static const char _ver[] = "WARC/1.0\r\n"; static const char * const _typ[LAST_WT] = { NULL, "warcinfo", "metadata", "resource", NULL }; char std_uuid[48U]; if (hdr.type == WT_NONE || hdr.type > WT_RSRC) { /* brilliant, how exactly did we get here? */ return -1; } archive_strcpy(tgt, _ver); archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]); if (hdr.tgturi != NULL) { /* check if there's a xyz:// */ static const char _uri[] = ""; static const char _fil[] = "file://"; const char *u; char *chk = strchr(hdr.tgturi, ':'); if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') { /* yep, it's definitely a URI */ u = _uri; } else { /* hm, best to prepend file:// then */ u = _fil; } archive_string_sprintf(tgt, "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi); } /* record time is usually when the http is sent off, * just treat the archive writing as such for a moment */ xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime); /* while we're at it, record the mtime */ xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime); if (hdr.recid == NULL) { /* generate one, grrrr */ warc_uuid_t u; _gen_uuid(&u); /* Unfortunately, archive_string_sprintf does not * handle the minimum number following '%'. * So we have to use snprintf function here instead * of archive_string_snprintf function. */ #if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900) #define snprintf _snprintf #endif snprintf( std_uuid, sizeof(std_uuid), "", u.u[0U], u.u[1U] >> 16U, u.u[1U] & 0xffffU, u.u[2U] >> 16U, u.u[2U] & 0xffffU, u.u[3U]); hdr.recid = std_uuid; } /* record-id is mandatory, fingers crossed we won't fail */ archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid); if (hdr.cnttyp != NULL) { archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp); } /* next one is mandatory */ archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen); /**/ archive_strncat(tgt, "\r\n", 2); return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt); } static int _gen_uuid(warc_uuid_t *tgt) { archive_random(tgt->u, sizeof(tgt->u)); /* obey uuid version 4 rules */ tgt->u[1U] &= 0xffff0fffU; tgt->u[1U] |= 0x4000U; tgt->u[2U] &= 0x3fffffffU; tgt->u[2U] |= 0x80000000U; return 0; } /* archive_write_set_format_warc.c ends here */ diff --git a/contrib/libarchive/libarchive/archive_write_set_format_xar.c b/contrib/libarchive/libarchive/archive_write_set_format_xar.c index 9d5e5dd41cbb..7307757d37a1 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_xar.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_xar.c @@ -1,3259 +1,3255 @@ /*- * 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; unsigned int virtual:1; unsigned 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) +#if defined(HAVE_GMTIME_S) + gmtime_s(&tm, &t); +#elif 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 > 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 */ diff --git a/contrib/libarchive/libarchive/archive_write_set_format_zip.c b/contrib/libarchive/libarchive/archive_write_set_format_zip.c index 1b48ecf9d63c..84b7d8b14e8f 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_zip.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_zip.c @@ -1,1704 +1,1693 @@ /*- * 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. Use the default * compression unless specified otherwise. * We enable Zip64 extensions unless we're told not to. */ zip->entry_compression = zip->requested_compression; if(zip->entry_compression == COMPRESSION_UNSPECIFIED){ 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) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_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) +#if defined(HAVE_LOCALTIME_S) + t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf; +#elif 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); } diff --git a/contrib/libarchive/libarchive/test/test_fuzz.c b/contrib/libarchive/libarchive/test/test_fuzz.c index ba3a553db987..7f85cbb04b35 100644 --- a/contrib/libarchive/libarchive/test/test_fuzz.c +++ b/contrib/libarchive/libarchive/test/test_fuzz.c @@ -1,645 +1,644 @@ /*- * 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$"); /* * This was inspired by an ISO fuzz tester written by Michal Zalewski * and posted to the "vulnwatch" mailing list on March 17, 2005: * http://seclists.org/vulnwatch/2005/q1/0088.html * * This test simply reads each archive image into memory, pokes * random values into it and runs it through libarchive. It tries * to damage about 1% of each file and repeats the exercise 100 times * with each file. * * Unlike most other tests, this test does not verify libarchive's * responses other than to ensure that libarchive doesn't crash. * * Due to the deliberately random nature of this test, it may be hard * to reproduce failures. Because this test deliberately attempts to * induce crashes, there's little that can be done in the way of * post-failure diagnostics. */ /* Because this works for any archive, we can just re-use the archives * developed for other tests. */ struct files { int uncompress; /* If 1, decompress the file before fuzzing. */ const char **names; }; static void test_fuzz(const struct files *filesets) { const void *blk; size_t blk_size; int64_t blk_offset; int n; const char *skip_fuzz_tests; skip_fuzz_tests = getenv("SKIP_TEST_FUZZ"); if (skip_fuzz_tests != NULL) { skipping("Skipping fuzz tests due to SKIP_TEST_FUZZ " "environment variable"); return; } for (n = 0; filesets[n].names != NULL; ++n) { const size_t buffsize = 30000000; struct archive_entry *ae; struct archive *a; char *rawimage = NULL, *image = NULL, *tmp = NULL; size_t size = 0, oldsize = 0; int i, q; extract_reference_files(filesets[n].names); if (filesets[n].uncompress) { int r; /* Use format_raw to decompress the data. */ 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)); r = archive_read_open_filenames(a, filesets[n].names, 16384); if (r != ARCHIVE_OK) { archive_read_free(a); if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) { skipping("Cannot uncompress fileset"); } else { skipping("Cannot uncompress %s", filesets[n].names[0]); } continue; } assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); rawimage = malloc(buffsize); size = archive_read_data(a, rawimage, buffsize); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); assert(size > 0); if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) { failure("Internal buffer is not big enough for " "uncompressed test files"); } else { failure("Internal buffer is not big enough for " "uncompressed test file: %s", filesets[n].names[0]); } if (!assert(size < buffsize)) { free(rawimage); rawimage = NULL; continue; } } else { for (i = 0; filesets[n].names[i] != NULL; ++i) { char *newraw; tmp = slurpfile(&size, "%s", filesets[n].names[i]); newraw = realloc(rawimage, oldsize + size); if (!assert(newraw != NULL)) { - free(rawimage); - rawimage = NULL; free(tmp); - continue; + size = 0; + break; } rawimage = newraw; memcpy(rawimage + oldsize, tmp, size); oldsize += size; size = oldsize; free(tmp); } } if (size == 0) { free(rawimage); rawimage = NULL; continue; } image = malloc(size); assert(image != NULL); if (image == NULL) { free(rawimage); rawimage = NULL; return; } assert(rawimage != NULL); srand((unsigned)time(NULL)); for (i = 0; i < 1000; ++i) { FILE *f; int j, numbytes, trycnt; /* Fuzz < 1% of the bytes in the archive. */ memcpy(image, rawimage, size); q = (int)size / 100; if (q < 4) q = 4; numbytes = (int)(rand() % q); for (j = 0; j < numbytes; ++j) image[rand() % size] = (char)rand(); /* Save the messed-up image to a file. * If we crash, that file will be useful. */ for (trycnt = 0; trycnt < 3; trycnt++) { f = fopen("after.test.failure.send.this.file." "to.libarchive.maintainers.with.system.details", "wb"); if (f != NULL) break; #if defined(_WIN32) && !defined(__CYGWIN__) /* * Sometimes previous close operation does not completely * end at this time. So we should take a wait while * the operation running. */ Sleep(100); #endif } assert(f != NULL); assertEqualInt((size_t)size, fwrite(image, 1, (size_t)size, f)); fclose(f); // Try to read all headers and bodies. 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)); if (0 == archive_read_open_memory(a, image, size)) { while(0 == archive_read_next_header(a, &ae)) { while (0 == archive_read_data_block(a, &blk, &blk_size, &blk_offset)) continue; } archive_read_close(a); } archive_read_free(a); // Just list headers, skip bodies. 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)); if (0 == archive_read_open_memory(a, image, size)) { while(0 == archive_read_next_header(a, &ae)) { } archive_read_close(a); } archive_read_free(a); } free(image); free(rawimage); } } DEFINE_TEST(test_fuzz_ar) { static const char *fileset1[] = { "test_read_format_ar.ar", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_cab) { static const char *fileset1[] = { "test_fuzz.cab", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_cpio) { static const char *fileset1[] = { "test_read_format_cpio_bin_be.cpio", NULL }; static const char *fileset2[] = { "test_read_format_cpio_bin_le.cpio", NULL }; static const char *fileset3[] = { /* Test RPM unwrapper */ "test_read_format_cpio_svr4_gzip_rpm.rpm", NULL }; static const struct files filesets[] = { {0, fileset1}, {0, fileset2}, {0, fileset3}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_iso9660) { static const char *fileset1[] = { "test_fuzz_1.iso.Z", NULL }; static const struct files filesets[] = { {0, fileset1}, /* Exercise compress decompressor. */ {1, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_lzh) { static const char *fileset1[] = { "test_fuzz.lzh", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_mtree) { static const char *fileset1[] = { "test_read_format_mtree.mtree", NULL }; static const struct files filesets[] = { {0, fileset1}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_rar) { static const char *fileset1[] = { /* Uncompressed RAR test */ "test_read_format_rar.rar", NULL }; static const char *fileset2[] = { /* RAR file with binary data */ "test_read_format_rar_binary_data.rar", NULL }; static const char *fileset3[] = { /* Best Compressed RAR test */ "test_read_format_rar_compress_best.rar", NULL }; static const char *fileset4[] = { /* Normal Compressed RAR test */ "test_read_format_rar_compress_normal.rar", NULL }; static const char *fileset5[] = { /* Normal Compressed Multi LZSS blocks RAR test */ "test_read_format_rar_multi_lzss_blocks.rar", NULL }; static const char *fileset6[] = { /* RAR with no EOF header */ "test_read_format_rar_noeof.rar", NULL }; static const char *fileset7[] = { /* Best Compressed RAR file with both PPMd and LZSS blocks */ "test_read_format_rar_ppmd_lzss_conversion.rar", NULL }; static const char *fileset8[] = { /* RAR with subblocks */ "test_read_format_rar_subblock.rar", NULL }; static const char *fileset9[] = { /* RAR with Unicode filenames */ "test_read_format_rar_unicode.rar", NULL }; static const char *fileset10[] = { "test_read_format_rar_multivolume.part0001.rar", "test_read_format_rar_multivolume.part0002.rar", "test_read_format_rar_multivolume.part0003.rar", "test_read_format_rar_multivolume.part0004.rar", NULL }; static const struct files filesets[] = { {0, fileset1}, {0, fileset2}, {0, fileset3}, {0, fileset4}, {0, fileset5}, {0, fileset6}, {0, fileset7}, {0, fileset8}, {0, fileset9}, {0, fileset10}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_tar) { static const char *fileset1[] = { "test_compat_bzip2_1.tbz", NULL }; static const char *fileset2[] = { "test_compat_gtar_1.tar", NULL }; static const char *fileset3[] = { "test_compat_gzip_1.tgz", NULL }; static const char *fileset4[] = { "test_compat_gzip_2.tgz", NULL }; static const char *fileset5[] = { "test_compat_tar_hardlink_1.tar", NULL }; static const char *fileset6[] = { "test_compat_xz_1.txz", NULL }; static const char *fileset7[] = { "test_read_format_gtar_sparse_1_17_posix10_modified.tar", NULL }; static const char *fileset8[] = { "test_read_format_tar_empty_filename.tar", NULL }; #if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H static const char *fileset9[] = { "test_compat_lzop_1.tar.lzo", NULL }; #endif #if HAVE_ZSTD_H && HAVE_LIBZSTD static const char *fileset10[] = { "test_compat_zstd_1.tar.zst", NULL }; #endif static const char *fileset11[] = { "test_compat_tar_directory_1.tar", NULL }; static const struct files filesets[] = { {0, fileset1}, /* Exercise bzip2 decompressor. */ {1, fileset1}, {0, fileset2}, {0, fileset3}, /* Exercise gzip decompressor. */ {0, fileset4}, /* Exercise gzip decompressor. */ {0, fileset5}, {0, fileset6}, /* Exercise xz decompressor. */ {0, fileset7}, {0, fileset8}, #if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H {0, fileset9}, /* Exercise lzo decompressor. */ #endif #if HAVE_ZSTD_H && HAVE_LIBZSTD {0, fileset10}, /* Exercise zstd decompressor. */ #endif {0, fileset11}, {1, NULL} }; test_fuzz(filesets); } DEFINE_TEST(test_fuzz_zip) { static const char *fileset1[] = { "test_compat_zip_1.zip", NULL }; static const char *fileset2[] = { "test_compat_zip_2.zip", NULL }; static const char *fileset3[] = { "test_compat_zip_3.zip", NULL }; static const char *fileset4[] = { "test_compat_zip_4.zip", NULL }; static const char *fileset5[] = { "test_compat_zip_5.zip", NULL }; static const char *fileset6[] = { "test_compat_zip_6.zip", NULL }; static const char *fileset7[] = { "test_read_format_zip.zip", NULL }; static const char *fileset8[] = { "test_read_format_zip_comment_stored_1.zip", NULL }; static const char *fileset9[] = { "test_read_format_zip_comment_stored_2.zip", NULL }; static const char *fileset10[] = { "test_read_format_zip_encryption_data.zip", NULL }; static const char *fileset11[] = { "test_read_format_zip_encryption_header.zip", NULL }; static const char *fileset12[] = { "test_read_format_zip_encryption_partially.zip", NULL }; static const char *fileset13[] = { "test_read_format_zip_filename_cp866.zip", NULL }; static const char *fileset14[] = { "test_read_format_zip_filename_cp932.zip", NULL }; static const char *fileset15[] = { "test_read_format_zip_filename_koi8r.zip", NULL }; static const char *fileset16[] = { "test_read_format_zip_filename_utf8_jp.zip", NULL }; static const char *fileset17[] = { "test_read_format_zip_filename_utf8_ru.zip", NULL }; static const char *fileset18[] = { "test_read_format_zip_filename_utf8_ru2.zip", NULL }; static const char *fileset19[] = { "test_read_format_zip_length_at_end.zip", NULL }; static const char *fileset20[] = { "test_read_format_zip_mac_metadata.zip", NULL }; static const char *fileset21[] = { "test_read_format_zip_malformed1.zip", NULL }; static const char *fileset22[] = { "test_read_format_zip_msdos.zip", NULL }; static const char *fileset23[] = { "test_read_format_zip_nested.zip", NULL }; static const char *fileset24[] = { "test_read_format_zip_nofiletype.zip", NULL }; static const char *fileset25[] = { "test_read_format_zip_padded1.zip", NULL }; static const char *fileset26[] = { "test_read_format_zip_padded2.zip", NULL }; static const char *fileset27[] = { "test_read_format_zip_padded3.zip", NULL }; static const char *fileset28[] = { "test_read_format_zip_symlink.zip", NULL }; static const char *fileset29[] = { "test_read_format_zip_traditional_encryption_data.zip", NULL }; static const char *fileset30[] = { "test_read_format_zip_ux.zip", NULL }; static const char *fileset31[] = { "test_read_format_zip_winzip_aes128.zip", NULL }; static const char *fileset32[] = { "test_read_format_zip_winzip_aes256.zip", NULL }; static const char *fileset33[] = { "test_read_format_zip_winzip_aes256_large.zip", NULL }; static const char *fileset34[] = { "test_read_format_zip_winzip_aes256_stored.zip", NULL }; static const char *fileset35[] = { "test_read_format_zip_zip64a.zip", NULL }; static const char *fileset36[] = { "test_read_format_zip_zip64b.zip", NULL }; static const struct files filesets[] = { {0, fileset1}, {0, fileset2}, {0, fileset3}, {0, fileset4}, {0, fileset5}, {0, fileset6}, {0, fileset7}, {0, fileset8}, {0, fileset9}, {0, fileset10}, {0, fileset11}, {0, fileset12}, {0, fileset13}, {0, fileset14}, {0, fileset15}, {0, fileset16}, {0, fileset17}, {0, fileset18}, {0, fileset19}, {0, fileset20}, {0, fileset21}, {0, fileset22}, {0, fileset23}, {0, fileset24}, {0, fileset25}, {0, fileset26}, {0, fileset27}, {0, fileset28}, {0, fileset29}, {0, fileset30}, {0, fileset31}, {0, fileset32}, {0, fileset33}, {0, fileset34}, {0, fileset35}, {0, fileset36}, {1, NULL} }; test_fuzz(filesets); } diff --git a/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c b/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c index bca3ce518e21..ad99acfef258 100644 --- a/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c +++ b/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c @@ -1,1869 +1,1876 @@ /*- * 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. */ #if defined(PATH_MAX) && !defined(__GLIBC__) 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"); #if defined(PATH_MAX) && !defined(__GLIBC__) 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; const char *skip_test_restore_atime; skip_test_restore_atime = getenv("SKIP_TEST_RESTORE_ATIME"); if (skip_test_restore_atime != NULL) { skipping("Skipping restore atime tests due to " "SKIP_TEST_RESTORE_ATIME environment variable"); return; } 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; +#if defined(O_PATH) || defined(O_SEARCH) || \ + (defined(__FreeBSD__) && defined(O_EXEC)) + const char *ignore_traversals_test4; + + ignore_traversals_test4 = getenv("IGNORE_TRAVERSALS_TEST4"); +#endif 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); + if (ignore_traversals_test4 == NULL) + 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(); } diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip.c b/contrib/libarchive/libarchive/test/test_read_format_7zip.c index 3c72595aeef7..1eca3936e5b4 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_7zip.c +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip.c @@ -1,838 +1,1184 @@ /*- * 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"); #if defined(_WIN32) && !defined(__CYGWIN__) #define close _close #define open _open #endif +#define __LIBARCHIVE_BUILD +#include + /* * Extract a non-encoded file. * The header of the 7z archive files is not encoded. */ static void test_copy(int use_open_fd) { const char *refname = "test_read_format_7zip_copy.7z"; struct archive_entry *ae; struct archive *a; char buff[128]; int fd = -1; extract_reference_file(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)); if (use_open_fd) { fd = open(refname, O_RDONLY | O_BINARY); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_fd(a, fd, 10240)); } else { assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); } /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0666), archive_entry_mode(ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(60, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assert(archive_read_has_encrypted_entries(a) > ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); assertEqualInt(60, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, " ", 4); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); if (fd != -1) close(fd); } /* * An archive file has no entry. */ static void test_empty_archive(void) { const char *refname = "test_read_format_7zip_empty_archive.7z"; struct archive_entry *ae; struct archive *a; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(0, archive_file_count(a)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * An archive file has one empty file. It means there is no content * in the archive file except for a header. */ static void test_empty_file(void) { const char *refname = "test_read_format_7zip_empty_file.7z"; struct archive_entry *ae; struct archive *a; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular empty. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("empty", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Extract an encoded file. * The header of the 7z archive files is not encoded. */ static void test_plain_header(const char *refname) { struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1322058763, archive_entry_mtime(ae)); assertEqualInt(2844, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "The libarchive distribution ", 28); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Extract multi files. * The header of the 7z archive files is encoded with LZMA. */ static void test_extract_all_files(const char *refname) { struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("dir1/file1", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(13, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(13, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\n", 13); /* Verify regular file2. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(26, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(26, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26); /* Verify regular file3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file3", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(39, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(39, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39); /* Verify regular file4. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file4", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(52, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); /* Verify directory dir1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFDIR | 0755), archive_entry_mode(ae)); assertEqualString("dir1/", archive_entry_pathname(ae)); assertEqualInt(2764801, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(5, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +/* + * Extract multi files. + * Like test_extract_all_files, but with zstandard compression. + */ +static void +test_extract_all_files_zstd(const char *refname) +{ + struct archive_entry *ae; + struct archive *a; + char buff[128]; + + extract_reference_file(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, + archive_read_open_filename(a, refname, 10240)); + + /* Verify directory dir1. Note that this comes before the dir1/file1 entry in recent versions of 7-Zip. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFDIR | 0755), archive_entry_mode(ae)); + assertEqualString("dir1/", archive_entry_pathname(ae)); + assertEqualInt(2764801, archive_entry_mtime(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); + + /* Verify regular file1. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); + assertEqualString("dir1/file1", archive_entry_pathname(ae)); + assertEqualInt(86401, archive_entry_mtime(ae)); + assertEqualInt(13, archive_entry_size(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); + assertEqualInt(13, archive_read_data(a, buff, sizeof(buff))); + assertEqualMem(buff, "aaaaaaaaaaaa\n", 13); + + /* Verify regular file2. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); + assertEqualString("file2", archive_entry_pathname(ae)); + assertEqualInt(86401, archive_entry_mtime(ae)); + assertEqualInt(26, archive_entry_size(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); + assertEqualInt(26, archive_read_data(a, buff, sizeof(buff))); + assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26); + + /* Verify regular file3. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); + assertEqualString("file3", archive_entry_pathname(ae)); + assertEqualInt(86401, archive_entry_mtime(ae)); + assertEqualInt(39, archive_entry_size(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); + assertEqualInt(39, archive_read_data(a, buff, sizeof(buff))); + assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39); + + /* Verify regular file4. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); + assertEqualString("file4", archive_entry_pathname(ae)); + assertEqualInt(86401, archive_entry_mtime(ae)); + assertEqualInt(52, archive_entry_size(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); + assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); + assertEqualMem(buff, + "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); + + assertEqualInt(5, archive_file_count(a)); + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* + * Extract file from an archives using ZSTD compression with and without BCJ. + */ +static void +test_extract_file_zstd_bcj_nobjc(const char *refname) +{ + struct archive_entry *ae; + struct archive *a; + char buff[4096]; + uint32_t computed_crc = 0; + uint32_t expected_crc = 0xbd66eebc; + + extract_reference_file(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, + archive_read_open_filename(a, refname, 10240)); + + /* Verify regular file: hw. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0775), archive_entry_mode(ae)); + assertEqualString("hw", archive_entry_pathname(ae)); + assertEqualInt(1685913368, archive_entry_mtime(ae)); + assertEqualInt(15952, archive_entry_size(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); + + for (;;) { + la_ssize_t bytes_read = archive_read_data(a, buff, sizeof(buff)); + assert(bytes_read >= 0); + if (bytes_read == 0) break; + computed_crc = crc32(computed_crc, buff, bytes_read); + } + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + /* * Extract last file. * The header of the 7z archive files is encoded with LZMA. */ static void test_extract_last_file(const char *refname) { struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("dir1/file1", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(13, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Verify regular file2. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(26, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Verify regular file3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file3", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(39, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); /* Verify regular file4. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file4", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(52, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); /* Verify directory dir1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFDIR | 0755), archive_entry_mode(ae)); assertEqualString("dir1/", archive_entry_pathname(ae)); assertEqualInt(2764801, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(5, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Extract a mixed archive file which has both LZMA and LZMA2 encoded files. * LZMA: file1, file2, file3, file4 * LZMA2: zfile1, zfile2, zfile3, zfile4 */ static void test_extract_all_files2(const char *refname) { struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("dir1/file1", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(13, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(13, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\n", 13); /* Verify regular file2. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(26, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(26, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26); /* Verify regular file3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file3", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(39, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(39, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39); /* Verify regular file4. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file4", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(52, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); /* Verify regular zfile1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("dir1/zfile1", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(13, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(13, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\n", 13); /* Verify regular zfile2. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("zfile2", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(26, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(26, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26); /* Verify regular zfile3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("zfile3", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(39, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(39, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39); /* Verify regular zfile4. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("zfile4", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(52, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); /* Verify directory dir1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFDIR | 0755), archive_entry_mode(ae)); assertEqualString("dir1/", archive_entry_pathname(ae)); assertEqualInt(2764801, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(9, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Extract a file compressed with DELTA + LZMA[12]. */ static void test_delta_lzma(const char *refname) { struct archive_entry *ae; struct archive *a; size_t remaining; ssize_t bytes; char buff[1024]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(172802, archive_entry_mtime(ae)); assertEqualInt(27627, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); remaining = (size_t)archive_entry_size(ae); while (remaining) { if (remaining < sizeof(buff)) assertEqualInt(remaining, bytes = archive_read_data(a, buff, sizeof(buff))); else assertEqualInt(sizeof(buff), bytes = archive_read_data(a, buff, sizeof(buff))); if (bytes > 0) remaining -= bytes; else break; } assertEqualInt(0, remaining); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Extract a file compressed with BCJ + LZMA2. */ static void test_bcj(const char *refname) { struct archive_entry *ae; struct archive *a; size_t remaining; ssize_t bytes; char buff[1024]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular x86exe. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0444), archive_entry_mode(ae) & ~0111); assertEqualString("x86exe", archive_entry_pathname(ae)); assertEqualInt(172802, archive_entry_mtime(ae)); assertEqualInt(27328, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); remaining = (size_t)archive_entry_size(ae); while (remaining) { if (remaining < sizeof(buff)) assertEqualInt(remaining, bytes = archive_read_data(a, buff, sizeof(buff))); else assertEqualInt(sizeof(buff), bytes = archive_read_data(a, buff, sizeof(buff))); if (bytes > 0) remaining -= bytes; else break; } assertEqualInt(0, remaining); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } /* * Extract a file compressed with PPMd. */ static void test_ppmd(void) { const char *refname = "test_read_format_7zip_ppmd.7z"; struct archive_entry *ae; struct archive *a; size_t remaining; ssize_t bytes; char buff[1024]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0666), archive_entry_mode(ae)); assertEqualString("ppmd_test.txt", archive_entry_pathname(ae)); assertEqualInt(1322464589, archive_entry_mtime(ae)); assertEqualInt(102400, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); remaining = (size_t)archive_entry_size(ae); while (remaining) { if (remaining < sizeof(buff)) assertEqualInt(remaining, bytes = archive_read_data(a, buff, sizeof(buff))); else assertEqualInt(sizeof(buff), bytes = archive_read_data(a, buff, sizeof(buff))); if (bytes > 0) remaining -= bytes; else break; } assertEqualInt(0, remaining); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_symname(void) { const char *refname = "test_read_format_7zip_symbolic_name.7z"; struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(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, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(32, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(32, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "hellohellohello\nhellohellohello\n", 32); /* Verify symbolic-link symlinkfile. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFLNK | 0755), archive_entry_mode(ae)); assertEqualString("symlinkfile", archive_entry_pathname(ae)); assertEqualString("file1", archive_entry_symlink(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualInt(2, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_7zip) { struct archive *a; assert((a = archive_read_new()) != NULL); /* Extracting with liblzma */ if (ARCHIVE_OK != archive_read_support_filter_xz(a)) { skipping("7zip:lzma decoding is not supported on this " "platform"); } else { test_symname(); test_extract_all_files("test_read_format_7zip_copy_2.7z"); test_extract_last_file("test_read_format_7zip_copy_2.7z"); test_extract_all_files2("test_read_format_7zip_lzma1_lzma2.7z"); test_bcj("test_read_format_7zip_bcj2_copy_lzma.7z"); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_7zip_bzip2) { struct archive *a; assert((a = archive_read_new()) != NULL); /* Extracting with libbzip2 */ if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { skipping("7zip:bzip2 decoding is not supported on this platform"); } else { test_plain_header("test_read_format_7zip_bzip2.7z"); test_bcj("test_read_format_7zip_bcj_bzip2.7z"); test_bcj("test_read_format_7zip_bcj2_bzip2.7z"); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_7zip_from_fd) { test_copy(1);/* read a 7zip file from a file descriptor. */ } DEFINE_TEST(test_read_format_7zip_copy) { test_copy(0); test_bcj("test_read_format_7zip_bcj_copy.7z"); test_bcj("test_read_format_7zip_bcj2_copy_1.7z"); test_bcj("test_read_format_7zip_bcj2_copy_2.7z"); } DEFINE_TEST(test_read_format_7zip_deflate) { struct archive *a; assert((a = archive_read_new()) != NULL); /* Extracting with libz */ if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { skipping( "7zip:deflate decoding is not supported on this platform"); } else { test_plain_header("test_read_format_7zip_deflate.7z"); test_bcj("test_read_format_7zip_bcj_deflate.7z"); test_bcj("test_read_format_7zip_bcj2_deflate.7z"); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +DEFINE_TEST(test_read_format_7zip_zstd) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + /* Extracting with libzstd */ + if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { + skipping( + "7zip:zstd decoding is not supported on this platform"); + } else { + test_extract_all_files_zstd("test_read_format_7zip_zstd.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_zstd_solid) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + /* Extracting with libzstd */ + if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { + skipping( + "7zip:zstd decoding is not supported on this platform"); + } else { + test_extract_all_files_zstd("test_read_format_7zip_solid_zstd.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_zstd_bcj) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + /* Extracting with libzstd */ + if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { + skipping( + "7zip:zstd decoding is not supported on this platform"); + } else { + test_extract_file_zstd_bcj_nobjc("test_read_format_7zip_zstd_bcj.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_zstd_nobcj) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + /* Extracting with libzstd */ + if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { + skipping( + "7zip:zstd decoding is not supported on this platform"); + } else { + test_extract_file_zstd_bcj_nobjc("test_read_format_7zip_zstd_nobcj.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_7zip_empty) { test_empty_archive(); test_empty_file(); } DEFINE_TEST(test_read_format_7zip_lzma1) { struct archive *a; assert((a = archive_read_new()) != NULL); /* Extracting with liblzma */ if (ARCHIVE_OK != archive_read_support_filter_xz(a)) { skipping("7zip:lzma decoding is not supported on this " "platform"); } else { test_plain_header("test_read_format_7zip_lzma1.7z"); test_extract_all_files("test_read_format_7zip_lzma1_2.7z"); test_extract_last_file("test_read_format_7zip_lzma1_2.7z"); test_bcj("test_read_format_7zip_bcj_lzma1.7z"); test_bcj("test_read_format_7zip_bcj2_lzma1_1.7z"); test_bcj("test_read_format_7zip_bcj2_lzma1_2.7z"); test_delta_lzma("test_read_format_7zip_delta_lzma1.7z"); test_delta_lzma("test_read_format_7zip_delta4_lzma1.7z"); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_format_7zip_lzma2) { struct archive *a; assert((a = archive_read_new()) != NULL); /* Extracting with liblzma */ if (ARCHIVE_OK != archive_read_support_filter_xz(a)) { skipping("7zip:lzma decoding is not supported on this " "platform"); } else { test_plain_header("test_read_format_7zip_lzma2.7z"); test_bcj("test_read_format_7zip_bcj_lzma2.7z"); test_bcj("test_read_format_7zip_bcj2_lzma2_1.7z"); test_bcj("test_read_format_7zip_bcj2_lzma2_2.7z"); test_delta_lzma("test_read_format_7zip_delta_lzma2.7z"); test_delta_lzma("test_read_format_7zip_delta4_lzma2.7z"); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +static void +test_arm_filter(const char *refname) +{ + struct archive *a; + struct archive_entry *ae; + char buff[7804]; + uint32_t computed_crc = 0; + uint32_t expected_crc = 0x355ec4e1; + + assert((a = archive_read_new()) != NULL); + + extract_reference_file(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, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0755), archive_entry_mode(ae)); + assertEqualString("hw-gnueabihf", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff), archive_entry_size(ae)); + assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); + computed_crc = crc32(computed_crc, buff, sizeof(buff)); + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_zstd_arm) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { + skipping( + "7zip:zstd decoding is not supported on this platform"); + } else { + test_arm_filter("test_read_format_7zip_zstd_arm.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_lzma2_arm) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_arm_filter("test_read_format_7zip_lzma2_arm.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_7zip_ppmd) { test_ppmd(); } + +static void +test_arm64_filter(const char *refname) +{ + struct archive *a; + struct archive_entry *ae; + char buff[70368]; + uint32_t computed_crc = 0; + uint32_t expected_crc = 0xde97d594; + + assert((a = archive_read_new()) != NULL); + + extract_reference_file(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, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0775), archive_entry_mode(ae)); + assertEqualString("hw-arm64", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff), archive_entry_size(ae)); + assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); + computed_crc = crc32(computed_crc, buff, sizeof(buff)); + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_lzma2_arm64) +{ +#ifdef HAVE_LZMA_FILTER_ARM64 + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_arm64_filter("test_read_format_7zip_lzma2_arm64.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +#else + skipping("This version of liblzma does not support LZMA_FILTER_ARM64"); +#endif +} + +DEFINE_TEST(test_read_format_7zip_deflate_arm64) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + } else { + test_arm64_filter("test_read_format_7zip_deflate_arm64.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_deflate_arm64.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_deflate_arm64.7z.uu new file mode 100644 index 000000000000..e516ca5b4792 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_deflate_arm64.7z.uu @@ -0,0 +1,64 @@ +begin 664 libarchive/test/test_read_format_7zip_deflate_arm64.7z +M-WJ\KR<<``2V^52G)0H```````!:`````````.HY+B_LU5^(3%$

2.2T-J0XF&*!R-C_.Z=<^_9 +MV1\-3^RE>M;>QXU.A7.TUWJB)`<[&KZ8T]<]&O#7!73=1:O.CH8O%NQ0C7^[)' +MUEN-U>

HU&)4Q[IZH<^Z^V_Z?J?JI.[UJY8?;3]2E +MG^V0&G.LDQ:JD1\IK:%&/MG#^(D]S&_TD&_K/H]^`'NSK2H6D]N^/=::C6>R +ML=WQ9(MDMK?%8SN2+?%4O_K#7#6EZ]NC$+9GV^L<]Z[ +M@,[[^Z_T>$/R\N?J_!-/OH\G/RYDOB@=N'^M9*K\>4.=*AUX>/6!&7M9D%Q!;M/@9]'B +MH"-+\J'`N)?66*?9YU[*N4N5?KOI-_IZ7=*/["TVO\D_8:E[MQC +M5.[96&D?377]O1DN=HY7^4^RK@X9JWY$BZ:<"TH[*.L\*LW.?X\6P[(=/I3+ +M(V3=[AIE`YV0'X^\(;%>XE*EOCR0Q_Q>:NJ_1HMQJ2F8QG4EU[;JNHZO5^II +MPZ]#SY?U/WRL_M>RDP]E?*?41GY%BTUV;<"MK=S#>_8]_-9/%8?*'-LBX05A +M0\VSUUB(%G-28U\C&"[F9.V7I854;M.VB%KPK5^X6#KPZ*IW_6_D\SAS=HQ3 +M=]_+O-8V5]O&YMNE5DEMM:;Z#)WG9^A]NRN12J5#^]*95+,R1IF+YNOOJG._ +MR^4-$K]*;)8XNEPN6]\96R2V2[PA\::U_R0^]KR'QOY-*M@VUAC5?YR=&REM +MAY&9[?B+"^IO-[$[+>.6M?2FZU73LP=#YXP8R<#*Q_ +M]_;Y0*?.6M-W&>.\O[(+E)GKK5[]X.5``````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````#PIQUK"7$B"*(ULY_$__\O$M2#"@F; +M]2^"&_^"/S0*(MI,,K.;T62R3"::50\>!/6@B'A8!$'0@X('3^)1;QX4/`I> +M5-23@H(?5#36))79Z4EE=P_B:=[2>>G75=7=-36]F0D1(D2($"%"A`@1(D2( +M$"%"C`I[(U`'L4+=./%XXH<==9Y`_3-D/X?Z5W[7/>=1/TH\EWB0QN<'QK_^ +MJ19KXRK4T$'ZG;8Z=S;FI_&QU+]*/(YX-O$,D-'66%]5J;%*_0KQ=/_V$;.( +M>SID_6R[O.Y'Q&,"\R\`V>Y7M;X_A:0_U-]+\:K>>!V?J+^.QG]0OQW^+YYW +MC(YOXG4=#ND(L(C2E=BV:=.ZV)(#F;+EE&/)Y?'NKN[E7=W)E?%DN:9U+T4U +MT95(QAHCL25&I=^PS8)A.5I^:>QP02LYAAVST7OEVK5KXWW+>U?TZ/PEY6'P-G67TL +MUC&GCX.'K#X>GK/Z!.CIY/2)>`YP^B2XPNJ3X3:K3X%7K#[5.U=D?1H,LOIT +MN,GJ,_#^Y_29^*DR^BR8S.JS(0BE=F]^KH($.A>8.AGO^OCT&.DS`4%Y;HXS +M!;Y$&)W)\QQ^/5Y\QQ^?YE=]^G3)7JX33V>N>YR?UXOSI$6<"X$X*T98_Y7` +M^C=@4QD]1?HC-I\SZ?P90IKL(;">8]@47STH_CA,'9YP;1C]!L7?RUU'IL[O +M8E-(Y^HA&F7B,'7[9-CZG`2#$3[/Z0A?A[GHZ*Y7P_Y"P/X9-H5T&7RI-M]WIUO$.>_JS#JOM=C7+=2GJ,WWQ?T6^7GLVOO. +MG_;&[X86]J]:K/.2:^\[]U:0_JZ%_<<6>?Y!<1IUNX3T/[2OH/U8E<_#8M7- +M,_/_<7_6=I*)(BS600@M8PI'ZX/%%4#91#FKY?/BI*$=%[V6JUFH(96<Z$[1+@FMC`&+A?Z\X1AZHHNW +M$+VF90K-MK4!@;^![`'HM;6"(?1RH3"`+KZ>0$M',LV=Q-4(L75?:M<6L67W +M9B%`;#ZT.[5KQR:4M^T^(+9LI]'MF_>!V+9SS\;43K%GZ];]6](BG=JX5$IFSF];BIDY3:N"/NUE5M+*>5 +M>G@U4_=H?,?`6L',XJQ%C%6/@7F"!%:L^V,<_@7F85-\I]8: +MU6,:EZ&`C$78(C[_M"KSXJ;_.3)6!?PKJLRQ$?PW8_N&SVP-_T%5YOFD=WC/ +M03)VTS.MZCWWROR)]$YL;3[_V<0'25>]YVB9KXZ0OZ/8JK[UKVN7>7I@_6J` +MCV/[X_/?VRYSEV_]"K/_4Y33AO_9=ID?^>=G]G^._#=ZS_$R5WS^,QC_R[2N +M3N^]A\RS1[C^%QO^+9ZC^T'&9)!Q+>#?TRGS>@4D](",ZP'_,Q&9IX^P_IN! +M^^]*Q&/9OT7]W@GXWX[(O'Z$^1\$_%]%9+[7>GZO/B9B:Y/?2R'S]M$`/\4V +M:N]'B.G^5:C^/3^Y#E[3_GWOK8@;]3/\_.]E?U\F%9IG +M^/Q](*W-6Y=*K'#VJ,KX3%H7>)#\-=9?9A6:42&+GNC0>\44<_^.\=8NXV&T +M[K],'7[]4UKXOQE;M_P)P_O_!0$$!@`!"8HE``<+`0`"`P0!"`$*`0`,P>`2 +MP>`2``@*`935E]X```4!&0(``!$3`&@`=P`M`&$`<@!M`#8`-````!D`%`H! +3`#)5M-E?M]D!%08!`""`_8$````` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_lzma2_arm.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_lzma2_arm.7z.uu new file mode 100644 index 000000000000..5a6ade031f22 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_lzma2_arm.7z.uu @@ -0,0 +1,50 @@ +begin 664 test_read_format_7zip_lzma2_arm.7z +M-WJ\KR<<``3J_$W1N`<```````!B`````````.JU,0K@'GL'L%T`/Y%%A&@[ +MWMZF$<*4U"093^%DJ=J._J..`X9!2RXNX6*J`]\W5 +MV'E1<]9A1&J5D`#J!0K[IET\(=96D87RO?!\YQ>LH!0''871$*/B-@-70EJ0 +M`;\!OE9CF-^[@!DI0LA7Z5P`#.!8`&65OAKG.CZ"F_H1?0DF"U1TA(*8JEQ>/>U8FBB]^E)QG>MQ_LLWD%-YVI_%$>)09X;WO&$[MP([#E"AI4U,3VSH@/S*]#&EGP37GQ_#W*+P:2"&0(O=$S'+O'75NH+-'ZI&2 +M9W^I\?.Y`$T4YPC[ETIN9KTL\>"^-]*+([,!RK2%12BUU!TRF!5:8B?]<3K' +M1/*E5"22231Q(@S6P/`GO*EAHOGEC)B5=%X;>B*\-D*4#KGX(2,,P4)Q\PQ< +M?L=SG>R/AC>SH1S1_FUK7"#5\]0&QR9`];@`YS*UH9Q@P^S977+E@_P`0KHZO?1>:7[4.RMGB]SF\_BQFC89B)!U68-_]72 +MY'BDKM\E;]&4Z%FR?TOR2'H[7X@Y6`M+Q@![*&M9W!0F#^@UO(8TK]U#-8<-I\2$7 +M#?WC\89&95YI\\`DXII"-P@0IQJ:L4]:O*=@V!W58(_#7%9L4BR8-<$P`4S# +MJX#Y/>DIDB#/.0DY67H@K60XS/*(46/U*%P?G\R_U#/(0%OF +M6B#:U+X9)8*I`.^U(`PKV^7%B=?Z%GP^8CQV8V#'_1?L2KB>;6>!N.8LI>3_ +M%)-N+<;/(+J(QI_U+9C&*VDR9)8X/!5R+N8W6N84NN@'S9P>KC +MN]XGR&MK!GNV=Y(MV+YS=QEYQ-,':9VRQ^N +MPC!ALZ:]"J8\:9T.Y28?K^B>@@5(:I]M>EFH%-#Z>BRBL(L)]_J#F +MT7U4C[62SWU-_0&#JECK.K2F9Z/5_U\6"K2[$'8K*N!+?`(T`:3BN$=LA)7? +MS`:MK/59JR@1*#FE_'[X+C68SP*%RH +MGV(2K8@W&^<"%O>`1]-6ODEC`^X__^'#-E9'<`F-W+^-/#*=00[^ER5/Z%<; +M<5>252LEY6L`C9Q:-4^3!XGD]CGO,-XDLT +M.;'"/12=:`C.Z+3G1-&=R-!O#5,V.W'"C9:N4?==ODY8#*:V;QL.N$ZR'R30 +M>U5QC/A0M`TOK9EX6PZ03U*3E;4527?8OP\+9NXTSDP.-ML`U\:36)X[JXOZ],_M[4ZD"D0T.7 +M3C*M=YOXBE!`I5-VO'3:YUR-42G):6"01XCG5%O]H17JJJQM8A<<,3RS8CI- +MT7FK7"\1P9GO?#3O07):>\L_RHRH?]3Z#W42TX43Z=2HC/8M!H]),JTZ-@/"U#%4-Q#G9EP-$Y^]=$>X70[D +MCP*^+R.X5U[;%+0P8JWW)`P#'VE[%=I>G;TGBX!_**[`)D9.JV9DAAYM[`"# +M0]"E%F5%X?]_%@,>,Y*W"%H.!H6KV`7H>4&=7BX&?6K$"V@"*\#2BVIL.FH9 +M_YP"D5UNL[$1$U$GLQROKS__F*#'PH`%ZC5^,0\)'D&S:;?)8`WEYT,8"? +M1]-Z'S1^IEL/*WJ-LQ\;?%QQ8/*JUY93:52Y,XHJNY<;Q.GRQ`&ZY5)H9'2< +MVT<0P98-*4HW2:\I<\W>0HW2E[B9D8LYS:````$$!@`!"8>X``<+`0`"(2$! +M`@0#`P4!`0`,GGR>?``("@'AQ%XU```%`1D!`!$;`&@`=P`M`&<`;@!U`&4` +D80!B`&D`:`!F````&0`4"@$```-.&5"B^),$C`%""HM.8#>=`#! +MHGG`);CBW],\(2H/P+,[60V#BSUD@SPX?+]7UE`-G%X8-3UI(38JH=H'>(C4 +M.BHYW!+WO_VP/@H5V?EN8ESQEC<5V/WV[($3WNDOW%0OV?,5D(VX$W,9F]X6 +M$@+,!DJ9MCLG?+981]+>4RH&\+O6N:Z[J\UUAP1\;YX=FQG04]%"*WE/:)&N +M7]/;*`CB"$1/1LN;6/@^--#D_P_@^=I7&LU`$C*&]O)XY16O/YBAT\L2Q!M6 +MZ,^,_"DV_%G"4[;]`8E`Y>_4'B-G'SBG0\P#T504(#V)73,/E2#B3I7=S".' +MT?4S]U]05SC_GJUAE*!NK%8\&=\',!%JIL5J>;6'O[D[_5JVE7>4\(D]JBL8$[^!T?<88]&GF&D!J76?C\!TV:R8*BQ,2%*"NX6^-J!7;)H[ +M,VL2_VSHEIU&_VR#)=O81/7B?&1://2C9_O[-3*X(-,H(QI$N8$/CSMR0_PU +MF+&[-\:YPQ-V_/\D'K<+P1?:NF3DC@(W0X+$X,HG8*-0_+3@`07IT`9`RZO> +M=4MI;EB=PHN\-U*#KN5B^JX'^8EFYG-3'H?CITKDVRN+'.(ABHXGW.51@<$J +MTX]Z-:0Y]$8M))->TGEHM">WLYXA/P.=F-)"H)_M7B5FK,I=7R&:5F=A^H:\ +M"U3HG2Y>^C\3P&?0,F/>+S1<<,$W5!DW#+5:DT!&H>R]*HYAL;3=`9 +MG-6=ZO*O/-T<0GY[;.-GK`F4@EZ(HL.JR%:.V(7[VY:.`?"47U/P;N",R)G" +M%MX4UG$88O@.`;_;(1W&"==@<5,SMBD.R7>ITI@G5OR14O5NB?*E!["2KU6: +MH+P#/KA.)R4]DLK9W^<7&2RL#EF40DU1*VS`"A%7D5^"M`OB.0W5*_<#5K7* +MR'D05`R:?G3Q$`L;B5>KD\B"A%!FAV9F[BA2=I^AWD%J7`WFL7T,T%?%VI^; +M]0`QABMO4"3N3&-",I^)1E`Z61GN*(E7I&+P'2/Z$W,Q4^P3@1\Z01D"VYZ! +MZ]*!@]@/<"ZSHHN2'Z66_/Q1';U/W@:5/JY).$#*$JYEYOXW:DD&\7,"\NUZ +M*R7JO?D$*$NX)#AFK5F&$ZS`,HKM4&XRP,+WL+W:X2X98SA`L."UXPSOZY<".O^LP->K,4?Z-"*L#Z[P#"4,UW54G>W0KX/!21G-DW010]NZ54" +MT?"[+_BDNGN7B2'-)K$-,Q;L3<*/`/+A_EM-CG,CZT>TOP.1(>#99CO&/97= +MB[##0N[?U\)&U'1(]0CFGJ2>AXP_3RLI*,:V)B$I-<88"C66?>!/I0-YKR[, +M,(`W)51UA>T26X/6.,4;@!NGC*;0VLT(3KS2Z>(RX'MC'$C+I1BBR!)!)C1* +M!AZ7_:5Q_RI!L!3"6M0;8&NO0V2HYVA,"18X1IW+/I9?$B,06BS=Z#B<$J,U +M-GI\,NTS)(%MEW#FI%Q,1ZU30VDFO38M=_@*<2-HT`H>8VF^;V6!0,8*T'5/ +MS(?V9HD%DO;K^EOY8MB'_7J6P=+7MGT:AAH>E977IC9\\<$?0X0CKH-,^Y5$ +MPG(L?(8VYY=8"QMC)#MW)JL9?(`E$Q-65D_!\FG\A]NOX"3KECRR&I/I,-_L +MY&!L^F*E(.H&^C3[CL?KK2\S60UD3W[E,,NZ1Y^*&S@5U6J#BIL,-/Q?VP9< +M,5*R?(?<6J69T=K\E=)Y6T0$(RM!_S!38A6'S"CCI=F<`'?6/*5KS2L7ON'B +M:!)QKWUW%-N6`)K7@<="%O@E[$=W9+1;A, +M+'*KT@O#)$%J/;W$T,J!'NA?2G7F7GK88KT1(K\Q52<<(!S6;SKP4H#K%(SYK0`B,Y-/&7\3]CZ/'( +M0@61%II0K.Q,&R`2$K'#I8'B(J0'\20($ONEJ2AXW;-'V3AP;9ME^L7JBW46 +M!+9%;M7.G(>CLF;A8*'@X"+832%9%WXO^K*>?/(]*..3\@`!!`8``0F(```%`1D"```1$P!H`'<`+0!A +C`'(`;0`V`#0````9`!0*`0`R5;397[?9`14&`0`@@/V!```` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_solid_zstd.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_solid_zstd.7z.uu new file mode 100644 index 000000000000..3bffb98f07ea --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_solid_zstd.7z.uu @@ -0,0 +1,9 @@ +begin 664 test_read_format_7zip_solid_zstd.7z +M-WJ\KR<<``1&(FS)O@`````````B`````````$V+D*,HM2_]`$@!``!0*DT8 +M!````"$````HM2_]((+%```P80IB8V0*!X"P/JZA4J4;TS.X,9C'`#4``($S +M!ZX/T5NC)*"0H'?;G=XO-Y<-`+"F*K87M`OOZ1+1#31#M/`2YN,FY:(1).I) +M(B(+;9$W4?0*8=3V5N;BSZ)(0UGD/'LOSN"0&#DNX!A2*5\#&8IP$1G=7-]@ +MP\EE$]Z;/%6NU^\_,X:MD?57P#S>.+BINH?CHRX,::[Q5P*!X\DH````%P8V +?`0F`B``'"P$``2,#`0$%70`0```,@-H*`9/CZP`````` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd.7z.uu new file mode 100644 index 000000000000..acd49c421a6f --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd.7z.uu @@ -0,0 +1,12 @@ +begin 664 test_read_format_7zip_zstd.7z +M-WJ\KR<<``2QPP_C,`$````````C``````````+H!E8R#*L]U;J=* +M@K`Q6]^1F4MZF33TE2LJK8X@%5MY%P.#1*EM98=_,9"?K'$*_A0YVC)]1ML= +MK-5-,X82B;/_L`I&FQ!5HP#H````%P:`J`$)@(@`!PL!``$C`P$!!5T`$``` ++#(#Z"@%\$`,4```` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_arm.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_arm.7z.uu new file mode 100644 index 000000000000..770e99b3c10d --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_arm.7z.uu @@ -0,0 +1,61 @@ +begin 644 test_read_format_7zip_zstd_arm.7z +M-WJ\KR<<``0IIN38A`D```````!R``````````1RSP4HM2_]`$@!``!0*DT8 +M!````&\)```HM2_]8'P=+4L`REV0%$E@''(._[5GFW&YEVY]?G/)^,\9+2"B +M(\1H-(@DHJ*L)A?\C,G6FR3?/D.:6QDP<&`QFLOCXM`%J_](<>7+QU&E6E:N +M9=5-A,@4*0%(`38!`-3GD[<=6PPVGVGC_)87GYQ_BZ,9+G<6(:EL+AFM]/OESN[+)245575._@ +M^RPCV7?'7IB-7R_..I.8;"%/NJ_\?\/_!_P<: +M^G3XSS-DOP`414SD%5U,'70-F?MS1VK_/VO^C&^I_1SA*^_=)^^5T\.;Z +M_W0/7Z?^[E_7Q03P`7[I>3]\I]RK_U^TYO^=_E7>+WWZ>^_CU12)L[N?\MU#>?H_X-28WKB?Y#2_H5D[#04WF9R/8>-46ZIY]4H +M_[E-HI8CI(5G.6PHP["KW.$%T24@`CT?H"YM4.#W#_7_$LP0H8J@_5G9\>QI +M=PPFO9L77'W,=3WH_D`?:4S&I@D;2#5JL=/]U?I!=OW1=F+M]/KEY>S4[+:QQ[<3#6#>,P +MS":???N<_*[T7VSG+Y+E_%NEE]<%?4YB^.=!@="D_R`W:7)EGJC+VO<>:,/( +M\K[_V5!^"/W92(#84!'B?5"@#>*'Y&'/_Q;Q1/_;[`_]<+/84PFV[WO2M)[4 +M2L^_FUK-[[<=1DVMEU-2?3DI7H!^/\D=^=X'_6[Q0?]$0Q)M_T)1>VH1],7_ +M:W`1HJNG_IVY`DR(,_?O^G7K_0[6:5[\H)=H)`=>"@'Z4 +M/Q^33T_=]2CA&;)S@F?<`77FZ')IJEE:`V4.V`!0HUVT@RG1%MK`':0"J3LH +M;^^*.CNZ+Q*?D\+1^+8/_W\ID\V-J^^W(_,#XTKIH4:O2Q$CO2S"#\-CA +M&,ZPD*+'A="2":C_L9T"_Q\,^!YD:/"1%93KNHT#)91V>Q/YSKVS/7:72SFY +M/DTCU^_RU_K)=\G&8JW27=_9ODYWQ=HJKB_/Z_K=Z?9U_0XS87=>?^:Q_-*S +M"V+N2S$Z%FO75]*LDE@556Q?8@N_3>3:C+]6M\OQ^LS9[?&KG#N]/I+M2S%Z +M_9?"XGK^W[ITD\=OVVFN5=7&K)++=,MY&[O/=K9U:1H)Q:>NHHI+L?/,\I=I;=WNPLREG'QGD^7T+/>6PRQ2AK\"@C2H$C.5*65$1$1$ +M)$F2-`9""D@*96%02N8^$J#D.`MT#".12:(@2)# +M4:BMMH#O(,3N\SNY98ID=!W0;%:X'+'JB96]?7)D)*]C(/`75J#"!K*H?_US +MA4W\/A]*S]-7_2]IAM/+/`B("R[9CK@UI^F_L_F\S3TLEF]4;-9K"D.FI-BN +MV;3B7IH,H:X*!VQF'5DKMS(QWD"?M;I7HLYJC5@4O84-JXI4]\ANPCC,Z6V! +MH1<1&1[VBD<[;N__]';,59N\AQ^&]#"-.ERLW77^#-%@[(V?&%>E1@EGMLCHLB@IEGBO +MR^7F7VZ4^<_*:H]3M2!41<$&Q+=RB)TZU2K(ZASM'+S$FSC)$0QJJE7#\<..?\ +M5<-K!(&!>!\SDU,3G@>GQG!HY@,:VA83.3UV:[8,/#IFAAR>4[SNQD@#X(ZP +MSB1KCH5,N!*?N?=.*K&"L:.#>WPGFRQ?G!MXSQ%MC#G3I!&A'&2-D>2,A/-E +MECT8^7<1UPS%/*;,`34&^-#OCIS.QE_GW3XHG\@9&@Y?L((/T]` +M)T;C<[`:OL9XFC,(CGNQ:L7!)HW!FUD*LUE../[1_FDQ56O"(#?<^P\D9Y8S +M\B>"887TP=S,B&&5FXH0#40E'^VT-J)ZZC$8F8IXY#3.]BBD/Y!W$_ZW_PF! +M]O@GGFKZK7B;%/:O*>@EBKE4E'Z,8L,CF729$*JA:H"9A4<-W536[#+GLD*, +MA1`PA]/73?\?687^X]/J)UG5TW%7:20ZKXIC[MVDJ)')G',2BOU+XJ+MC.)< +M.2`Z'M=W?='`QE[.OF=_/FV="Y,I[]N:M1/U6>W;\;-N"M*]+K&L1B*%65/ZC8$_36I;+/=_@$ +M3FB?WK+IOVATX0C;5Y*=[>\S4F1(V)J!O??36$$TKF(S2@FLJ\FLP*@EGBB:2%'/9-Z8 +M8Q,_6=Z>;B0PSV4-O0H-CLX!^O0EGI6A1BSB-+@-^M_[_W@_H7<<>.R8P"(# +M;W9GXI=Q",^;'>9$=#]CA-$DQL\_IE\_^00T'^]Z\9A?)Y\``@*`>'$7C4```4!&0H` +M````````````$1L`:`!W`"T`9P!N`'4`90!A`&(`:0!H`&8````9`!0*`0`` +166S@9)S9`14&`0`@@.V!```` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_bcj.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_bcj.7z.uu new file mode 100644 index 000000000000..2a75db8972cf --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_bcj.7z.uu @@ -0,0 +1,56 @@ +begin 664 test_read_format_7zip_zstd_bcj.7z +M-WJ\KR<<``0N5BP@P@@```````!B`````````(*-K]$HM2_]`$@!``!0*DT8 +M!````*T(```HM2_]8%`]'44`ZE%D$DIPW'$.RP,C]5>);DM31:I?D,QMZ#'[ +MX]O[KCOFX)J*\$44=%&TV"+)8#QPV28QFR]0!V-3JH9UII:164M``!FT!`!L +MXELXL[8O4Q,!'@$2`5/J6F?&6/][6'!>ZPF&:U6C8%?F9DH.#=>\Y71GN:,M +M^ZA,>'Z0J2/,U:!!Y(?S^B3?P==^[[?]YK&,Q.+\7;C(4KFQ#"L]4*LEI'SCU\6R?O,=IH;BR_, +M^=,]F?4+_I]'MWL=@)3B4LF]J`%84^*+!@]ERRM(EKDF0@0*\*,A13M[J_;, +M*JBF":I,JT3`(:"1UI!6,G[AH1;N$0&++]Q;5RDTUW:S#KB?&TWF[K] +MTO%\G]%M-\G(:E8U$$&Q(;$WDZ;I?H%#_\%^!#:>[<]:V>W_LQ-_Y?Q`(@"A`L^!S]UHDBP&X#+X<) +MS4GK>LP.H+]9:E#78]`VV'FC.[!1K?U2*3:2NB93]/].==UT^O\>==W,\/]? +M==V44-=-'0;;@?WP+*-()<@P[!['GHC]HU%G3KK6_QP>N.7:3':M;%ATH6"X +M5K.NM0IE?OG9C8W8_ZH67)-_%.SL'Q6>_:M>@EZKKIMUJ5R"7&M5A.0'M[;4 +M9V?&P.&\UIV>1F5MLE<;FI+U2(R2M=FUI@DE%2V)P>4ZMO6SVU+C=&8,(,YK +MY>EH%F;<4AEF?GO3?%`"2[7)S_F;T@5U>S"M7+I6DE0Z2*&^9T( +MJG_I9W?UWQ,25(-=_:/IEZ/P9_??RQ%4__=P%-5CYQ*N-=P)*8F#4Q-_J:5U +M'?(JJ";.7XGXA`058FD):LB'3TA23)R_445Q[&_\["64C9^%I)EM.SW%L.LT49QN)R:^3(SM^H7ETE.]UGXV.Q +MG;]%EO.7U5^?C5.OJ[*+Z*C1$-(A?J%;#=`G\MY'Q2V%>VHZ_YM'O9).+?@] +M&[B#>O9PXX#A62$P9N#(I\W?">*#9\:C*[Y`E1!]GR=28((]^C4"J>WK;[__ +M:S)_$_HE*_R&?+\TV>-DL>I!>D%)J`P3US430XU<+-S +M),83X)<`RFY;.%N87*>=0>!SX'OZ_PMXW8W.*B=`J28#S1D*U77O4Y3UMK+_ +MO\$[$]BLC/9N@+Y/YWVH[O3MW+GVQ&Q`R,C:,)F[_R=SFIU!UC7+1_G_FTY: +M8Y)D#["R,A(5%88*A3Y0-$W3'+='>?"2_FWPSMRM12UGXO>_YX^R,O;,`86% +MR76-YO\_I%]?Q!O0\0A\!KO'+A(I&]6GZ:+ZQA?)QLIV;H]'5WUY5M7O6FY7 +MW^C2,I:J?H>S6-B=U9]Y++_T[)Z8.U))Q\I6_2/-'H>(24CM.]3";[*H[+.; +M)<]9:C+&4MTNQ^IGS6Z/W^/<:?6+;$L+^>0INN&\C!Q)+"(-?'D)(%2ZL3O]\]"#?E1$+0VXIS="D"#8T@4)AH;OS0QK*@.S(L,50<_CA +MSLQF9'9?XSB;;-U94YD.FO]*997K@$8#<$+3;FE`!=:DQF+2IM`^P_V)X+H8 +M>CA6D83\V2'4'&8019V9GJ;4N^GH!]DY[/[G+;OB1_1;1C*]PB'S([P%1P=2 +MVZWP^0AVC4J*P$8O6>4N-N<4YQ@52=LB99,=Q-92\YTM6_:!O(CLS*.3].4# +M"=G'`&WN&'Q@PN!^%WP*@ +M>Q#R';%'W>W,J`@NVM/IYT(1Z9WO[L0DR.YFB5_M%B-Q;^"AA"&\S5O?^]2G +M5:0N[G64]G+ZWR@D&V)Q@^LXPO*B9G9F,9'#!BQ>,6;6FU$N?3 +M`.Q/U+S_$GPHT*;\)<@Y@CF:$OL@/;(&MBC.]2FU)]]1H%"W*G?%/.,%R +M%[H<>8MCK*V1.J%.C=7'N/;E*:U^2SX@)@'SYZ\;36III>#+-E0=MARSYX'R +M\WH,G%TT4TXJ7V:1?#Q/HER'4TFC.'Y%Y0D*MH2(6\B5YU*21O#?4OS=K,#> +MM?@F6*^:37;#*>NV\;VYS9R567I^EN(RXY3MF[\:?IS1%VRW7Z[:^VI'LY=_ZBTR<)4IT7]IYY]1OK&'@.&E"VR@9%YH&S'Q,#9CF] +MG8&_K//(.AY#/&&R\L5Y9G9*M">BYP#J+4'9&3=D:QBW=45`IG&!W"1D^=K7 +M3-\)$&`N%0_$JV$!\![TG-`=:",BG5.9A)"?V=NSXPVO21_O-QG<+$FCO"K< +M+7,ER$BPT:CS!0`Z0BV^S#)QZ3PK0-/_L#+OU^QUO<'=-9BN6Y:-5!LY:B;N +MZREX8;-G[,XTOK;@Y$4Z[)9[!"MHPI^1M7KK1KL45B0C''SO8H,4XUG<1RU\ +MG/6=<8^3]'MPL\V\;ITR,#$MU@E[S7%[`00&``$)B,(`!PL!``(D!/<1`04! +M!0,```0#`P$#`0`,OE"^4``("@&\[F:]```%`1D*`````````````!$'`&@` +@=P```!D$`````!0*`0``'';'*9?9`14&`0`@@/V!```` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_nobcj.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_nobcj.7z.uu new file mode 100644 index 000000000000..c6fed0bebb84 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_zstd_nobcj.7z.uu @@ -0,0 +1,56 @@ +begin 664 test_read_format_7zip_zstd_nobcj.7z +M-WJ\KR<<``0`N.\QQ0@```````!2`````````#TEXKPHM2_]`$@!``!0*DT8 +M!````+`(```HM2_]8%`]-44`*E)P$DIPW'$.R^E\_]^S[,>DG"("L:I:;)%D,!ZX;).8S1>H@[$I[8=UII:164M``!FT!`!L +MLB.<6?N3%!4!'@$2`4JZ6"F]QIDPV'_>%=P7BW[I8M4HT)&YD9)#@VO>T')( +M6DXDLR#Z0.9.,$=CQA`?#OR3?`=?^_-\VY];GI%8>_\;%UNJ5YYCX1,5_?(: +MQGKIU]U/5,3]112,U@GKA"U4(CHU_>]?%'#,>[\,M\C-Z9;7VUB$ +MX[V_YLFL7_#_N]H]V0'(*.^4G(L:@#4EO&CP8+:TNUZ$?T.]HBVDK*<$SDQKRB7Q&.>2PKE>9]N=98W(:M@B]-TWPM'=H;6Q(O]U]F!6[+-I!?+9H57 +M^J6+/?-BJWKO6EZ`=&0CSO\U"XY)[]U%Z[W[H/VO78!?;`X_\UJW`+O8J@C) +M!W)IZ2AGPL#AOE@>GC:%97*R-C1)6*0P2=BF%YLVI&2T)`*7+!D7D$Y+Q^%, +M&#_<%]O#T5B@<4LE<,G^"R#=>E7FGS?5\P2X9)WXF+XA55232Q>[Q8LE,<6+ +M",9W0JC_$I#NZI\71*B'7?7>MW@Y!H%T_SP<0OV?=\.HDZ$!3DE*')R:]DLM +ML>QP5T$U[?U"XE,25(BE):CA'CXE23'M_<95Z\AA&=Y[OQ*)3+K[1;;TU2MI +M+K];V=)TN^'>ZK[\C;.0L_&WDNVNZ9F[.VZ+XD7R_<;VZ2O*:T\;' +M:OF&B^W>/Z\^3ANG/H?*221';<;0#A$LY6J0/G'W/REN:NQ3V_D_=]0+=VK` +M]SE_IY#/.3QQONRJ$"`S8.35YO,`Z?$],CY5\5_@SV#Z/T^DP$S#/OT:`=4& +M]K<___N8OPWYDA5\1#V?FN1RLECU0+T@%#;UTN!TFEU-._L:5`$.$\5%W>12 +M`S<\2&,]`7X)P.S&A;.)S3W-#(*?`^_#_U_`Y]SD6.4$"&LQ4IWA4/]CE/4V +ML_^WL4,3V*R.SK-!^KV=[Z4Y:._,H2QOS/:CK*P-DZG[_S'H&0J$9;$\E/^_ +M[:BU)4C.`5961:*BQ-)@L$>:JJJ*X_;ICIW2?XT=FKNQJ.5,_/DPZL/,K$5S +M06)B<^]G_O^#"@9&O`4=C[]G<'+Y12)GX_IU75S?""-;6>G2[O'XKC_3Z_I\ +MZ^7K&]]ZQEK7YW%6&[_T^O269[BFWQ,T1RIK6>FN?ZS;(Q%1*?TL^N4R?*1YO7ZQY4AEO?X+GZCJNKCWN_*[WWWKY)9A +ME]>;`]&5V^.>:W>WO/V6M^Z[@WOHI)1$M:F&4TQ,'':"4:CB>C%")2(21)(D +MQ1RR"R@01$'(&+EY$D!C+-E5AA&"G!$1$1DI*"HJ*%3:#M)%"5G$G\"8%:>G[^>!M\V$XK!3)(B?F)-CZ=K`("^=H&,T+K]/R8B +M['>&V!;.[RZK*AXHW/(1&>W$L([TS6UB*(17-)#`^=N<-Y!+LP+,;VWOM.:V +MG"H>@K<%',>.&QD-=+)!XH+%'R\&]".3\%"A*"Q49ZE[Z/-`S&!3[$CTBVC3R]ZNOP";Z'2 +MH2O_5Y`ZL)EB)470T:OT>2R/20[R[(Y,@NY,>;K5;&0GE`P\E#.%]WG4O +M&5+K/5T3')<)O"I,1MVZ"]]^(&Q5B">>RVD\[W$PP\;7(AE#%.%E-HDY"IIT +MR+8L[$1_("Y?)9Q7F&M +MK64ED6D`V29*P=.*;'(U%I_))$V/)_?,$=;YBZAX0W2M@PFKU%9S3_T.T:#< +MW/V$HY:ZT^6Q"C":"ARLJ?J@S!>.02=/K6Z%TS_J/3*YY=UH8DNK;;"^H=ZX +M?#9L*(R;]X<0VT7;[:2U;!;IBO(DRB.[\UAYLS,UO/S@RN?PVG?_*AAX1GE0+O-\:J] +M7,:5^[ASH4F3NG>,TSNX9AQPQ>"V$LT4["&]0W=&EFLO#1^W/A*04USP-0!9 +MOO8][Q\"!,VAXT'A8CF`N?FP)O*.V0A/YEIF(B0G]^KN-F5KPD;:J[TX$J21 +MW@+<%;CB-NJY,<;P!8#[&A:9S.V+U9\ADL)_&*EW<[O +ML2P=S_XO.NY4@\-FS$OP8EX>6=[`00&``$)B,4`!PL!``$D!/<1 +M`04!!0,```R^4``("@&\[F:]```%`1D#````$0<`:`!W````&00`````%`H! +3```<=L #include #define PROLOGUE(reffile) \ struct archive_entry *ae; \ struct archive *a; \ \ (void) a; /* Make the compiler happy if we won't use this variables */ \ (void) ae; /* in the test cases. */ \ \ extract_reference_file(reffile); \ assert((a = archive_read_new()) != NULL); \ assertA(0 == archive_read_support_filter_all(a)); \ assertA(0 == archive_read_support_format_all(a)); \ assertA(0 == archive_read_open_filename(a, reffile, 10240)) #define PROLOGUE_MULTI(reffile) \ struct archive_entry *ae; \ struct archive *a; \ \ (void) a; \ (void) ae; \ \ extract_reference_files(reffile); \ assert((a = archive_read_new()) != NULL); \ assertA(0 == archive_read_support_filter_all(a)); \ assertA(0 == archive_read_support_format_all(a)); \ assertA(0 == archive_read_open_filenames(a, reffile, 10240)) #define EPILOGUE() \ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); \ assertEqualInt(ARCHIVE_OK, archive_read_free(a)) static int verify_data(const uint8_t* data_ptr, int magic, int size) { int i = 0; /* This is how the test data inside test files was generated; * we are re-generating it here and we check if our re-generated * test data is the same as in the test file. If this test is * failing it's either because there's a bug in the test case, * or the unpacked data is corrupted. */ for(i = 0; i < size / 4; ++i) { const int k = i + 1; const signed int* lptr = (const signed int*) &data_ptr[i * 4]; signed int val = k * k - 3 * k + (1 + magic); if(val < 0) val = 0; /* *lptr is a value inside unpacked test file, val is the * value that should be in the unpacked test file. */ if(archive_le32dec(lptr) != (uint32_t) val) return 0; } return 1; } 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; } DEFINE_TEST(test_read_format_rar5_set_format) { struct archive *a; struct archive_entry *ae; const char reffile[] = "test_read_format_rar5_stored.rar"; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR_V5)); assertA(0 == archive_read_open_filename(a, reffile, 10240)); assertA(0 == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored) { const char helloworld_txt[] = "hello libarchive test suite!\n"; la_ssize_t file_size = sizeof(helloworld_txt) - 1; char buff[64]; PROLOGUE("test_read_format_rar5_stored.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("helloworld.txt", archive_entry_pathname(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertA((int) archive_entry_ctime(ae) == 0); assertA((int) archive_entry_atime(ae) == 0); assertEqualInt(file_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(file_size == archive_read_data(a, buff, file_size)); assertEqualMem(buff, helloworld_txt, file_size); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_compressed) { const int DATA_SIZE = 1200; uint8_t buff[1200]; PROLOGUE("test_read_format_rar5_compressed.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); assertA(1 == verify_data(buff, 0, DATA_SIZE)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiple_files) { const int DATA_SIZE = 4096; uint8_t buff[4096]; PROLOGUE("test_read_format_rar5_multiple_files.rar"); /* There should be 4 files inside this test file. Check for their * existence, and also check the contents of those test files. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 1, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 2, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 3, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 4, DATA_SIZE)); /* There should be no more files in this archive. */ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } /* This test is really the same as the test above, but it deals with a solid * archive instead of a regular archive. The test solid archive contains the * same set of files as regular test archive, but it's size is 2x smaller, * because solid archives reuse the window buffer from previous compressed * files, so it's able to compress lots of small files more effectively. */ DEFINE_TEST(test_read_format_rar5_multiple_files_solid) { const int DATA_SIZE = 4096; uint8_t buff[4096]; PROLOGUE("test_read_format_rar5_multiple_files_solid.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 1, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 2, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 3, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(1 == verify_data(buff, 4, DATA_SIZE)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all) { const char* reffiles[] = { "test_read_format_rar5_multiarchive.part01.rar", "test_read_format_rar5_multiarchive.part02.rar", "test_read_format_rar5_multiarchive.part03.rar", "test_read_format_rar5_multiarchive.part04.rar", "test_read_format_rar5_multiarchive.part05.rar", "test_read_format_rar5_multiarchive.part06.rar", "test_read_format_rar5_multiarchive.part07.rar", "test_read_format_rar5_multiarchive.part08.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdcat_test", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdtar_test", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_first) { const char* reffiles[] = { "test_read_format_rar5_multiarchive.part01.rar", "test_read_format_rar5_multiarchive.part02.rar", "test_read_format_rar5_multiarchive.part03.rar", "test_read_format_rar5_multiarchive.part04.rar", "test_read_format_rar5_multiarchive.part05.rar", "test_read_format_rar5_multiarchive.part06.rar", "test_read_format_rar5_multiarchive.part07.rar", "test_read_format_rar5_multiarchive.part08.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertA(0 == extract_one(a, ae, 0x35277473)); assertA(0 == archive_read_next_header(a, &ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_second) { const char* reffiles[] = { "test_read_format_rar5_multiarchive.part01.rar", "test_read_format_rar5_multiarchive.part02.rar", "test_read_format_rar5_multiarchive.part03.rar", "test_read_format_rar5_multiarchive.part04.rar", "test_read_format_rar5_multiarchive.part05.rar", "test_read_format_rar5_multiarchive.part06.rar", "test_read_format_rar5_multiarchive.part07.rar", "test_read_format_rar5_multiarchive.part08.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertA(0 == archive_read_next_header(a, &ae)); assertA(0 == extract_one(a, ae, 0xE59665F8)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_blake2) { const la_ssize_t proper_size = 814; uint8_t buf[814]; PROLOGUE("test_read_format_rar5_blake2.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(proper_size, archive_entry_size(ae)); /* Should blake2 calculation fail, we'll get a failure return * value from archive_read_data(). */ assertA(proper_size == archive_read_data(a, buf, proper_size)); /* To be extra pedantic, let's also check crc32 of the poem. */ assertEqualInt(crc32(0, buf, proper_size), 0x7E5EC49E); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_arm_filter) { /* This test unpacks a file that uses an ARM filter. The DELTA * and X86 filters are tested implicitly in the "multiarchive_skip" * test. */ const la_ssize_t proper_size = 90808; uint8_t buf[90808]; PROLOGUE("test_read_format_rar5_arm.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(proper_size, archive_entry_size(ae)); assertA(proper_size == archive_read_data(a, buf, proper_size)); /* Yes, RARv5 unpacker itself should calculate the CRC, but in case * the DONT_FAIL_ON_CRC_ERROR define option is enabled during compilation, * let's still fail the test if the unpacked data is wrong. */ assertEqualInt(crc32(0, buf, proper_size), 0x886F91EB); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_all) { const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; PROLOGUE(fname); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_in_part) { const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; char buf[6]; /* Skip first, extract in part rest. */ PROLOGUE(fname); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(6 == archive_read_data(a, buf, 6)); assertEqualInt(0, memcmp(buf, "Cebula", 6)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(4 == archive_read_data(a, buf, 4)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_all_but_first) { const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; char buf[405]; /* Extract first, skip rest. */ PROLOGUE(fname); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); assertA(405 == archive_read_data(a, buf, sizeof(buf))); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_all_in_part) { const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; char buf[4]; /* Extract in part all */ PROLOGUE(fname); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); assertA(4 == archive_read_data(a, buf, 4)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(4 == archive_read_data(a, buf, 4)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(4 == archive_read_data(a, buf, 4)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_extr_all) { const char* reffiles[] = { "test_read_format_rar5_multiarchive_solid.part01.rar", "test_read_format_rar5_multiarchive_solid.part02.rar", "test_read_format_rar5_multiarchive_solid.part03.rar", "test_read_format_rar5_multiarchive_solid.part04.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7E5EC49E)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7cca70cd)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7e13b2c6)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0xf166afcb)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x9fb123d9)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x10c43ed4)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0xb9d155f2)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x36a448ff)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x886F91EB)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all) { const char* reffiles[] = { "test_read_format_rar5_multiarchive_solid.part01.rar", "test_read_format_rar5_multiarchive_solid.part02.rar", "test_read_format_rar5_multiarchive_solid.part03.rar", "test_read_format_rar5_multiarchive_solid.part04.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_first) { const char* reffiles[] = { "test_read_format_rar5_multiarchive_solid.part01.rar", "test_read_format_rar5_multiarchive_solid.part02.rar", "test_read_format_rar5_multiarchive_solid.part03.rar", "test_read_format_rar5_multiarchive_solid.part04.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7E5EC49E)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } /* "skip_all_but_scnd" -> am I hitting the test name limit here after * expansion of "scnd" to "second"? */ DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_scnd) { const char* reffiles[] = { "test_read_format_rar5_multiarchive_solid.part01.rar", "test_read_format_rar5_multiarchive_solid.part02.rar", "test_read_format_rar5_multiarchive_solid.part03.rar", "test_read_format_rar5_multiarchive_solid.part04.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7CCA70CD)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_third) { const char* reffiles[] = { "test_read_format_rar5_multiarchive_solid.part01.rar", "test_read_format_rar5_multiarchive_solid.part02.rar", "test_read_format_rar5_multiarchive_solid.part03.rar", "test_read_format_rar5_multiarchive_solid.part04.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7E13B2C6)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_last) { const char* reffiles[] = { "test_read_format_rar5_multiarchive_solid.part01.rar", "test_read_format_rar5_multiarchive_solid.part02.rar", "test_read_format_rar5_multiarchive_solid.part03.rar", "test_read_format_rar5_multiarchive_solid.part04.rar", NULL }; PROLOGUE_MULTI(reffiles); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("cebula.txt", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x886F91EB)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all) { const char* reffile = "test_read_format_rar5_solid.rar"; /* Skip all */ PROLOGUE(reffile); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_first) { const char* reffile = "test_read_format_rar5_solid.rar"; /* Extract first, skip rest */ PROLOGUE(reffile); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7CCA70CD)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_second) { const char* reffile = "test_read_format_rar5_solid.rar"; /* Skip first, extract second, skip rest */ PROLOGUE(reffile); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x7E13B2C6)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_last) { const char* reffile = "test_read_format_rar5_solid.rar"; /* Skip all but last, extract last */ PROLOGUE(reffile); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0x36A448FF)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_extract_win32) { PROLOGUE("test_read_format_rar5_win32.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); assertA(0 == extract_one(a, ae, 0)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x7CCA70CD)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x7E13B2C6)); assertA(0 == archive_read_next_header(a, &ae)); /* Read only file */ assertEqualString("test2.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0444); assertA(0 == extract_one(a, ae, 0xF166AFCB)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x9FB123D9)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x10C43ED4)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0xB9D155F2)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x36A448FF)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_block_by_block) { /* This test uses strange buffer sizes intentionally. */ struct archive_entry *ae; struct archive *a; uint8_t buf[173]; int bytes_read; uint32_t computed_crc = 0; extract_reference_file("test_read_format_rar5_compressed.rar"); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_open_filename(a, "test_read_format_rar5_compressed.rar", 130)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); assertEqualInt(1200, archive_entry_size(ae)); /* File size is 1200 bytes, we're reading it using a buffer of 173 bytes. * Libarchive is configured to use a buffer of 130 bytes. */ while(1) { /* archive_read_data should return one of: * a) 0, if there is no more data to be read, * b) negative value, if there was an error, * c) positive value, meaning how many bytes were read. */ bytes_read = archive_read_data(a, buf, sizeof(buf)); assertA(bytes_read >= 0); if(bytes_read <= 0) break; computed_crc = crc32(computed_crc, buf, bytes_read); } assertEqualInt(computed_crc, 0x7CCA70CD); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_owner) { const int DATA_SIZE = 5; uint8_t buff[5]; PROLOGUE("test_read_format_rar5_owner.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("root.txt", archive_entry_pathname(ae)); assertEqualString("root", archive_entry_uname(ae)); assertEqualString("wheel", archive_entry_gname(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("nobody.txt", archive_entry_pathname(ae)); assertEqualString("nobody", archive_entry_uname(ae)); assertEqualString("nogroup", archive_entry_gname(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("numeric.txt", archive_entry_pathname(ae)); assertEqualInt(9999, archive_entry_uid(ae)); assertEqualInt(8888, archive_entry_gid(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_symlink) { const int DATA_SIZE = 5; uint8_t buff[5]; PROLOGUE("test_read_format_rar5_symlink.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("file.txt", archive_entry_pathname(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("symlink.txt", archive_entry_pathname(ae)); assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); assertEqualString("file.txt", archive_entry_symlink(ae)); assertEqualInt(AE_SYMLINK_TYPE_FILE, archive_entry_symlink_type(ae)); assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("dirlink", archive_entry_pathname(ae)); assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); assertEqualString("dir", archive_entry_symlink(ae)); assertEqualInt(AE_SYMLINK_TYPE_DIRECTORY, archive_entry_symlink_type(ae)); assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("dir", archive_entry_pathname(ae)); assertEqualInt(AE_IFDIR, archive_entry_filetype(ae)); assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_hardlink) { const int DATA_SIZE = 5; uint8_t buff[5]; PROLOGUE("test_read_format_rar5_hardlink.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("file.txt", archive_entry_pathname(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertA((int) archive_entry_mtime(ae) > 0); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("hardlink.txt", archive_entry_pathname(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualString("file.txt", archive_entry_hardlink(ae)); assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_extra_field_version) { PROLOGUE("test_read_format_rar5_extra_field_version.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("bin/2to3;1", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0xF24181B7)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("bin/2to3", archive_entry_pathname(ae)); assertA(0 == extract_one(a, ae, 0xF24181B7)); assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_readtables_overflow) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_readtables_overflow.rar"); /* This archive is invalid. However, processing it shouldn't cause any * buffer overflow errors during reading rar5 tables. */ (void) archive_read_next_header(a, &ae); (void) archive_read_data(a, buf, sizeof(buf)); (void) archive_read_next_header(a, &ae); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_leftshift1) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_leftshift1.rar"); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations when using -fsanitize. */ (void) archive_read_next_header(a, &ae); (void) archive_read_data(a, buf, sizeof(buf)); (void) archive_read_next_header(a, &ae); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_leftshift2) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_leftshift2.rar"); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations when using -fsanitize. */ (void) archive_read_next_header(a, &ae); (void) archive_read_data(a, buf, sizeof(buf)); (void) archive_read_next_header(a, &ae); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_truncated_huff) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_truncated_huff.rar"); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations when using -fsanitize. */ (void) archive_read_next_header(a, &ae); (void) archive_read_data(a, buf, sizeof(buf)); (void) archive_read_next_header(a, &ae); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_invalid_dict_reference) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_invalid_dict_reference.rar"); /* This test should fail on parsing the header. */ assertA(archive_read_next_header(a, &ae) != ARCHIVE_OK); /* This archive is invalid. However, processing it shouldn't cause any * errors related to buffer underflow when using -fsanitize. */ assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); /* This test only cares about not returning success here. */ assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_distance_overflow) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_distance_overflow.rar"); /* This archive is invalid. However, processing it shouldn't cause any * errors related to variable overflows when using -fsanitize. */ (void) archive_read_next_header(a, &ae); (void) archive_read_data(a, buf, sizeof(buf)); (void) archive_read_next_header(a, &ae); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_nonempty_dir_stream) { uint8_t buf[16]; PROLOGUE("test_read_format_rar5_nonempty_dir_stream.rar"); /* This archive is invalid. However, processing it shouldn't cause any * errors related to buffer overflows when using -fsanitize. */ (void) archive_read_next_header(a, &ae); (void) archive_read_data(a, buf, sizeof(buf)); (void) archive_read_next_header(a, &ae); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_fileattr) { unsigned long set, clear, flag; flag = 0; PROLOGUE("test_read_format_rar5_fileattr.rar"); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0444 | AE_IFREG); assertEqualString("readonly.txt", archive_entry_pathname(ae)); assertEqualString("rdonly", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_READONLY; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_READONLY; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0644 | AE_IFREG); assertEqualString("hidden.txt", archive_entry_pathname(ae)); assertEqualString("hidden", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_HIDDEN; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_HIDDEN; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0644 | AE_IFREG); assertEqualString("system.txt", archive_entry_pathname(ae)); assertEqualString("system", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_SYSTEM;; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_SYSTEM; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0444 | AE_IFREG); assertEqualString("ro_hidden.txt", archive_entry_pathname(ae)); assertEqualString("rdonly,hidden", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_READONLY | UF_HIDDEN; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0555 | AE_IFDIR); assertEqualString("dir_readonly", archive_entry_pathname(ae)); assertEqualString("rdonly", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_READONLY; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_READONLY; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0755 | AE_IFDIR); assertEqualString("dir_hidden", archive_entry_pathname(ae)); assertEqualString("hidden", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_HIDDEN; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_HIDDEN; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0755 | AE_IFDIR); assertEqualString("dir_system", archive_entry_pathname(ae)); assertEqualString("system", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_SYSTEM; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_SYSTEM; #endif assertEqualInt(flag, set & flag); assertA(0 == archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_mode(ae), 0555 | AE_IFDIR); assertEqualString("dir_rohidden", archive_entry_pathname(ae)); assertEqualString("rdonly,hidden", archive_entry_fflags_text(ae)); archive_entry_fflags(ae, &set, &clear); #if defined(__FreeBSD__) flag = UF_READONLY | UF_HIDDEN; #elif defined(_WIN32) && !defined(CYGWIN) flag = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN; #endif assertEqualInt(flag, set & flag); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_different_window_size) { char buf[4096]; PROLOGUE("test_read_format_rar5_different_window_size.rar"); /* Return codes of those calls are ignored, because this sample file * is invalid. However, the unpacker shouldn't produce any SIGSEGV * errors during processing. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_window_buf_and_size_desync) { /* oss fuzz 30442 */ char buf[4096]; PROLOGUE("test_read_format_rar5_window_buf_and_size_desync.rar"); /* Return codes of those calls are ignored, because this sample file * is invalid. However, the unpacker shouldn't produce any SIGSEGV * errors during processing. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, 46)) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_arm_filter_on_window_boundary) { char buf[4096]; PROLOGUE("test_read_format_rar5_arm_filter_on_window_boundary.rar"); /* Return codes of those calls are ignored, because this sample file * is invalid. However, the unpacker shouldn't produce any SIGSEGV * errors during processing. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_different_solid_window_size) { char buf[4096]; PROLOGUE("test_read_format_rar5_different_solid_window_size.rar"); /* Return codes of those calls are ignored, because this sample file * is invalid. However, the unpacker shouldn't produce any SIGSEGV * errors during processing. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_different_winsize_on_merge) { char buf[4096]; PROLOGUE("test_read_format_rar5_different_winsize_on_merge.rar"); /* Return codes of those calls are ignored, because this sample file * is invalid. However, the unpacker shouldn't produce any SIGSEGV * errors during processing. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_block_size_is_too_small) { char buf[4096]; PROLOGUE("test_read_format_rar5_block_size_is_too_small.rar"); /* This file is damaged, so those functions should return failure. * Additionally, SIGSEGV shouldn't be raised during execution * of those functions. */ assertA(archive_read_next_header(a, &ae) != ARCHIVE_OK); assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_sfx) { struct archive *a; struct archive_entry *ae; int bs = 10240; char buff[32]; const char reffile[] = "test_read_format_rar5_sfx.exe"; const char test_txt[] = "123"; int size = sizeof(test_txt) - 1; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_open_filename(a, reffile, bs)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt.txt", archive_entry_pathname(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); + + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_decode_number_out_of_bounds_read) { /* oss fuzz 30448 */ char buf[4096]; PROLOGUE("test_read_format_rar5_decode_number_out_of_bounds_read.rar"); /* Return codes of those calls are ignored, because this sample file * is invalid. However, the unpacker shouldn't produce any SIGSEGV * errors during processing. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_bad_window_size_in_multiarchive_file) { /* oss fuzz 30459 */ char buf[4096]; PROLOGUE("test_read_format_rar5_bad_window_sz_in_mltarc_file.rar"); /* This file is damaged, so those functions should return failure. * Additionally, SIGSEGV shouldn't be raised during execution * of those functions. */ (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_read_data_block_uninitialized_offset) { const void *buf; size_t size; la_int64_t offset; PROLOGUE("test_read_format_rar5_compressed.rar"); assertA(0 == archive_read_next_header(a, &ae)); /* A real code may pass a pointer to an uninitialized variable as an offset * output argument. Here we want to check this situation. But because * relying on a value of an uninitialized variable in a test is not a good * idea, let's pretend that 0xdeadbeef is a random value of the * uninitialized variable. */ offset = 0xdeadbeef; assertEqualInt(ARCHIVE_OK, archive_read_data_block(a, &buf, &size, &offset)); /* The test archive doesn't contain a sparse file. And because of that, here * we assume that the first returned offset should be 0. */ assertEqualInt(0, offset); EPILOGUE(); } diff --git a/contrib/libarchive/libarchive/test/test_read_format_zip.c b/contrib/libarchive/libarchive/test/test_read_format_zip.c index 303637dd3fe6..0bd9c1407ab7 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_zip.c +++ b/contrib/libarchive/libarchive/test/test_read_format_zip.c @@ -1,1170 +1,1175 @@ /*- * 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_zstd_one_file) { const char *refname = "test_read_format_zip_zstd.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { skipping("zstd is not fully supported on this platform"); archive_read_close(a); + 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 (zstd)", 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_zstd_one_file_blockread) { const char *refname = "test_read_format_zip_zstd.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { skipping("zstd is not fully supported on this platform"); archive_read_close(a); + 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 (zstd)", 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_zstd_multi) { const char *refname = "test_read_format_zip_zstd_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { skipping("zstd is not fully supported on this platform"); archive_read_close(a); + 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 (zstd)", 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 2.0 (zstd)", 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 2.0 (zstd)", 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_zstd_multi_blockread) { const char *refname = "test_read_format_zip_zstd_multi.zipx"; struct archive *a; struct archive_entry *ae; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_filter_zstd(a)) { skipping("zstd is not fully supported on this platform"); archive_read_close(a); + 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 (zstd)", 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 2.0 (zstd)", 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 2.0 (zstd)", 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); + archive_read_free(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)); } DEFINE_TEST(test_read_format_zip_7z_deflate) { const char *refname = "test_read_format_zip_7z_deflate.zip"; struct archive_entry *ae; struct archive *a; int r; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); 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 first symlink r = archive_read_next_header(a, &ae); if (archive_zlib_version() == NULL) { assertEqualInt(ARCHIVE_FAILED, r); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method during decompression " "of link entry (8: deflation)"); assert(archive_errno(a) != 0); } else { assertEqualIntA(a, ARCHIVE_OK, r); assertEqualString("libxkbcommon-x11.so.0.0.0", archive_entry_symlink(ae)); } assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); //read second symlink r = archive_read_next_header(a, &ae); if (archive_zlib_version() == NULL) { assertEqualInt(ARCHIVE_FAILED, r); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method during decompression " "of link entry (8: deflation)"); assert(archive_errno(a) != 0); } else { assertEqualIntA(a, ARCHIVE_OK, r); assertEqualString("libxkbcommon-x11.so.0.0.0", archive_entry_symlink(ae)); } assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } diff --git a/contrib/libarchive/libarchive/test/test_short_writes.c b/contrib/libarchive/libarchive/test/test_short_writes.c index afa0206f07cd..8221cece1721 100644 --- a/contrib/libarchive/libarchive/test/test_short_writes.c +++ b/contrib/libarchive/libarchive/test/test_short_writes.c @@ -1,216 +1,218 @@ /*- * Copyright (c) 2021 Red Hat, Inc. * 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 #include #include /* * This test checks whether things work correctly when the archive_write_callback * passed to archive_write_open() does a short write and only writes some of the * data passed in. The way the test works is that two archives are constructed * in parallel - one with short writes, one with full writes - and the results * are compared to see if they are identical. */ struct checker { struct archive *short_archive; char *shortbuf; size_t shortbuf_len; struct archive *full_archive; char *fullbuf; size_t fullbuf_len; }; static ssize_t short_write_callback(struct archive *a, void *client_data, const void *buffer, size_t length) { (void)a; struct checker *checker = client_data; size_t to_write = length < 100 ? length : 100; size_t new_len = checker->shortbuf_len + to_write; char *new_buf = realloc(checker->shortbuf, new_len); assert(new_buf != NULL); checker->shortbuf = new_buf; memcpy(checker->shortbuf + checker->shortbuf_len, buffer, to_write); checker->shortbuf_len = new_len; return to_write; } static ssize_t full_write_callback(struct archive *a, void *client_data, const void *buffer, size_t length) { (void)a; struct checker *checker = client_data; size_t to_write = length; size_t new_len = checker->fullbuf_len + to_write; char *new_buf = realloc(checker->fullbuf, new_len); assert(new_buf != NULL); checker->fullbuf = new_buf; memcpy(checker->fullbuf + checker->fullbuf_len, buffer, to_write); checker->fullbuf_len = new_len; return to_write; } static struct archive * create_archive(struct checker *checker, archive_write_callback write_cb, int buffered) { struct archive *a; assert((a = archive_write_new()) != NULL); if (!buffered) assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 0)); /* With the default value of bytes_in_last_block, the writing code will * pad out the final write to make it a full block. This causes problems * for us because the size of the final write can be different depending * on the size of previous writes, causing the "short" and "full" paths * to get different amounts of padding. Setting it to 1 results in no * padding other than that defined by the archive format. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); /* We write a pax archive, but other formats would work fine too. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open(a, checker, NULL, write_cb, NULL)); return a; } static struct checker * checker_new(int buffered) { struct checker *checker; assert ((checker = calloc(1, sizeof *checker)) != NULL); checker->short_archive = create_archive(checker, short_write_callback, buffered); checker->full_archive = create_archive(checker, full_write_callback, buffered); return checker; } static void checker_add_file(struct checker *checker, const char *name, char *buffer, size_t len) { struct archive_entry *entry; assert((entry = archive_entry_new()) != NULL); archive_entry_set_pathname(entry, name); archive_entry_set_mode(entry, AE_IFREG | 0755); archive_entry_set_size(entry, len); assertEqualIntA(checker->short_archive, ARCHIVE_OK, archive_write_header(checker->short_archive, entry)); assertEqualIntA(checker->short_archive, len, archive_write_data(checker->short_archive, buffer, len)); assertEqualIntA(checker->full_archive, ARCHIVE_OK, archive_write_header(checker->full_archive, entry)); assertEqualIntA(checker->full_archive, len, archive_write_data(checker->full_archive, buffer, len)); archive_entry_free(entry); } static void checker_close(struct checker *checker) { assertEqualIntA(checker->short_archive, ARCHIVE_OK, archive_write_close(checker->short_archive)); assertEqualIntA(checker->short_archive, ARCHIVE_OK, archive_write_close(checker->full_archive)); } static void checker_check(struct checker *checker) { assertEqualInt(checker->shortbuf_len, checker->fullbuf_len); assert(memcmp(checker->shortbuf, checker->fullbuf, checker->fullbuf_len) == 0); } static void checker_free(struct checker *checker) { free(checker->shortbuf); free(checker->fullbuf); + archive_read_free(checker->short_archive); + archive_read_free(checker->full_archive); free(checker); } DEFINE_TEST(test_short_writes) { struct checker *checker; uint16_t test_data[16384]; int i; for (i = 0; i < 16384; i++) test_data[i] = i; /* Write a file smaller than the default buffer size (10 * 1024); * this will be written out at close. */ checker = checker_new(1); checker_add_file(checker, "a", (char *)test_data, 1024); checker_close(checker); assert(checker->shortbuf_len > 1024); checker_check(checker); checker_free(checker); /* Write a file larger larger than twice default buffer size (10 * 1024); * this both fills the buffer and writes it out, and also exercises * the "write out full blocks directly" code path. */ checker = checker_new(1); checker_add_file(checker, "a", (char *)test_data, 21 * 1024); checker_close(checker); assert(checker->shortbuf_len > 21 * 1024); checker_check(checker); checker_free(checker); /* Test unbuffered writes - a different code path. */ checker = checker_new(0); checker_add_file(checker, "a", (char *)test_data, 1024); checker_close(checker); assert(checker->shortbuf_len > 1024); checker_check(checker); checker_free(checker); } diff --git a/contrib/libarchive/libarchive/test/test_write_filter_zstd.c b/contrib/libarchive/libarchive/test/test_write_filter_zstd.c index 263cea5bc49e..aa77b49860c0 100644 --- a/contrib/libarchive/libarchive/test/test_write_filter_zstd.c +++ b/contrib/libarchive/libarchive/test/test_write_filter_zstd.c @@ -1,262 +1,289 @@ /*- * Copyright (c) 2017 Sean Purcell * 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_write_filter_zstd) { struct archive_entry *ae; struct archive *a; char *buff, *data; size_t buffsize, datasize; char path[16]; size_t used1, used2, used3; int i, r; buffsize = 2000000; assert(NULL != (buff = (char *)malloc(buffsize))); if (buff == NULL) return; datasize = 10000; assert(NULL != (data = (char *)malloc(datasize))); if (data == NULL) { free(buff); return; } memset(data, 0, datasize); /* * Write a 100 files and read them all back. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); r = archive_write_add_filter_zstd(a); if (r != ARCHIVE_OK) { skipping("zstd writing not supported on this platform"); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); free(buff); free(data); return; } assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); assertEqualInt(ARCHIVE_FILTER_ZSTD, archive_filter_code(a, 0)); assertEqualString("zstd", archive_filter_name(a, 0)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used1)); assertEqualInt(ARCHIVE_FILTER_ZSTD, archive_filter_code(a, 0)); assertEqualString("zstd", archive_filter_name(a, 0)); assert((ae = archive_entry_new()) != NULL); archive_entry_set_filetype(ae, AE_IFREG); archive_entry_set_size(ae, datasize); for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "file%03d", i); archive_entry_copy_pathname(ae, path); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); assertA(datasize == (size_t)archive_write_data(a, data, datasize)); } archive_entry_free(ae); assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); r = archive_read_support_filter_zstd(a); if (r == ARCHIVE_WARN) { skipping("Can't verify zstd writing by reading back;" " zstd reading not fully supported on this platform"); } else { assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used1)); for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "file%03d", i); if (!assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &ae))) break; assertEqualString(path, archive_entry_pathname(ae)); assertEqualInt((int)datasize, archive_entry_size(ae)); } assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* * Repeat the cycle again, this time setting some compression * options. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_zstd(a)); assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, NULL, "nonexistent-option", "0")); assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, NULL, "compression-level", "abc")); assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, NULL, "compression-level", "25")); /* too big */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, NULL, "compression-level", "9")); /* Following is disabled as it will fail on library versions < 1.3.4 */ /* assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, NULL, "compression-level", "-1")); */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, NULL, "compression-level", "7")); assertEqualIntA(a, ARCHIVE_FAILED, archive_write_set_filter_option(a, NULL, "threads", "-1")); /* negative */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, NULL, "threads", "4")); +#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + /* frame-per-file: boolean */ + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "frame-per-file", "")); + /* min-frame-size: >= 0 */ + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "min-frame-size", "")); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "min-frame-size", "-1")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "min-frame-size", "0")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "min-frame-size", "1048576")); + /* max-frame-size: >= 1024 */ + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "max-frame-size", "")); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "max-frame-size", "-1")); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "max-frame-size", "0")); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_set_filter_option(a, NULL, "max-frame-size", "1023")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "max-frame-size", "1024")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_filter_option(a, NULL, "max-frame-size", "1048576")); +#endif assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used2)); for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "file%03d", i); assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, path); archive_entry_set_size(ae, datasize); archive_entry_set_filetype(ae, AE_IFREG); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); assertA(datasize == (size_t)archive_write_data(a, data, datasize)); archive_entry_free(ae); } assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); r = archive_read_support_filter_zstd(a); if (r == ARCHIVE_WARN) { skipping("zstd reading not fully supported on this platform"); } else { assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used2)); for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "file%03d", i); failure("Trying to read %s", path); if (!assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae))) break; assertEqualString(path, archive_entry_pathname(ae)); assertEqualInt((int)datasize, archive_entry_size(ae)); } assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* * One more time at level 1 */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 10)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_zstd(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_filter_option(a, NULL, "compression-level", "1")); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used3)); assert((ae = archive_entry_new()) != NULL); archive_entry_set_filetype(ae, AE_IFREG); archive_entry_set_size(ae, datasize); for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "file%03d", i); archive_entry_copy_pathname(ae, path); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); assertA(datasize == (size_t)archive_write_data(a, data, datasize)); } archive_entry_free(ae); assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); r = archive_read_support_filter_zstd(a); if (r == ARCHIVE_WARN) { skipping("zstd reading not fully supported on this platform"); } else { assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used3)); for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "file%03d", i); failure("Trying to read %s", path); if (!assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae))) break; assertEqualString(path, archive_entry_pathname(ae)); assertEqualInt((int)datasize, archive_entry_size(ae)); } assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); } assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* * Check output sizes for various compression levels, expectation * is that archive size for level=7 < default < level=1 */ failure("compression-level=7 wrote %d bytes, default wrote %d bytes", (int)used2, (int)used1); assert(used2 < used1); failure("compression-level=1 wrote %d bytes, default wrote %d bytes", (int)used3, (int)used1); assert(used1 < used3); /* * Test various premature shutdown scenarios to make sure we * don't crash or leak memory. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_zstd(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_zstd(a)); assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_zstd(a)); assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_zstd(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used2)); assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* * Clean up. */ free(data); free(buff); } diff --git a/contrib/libarchive/libarchive/test/test_write_format_zip_compression_store.c b/contrib/libarchive/libarchive/test/test_write_format_zip_compression_store.c index ed0908787579..b52d170ccea0 100644 --- a/contrib/libarchive/libarchive/test/test_write_format_zip_compression_store.c +++ b/contrib/libarchive/libarchive/test/test_write_format_zip_compression_store.c @@ -1,405 +1,396 @@ /*- * Copyright (c) 2008 Anselm Strauss * 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 "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_format_zip_no_compression.c 201247 2009-12-30 05:59:21Z kientzle $"); /* File data */ static const char file_name[] = "file"; static const char file_data1[] = {'1', '2', '3', '4', '5'}; static const char file_data2[] = {'6', '7', '8', '9', '0'}; static const int file_perm = 00644; static const short file_uid = 10; static const short file_gid = 20; /* Folder data */ static const char folder_name[] = "folder/"; static const int folder_perm = 00755; static const short folder_uid = 30; static const short folder_gid = 40; static time_t now; static unsigned long bitcrc32(unsigned long c, const void *_p, size_t s) { /* This is a drop-in replacement for crc32() from zlib. * Libarchive should be able to correctly generate * uncompressed zip archives (including correct CRCs) even * when zlib is unavailable, and this function helps us verify * that. Yes, this is very, very slow and unsuitable for * production use, but it's correct, compact, and works well * enough for this particular usage. Libarchive internally * uses a much more efficient implementation. */ const unsigned char *p = _p; int bitctr; if (p == NULL) return (0); for (; s > 0; --s) { c ^= *p++; for (bitctr = 8; bitctr > 0; --bitctr) { if (c & 1) c = (c >> 1); else c = (c >> 1) ^ 0xedb88320; c ^= 0x80000000; } } return (c); } static void verify_write_uncompressed(struct archive *a) { struct archive_entry *entry; /* Write entries. */ /* Regular file */ assert((entry = archive_entry_new()) != NULL); archive_entry_set_pathname(entry, file_name); archive_entry_set_mode(entry, S_IFREG | 0644); archive_entry_set_size(entry, sizeof(file_data1) + sizeof(file_data2)); archive_entry_set_uid(entry, file_uid); archive_entry_set_gid(entry, file_gid); archive_entry_set_mtime(entry, now, 0); archive_entry_set_atime(entry, now + 3, 0); assertEqualIntA(a, 0, archive_write_header(a, entry)); assertEqualIntA(a, sizeof(file_data1), archive_write_data(a, file_data1, sizeof(file_data1))); assertEqualIntA(a, sizeof(file_data2), archive_write_data(a, file_data2, sizeof(file_data2))); archive_entry_free(entry); /* Folder */ assert((entry = archive_entry_new()) != NULL); archive_entry_set_pathname(entry, folder_name); archive_entry_set_mode(entry, S_IFDIR | folder_perm); archive_entry_set_size(entry, 0); archive_entry_set_uid(entry, folder_uid); archive_entry_set_gid(entry, folder_gid); archive_entry_set_mtime(entry, now, 0); archive_entry_set_ctime(entry, now + 5, 0); assertEqualIntA(a, 0, archive_write_header(a, entry)); archive_entry_free(entry); } /* Quick and dirty: Read 2-byte and 4-byte integers from Zip file. */ static unsigned int i2(const void *p_) { const unsigned char *p = p_; return (p[0] | (p[1] << 8)); } static unsigned int i4(const void *p_) { const unsigned char *p = p_; return (i2(p) | (i2(p + 2) << 16)); } static void verify_uncompressed_contents(const char *buff, size_t used) { const char *buffend; /* Misc variables */ unsigned long crc; struct tm *tm; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; -#endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; #endif /* p is the pointer to walk over the central directory, * q walks over the local headers, the data and the data descriptors. */ const char *p, *q, *local_header, *extra_start; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &now) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&now, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = now; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = localtime(&now); #endif /* Remember the end of the archive in memory. */ buffend = buff + used; /* Verify "End of Central Directory" record. */ /* Get address of end-of-central-directory record. */ p = buffend - 22; /* Assumes there is no zip comment field. */ failure("End-of-central-directory begins with PK\\005\\006 signature"); assertEqualMem(p, "PK\005\006", 4); failure("This must be disk 0"); assertEqualInt(i2(p + 4), 0); failure("Central dir must start on disk 0"); assertEqualInt(i2(p + 6), 0); failure("All central dir entries are on this disk"); assertEqualInt(i2(p + 8), i2(p + 10)); failure("CD start (%d) + CD length (%d) should == archive size - 22", i4(p + 12), i4(p + 16)); assertEqualInt(i4(p + 12) + i4(p + 16), used - 22); failure("no zip comment"); assertEqualInt(i2(p + 20), 0); /* Get address of first entry in central directory. */ p = buff + i4(buffend - 6); failure("Central file record at offset %d should begin with" " PK\\001\\002 signature", i4(buffend - 10)); /* Verify file entry in central directory. */ assertEqualMem(p, "PK\001\002", 4); /* Signature */ assertEqualInt(i2(p + 4), 3 * 256 + 10); /* Version made by */ assertEqualInt(i2(p + 6), 10); /* Version needed to extract */ assertEqualInt(i2(p + 8), 8); /* Flags */ assertEqualInt(i2(p + 10), 0); /* Compression method */ assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ crc = bitcrc32(0, file_data1, sizeof(file_data1)); crc = bitcrc32(crc, file_data2, sizeof(file_data2)); assertEqualInt(i4(p + 16), crc); /* CRC-32 */ assertEqualInt(i4(p + 20), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ assertEqualInt(i4(p + 24), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */ assertEqualInt(i2(p + 30), 28); /* Extra field length */ assertEqualInt(i2(p + 32), 0); /* File comment length */ assertEqualInt(i2(p + 34), 0); /* Disk number start */ assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ assertEqualInt(i4(p + 38) >> 16 & 01777, file_perm); /* External file attrs */ assertEqualInt(i4(p + 42), 0); /* Offset of local header */ assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */ p = p + 46 + strlen(file_name); assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(p + 2), 9); /* 'UT' size */ assertEqualInt(p[4], 3); /* 'UT' flags */ assertEqualInt(i4(p + 5), now); /* 'UT' mtime */ assertEqualInt(i4(p + 9), now + 3); /* 'UT' atime */ p = p + 4 + i2(p + 2); assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(p + 2), 11); /* 'ux' size */ /* TODO */ p = p + 4 + i2(p + 2); /* Verify local header of file entry. */ local_header = q = buff; assertEqualMem(q, "PK\003\004", 4); /* Signature */ assertEqualInt(i2(q + 4), 10); /* Version needed to extract */ assertEqualInt(i2(q + 6), 8); /* Flags */ assertEqualInt(i2(q + 8), 0); /* Compression method */ assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ assertEqualInt(i4(q + 14), 0); /* CRC-32 */ assertEqualInt(i4(q + 18), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ assertEqualInt(i4(q + 22), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ assertEqualInt(i2(q + 26), strlen(file_name)); /* Pathname length */ assertEqualInt(i2(q + 28), 41); /* Extra field length */ assertEqualMem(q + 30, file_name, strlen(file_name)); /* Pathname */ extra_start = q = q + 30 + strlen(file_name); assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(q + 2), 9); /* 'UT' size */ assertEqualInt(q[4], 3); /* 'UT' flags */ assertEqualInt(i4(q + 5), now); /* 'UT' mtime */ assertEqualInt(i4(q + 9), now + 3); /* 'UT' atime */ q = q + 4 + i2(q + 2); assertEqualInt(i2(q), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(q + 2), 11); /* 'ux' size */ assertEqualInt(q[4], 1); /* 'ux' version */ assertEqualInt(q[5], 4); /* 'ux' uid size */ assertEqualInt(i4(q + 6), file_uid); /* 'Ux' UID */ assertEqualInt(q[10], 4); /* 'ux' gid size */ assertEqualInt(i4(q + 11), file_gid); /* 'Ux' GID */ q = q + 4 + i2(q + 2); assertEqualInt(i2(q), 0x6c78); /* 'xl' experimental extension header */ assertEqualInt(i2(q + 2), 9); /* size */ assertEqualInt(q[4], 7); /* Bitmap of fields included. */ assertEqualInt(i2(q + 5) >> 8, 3); /* system & version made by */ assertEqualInt(i2(q + 7), 0); /* internal file attributes */ assertEqualInt(i4(q + 9) >> 16 & 01777, file_perm); /* external file attributes */ q = q + 4 + i2(q + 2); assert(q == extra_start + i2(local_header + 28)); q = extra_start + i2(local_header + 28); /* Verify data of file entry. */ assertEqualMem(q, file_data1, sizeof(file_data1)); assertEqualMem(q + sizeof(file_data1), file_data2, sizeof(file_data2)); q = q + sizeof(file_data1) + sizeof(file_data2); /* Verify data descriptor of file entry. */ assertEqualMem(q, "PK\007\010", 4); /* Signature */ assertEqualInt(i4(q + 4), crc); /* CRC-32 */ assertEqualInt(i4(q + 8), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ assertEqualInt(i4(q + 12), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ q = q + 16; /* Verify folder entry in central directory. */ assertEqualMem(p, "PK\001\002", 4); /* Signature */ assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */ assertEqualInt(i2(p + 6), 20); /* Version needed to extract */ assertEqualInt(i2(p + 8), 0); /* Flags */ assertEqualInt(i2(p + 10), 0); /* Compression method */ assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ crc = 0; assertEqualInt(i4(p + 16), crc); /* CRC-32 */ assertEqualInt(i4(p + 20), 0); /* Compressed size */ assertEqualInt(i4(p + 24), 0); /* Uncompressed size */ assertEqualInt(i2(p + 28), strlen(folder_name)); /* Pathname length */ assertEqualInt(i2(p + 30), 28); /* Extra field length */ assertEqualInt(i2(p + 32), 0); /* File comment length */ assertEqualInt(i2(p + 34), 0); /* Disk number start */ assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ assertEqualInt(i4(p + 38) >> 16 & 01777, folder_perm); /* External file attrs */ assertEqualInt(i4(p + 42), q - buff); /* Offset of local header */ assertEqualMem(p + 46, folder_name, strlen(folder_name)); /* Pathname */ p = p + 46 + strlen(folder_name); assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(p + 2), 9); /* 'UT' size */ assertEqualInt(p[4], 5); /* 'UT' flags */ assertEqualInt(i4(p + 5), now); /* 'UT' mtime */ assertEqualInt(i4(p + 9), now + 5); /* 'UT' atime */ p = p + 4 + i2(p + 2); assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(p + 2), 11); /* 'ux' size */ assertEqualInt(p[4], 1); /* 'ux' version */ assertEqualInt(p[5], 4); /* 'ux' uid size */ assertEqualInt(i4(p + 6), folder_uid); /* 'ux' UID */ assertEqualInt(p[10], 4); /* 'ux' gid size */ assertEqualInt(i4(p + 11), folder_gid); /* 'ux' GID */ /*p = p + 4 + i2(p + 2);*/ /* Verify local header of folder entry. */ local_header = q; assertEqualMem(q, "PK\003\004", 4); /* Signature */ assertEqualInt(i2(q + 4), 20); /* Version needed to extract */ assertEqualInt(i2(q + 6), 0); /* Flags */ assertEqualInt(i2(q + 8), 0); /* Compression method */ assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ assertEqualInt(i4(q + 14), 0); /* CRC-32 */ assertEqualInt(i4(q + 18), 0); /* Compressed size */ assertEqualInt(i4(q + 22), 0); /* Uncompressed size */ assertEqualInt(i2(q + 26), strlen(folder_name)); /* Pathname length */ assertEqualInt(i2(q + 28), 41); /* Extra field length */ assertEqualMem(q + 30, folder_name, strlen(folder_name)); /* Pathname */ extra_start = q = q + 30 + strlen(folder_name); assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(q + 2), 9); /* 'UT' size */ assertEqualInt(q[4], 5); /* 'UT' flags */ assertEqualInt(i4(q + 5), now); /* 'UT' mtime */ assertEqualInt(i4(q + 9), now + 5); /* 'UT' atime */ q = q + 4 + i2(q + 2); assertEqualInt(i2(q), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(q + 2), 11); /* 'ux' size */ assertEqualInt(q[4], 1); /* 'ux' version */ assertEqualInt(q[5], 4); /* 'ux' uid size */ assertEqualInt(i4(q + 6), folder_uid); /* 'ux' UID */ assertEqualInt(q[10], 4); /* 'ux' gid size */ assertEqualInt(i4(q + 11), folder_gid); /* 'ux' GID */ q = q + 4 + i2(q + 2); assertEqualInt(i2(q), 0x6c78); /* 'xl' experimental extension header */ assertEqualInt(i2(q + 2), 9); /* size */ assertEqualInt(q[4], 7); /* bitmap of fields */ assertEqualInt(i2(q + 5) >> 8, 3); /* system & version made by */ assertEqualInt(i2(q + 7), 0); /* internal file attributes */ assertEqualInt(i4(q + 9) >> 16 & 01777, folder_perm); /* external file attributes */ q = q + 4 + i2(q + 2); assert(q == extra_start + i2(local_header + 28)); q = extra_start + i2(local_header + 28); /* There should not be any data in the folder entry, * so the first central directory entry should be next: */ assertEqualMem(q, "PK\001\002", 4); /* Signature */ } DEFINE_TEST(test_write_format_zip_compression_store) { /* Buffer data */ struct archive *a; char buff[100000]; size_t used; /* Time data */ now = time(NULL); /* Create new ZIP archive in memory without padding. */ /* Use compression=store to disable compression. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:compression=store")); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:experimental")); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff), &used)); verify_write_uncompressed(a); /* Close the archive . */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); dumpfile("constructed.zip", buff, used); verify_uncompressed_contents(buff, used); /* Create new ZIP archive in memory without padding. */ /* Use compression-level=0 to disable compression. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:compression-level=0")); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:experimental")); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff), &used)); verify_write_uncompressed(a); /* Close the archive . */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); dumpfile("constructed.zip", buff, used); verify_uncompressed_contents(buff, used); } diff --git a/contrib/libarchive/libarchive/test/test_write_format_zip_file.c b/contrib/libarchive/libarchive/test/test_write_format_zip_file.c index 7796a48cdac3..4ccc30360691 100644 --- a/contrib/libarchive/libarchive/test/test_write_format_zip_file.c +++ b/contrib/libarchive/libarchive/test/test_write_format_zip_file.c @@ -1,270 +1,261 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * Copyright (c) 2008 Anselm Strauss * 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 "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_format_zip.c 201247 2009-12-30 05:59:21Z kientzle $"); /* * Detailed byte-for-byte verification of the format of a zip archive * with a single file written to it. */ static unsigned long bitcrc32(unsigned long c, void *_p, size_t s) { /* This is a drop-in replacement for crc32() from zlib. * Libarchive should be able to correctly generate * uncompressed zip archives (including correct CRCs) even * when zlib is unavailable, and this function helps us verify * that. Yes, this is very, very slow and unsuitable for * production use, but it's correct, compact, and works well * enough for this particular usage. Libarchive internally * uses a much more efficient implementation. */ const unsigned char *p = _p; int bitctr; if (p == NULL) return (0); for (; s > 0; --s) { c ^= *p++; for (bitctr = 8; bitctr > 0; --bitctr) { if (c & 1) c = (c >> 1); else c = (c >> 1) ^ 0xedb88320; c ^= 0x80000000; } } return (c); } /* Quick and dirty: Read 2-byte and 4-byte integers from Zip file. */ static unsigned i2(const unsigned char *p) { return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); } static unsigned i4(const unsigned char *p) { return (i2(p) | (i2(p + 2) << 16)); } DEFINE_TEST(test_write_format_zip_file) { struct archive *a; struct archive_entry *ae; time_t t = 1234567890; struct tm *tm; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; -#endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; #endif size_t used, buffsize = 1000000; unsigned long crc; int file_perm = 00644; int zip_version = 20; int zip_compression = 8; short file_uid = 10, file_gid = 20; unsigned char *buff, *buffend, *p; unsigned char *central_header, *local_header, *eocd, *eocd_record; unsigned char *extension_start, *extension_end; char file_data[] = {'1', '2', '3', '4', '5', '6', '7', '8'}; const char *file_name = "file"; #ifndef HAVE_ZLIB_H zip_version = 10; zip_compression = 0; #endif -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif 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 buff = malloc(buffsize); /* Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:experimental")); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used)); assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, file_name); archive_entry_set_mode(ae, AE_IFREG | file_perm); archive_entry_set_size(ae, sizeof(file_data)); archive_entry_set_uid(ae, file_uid); archive_entry_set_gid(ae, file_gid); archive_entry_set_mtime(ae, t, 0); assertEqualInt(0, archive_write_header(a, ae)); archive_entry_free(ae); assertEqualInt(8, archive_write_data(a, file_data, sizeof(file_data))); assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); buffend = buff + used; dumpfile("constructed.zip", buff, used); /* Verify "End of Central Directory" record. */ /* Get address of end-of-central-directory record. */ eocd_record = p = buffend - 22; /* Assumes there is no zip comment field. */ failure("End-of-central-directory begins with PK\\005\\006 signature"); assertEqualMem(p, "PK\005\006", 4); failure("This must be disk 0"); assertEqualInt(i2(p + 4), 0); failure("Central dir must start on disk 0"); assertEqualInt(i2(p + 6), 0); failure("All central dir entries are on this disk"); assertEqualInt(i2(p + 8), i2(p + 10)); eocd = buff + i4(p + 12) + i4(p + 16); failure("no zip comment"); assertEqualInt(i2(p + 20), 0); /* Get address of first entry in central directory. */ central_header = p = buff + i4(buffend - 6); failure("Central file record at offset %d should begin with" " PK\\001\\002 signature", i4(buffend - 10)); /* Verify file entry in central directory. */ assertEqualMem(p, "PK\001\002", 4); /* Signature */ assertEqualInt(i2(p + 4), 3 * 256 + zip_version); /* Version made by */ assertEqualInt(i2(p + 6), zip_version); /* Version needed to extract */ assertEqualInt(i2(p + 8), 8); /* Flags */ assertEqualInt(i2(p + 10), zip_compression); /* Compression method */ assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ crc = bitcrc32(0, file_data, sizeof(file_data)); assertEqualInt(i4(p + 16), crc); /* CRC-32 */ /* assertEqualInt(i4(p + 20), sizeof(file_data)); */ /* Compressed size */ assertEqualInt(i4(p + 24), sizeof(file_data)); /* Uncompressed size */ assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */ /* assertEqualInt(i2(p + 30), 28); */ /* Extra field length: See below */ assertEqualInt(i2(p + 32), 0); /* File comment length */ assertEqualInt(i2(p + 34), 0); /* Disk number start */ assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ assertEqualInt(i4(p + 38) >> 16 & 01777, file_perm); /* External file attrs */ assertEqualInt(i4(p + 42), 0); /* Offset of local header */ assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */ p = extension_start = central_header + 46 + strlen(file_name); extension_end = extension_start + i2(central_header + 30); assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(p + 2), 5); /* 'UT' size */ assertEqualInt(p[4], 1); /* 'UT' flags */ assertEqualInt(i4(p + 5), t); /* 'UT' mtime */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(p + 2), 11); /* 'ux' size */ /* TODO: verify 'ux' contents */ p += 4 + i2(p + 2); /* Just in case: Report any extra extensions. */ while (p < extension_end) { failure("Unexpected extension 0x%04X", i2(p)); assert(0); p += 4 + i2(p + 2); } /* Should have run exactly to end of extra data. */ assert(p == extension_end); assert(p == eocd); /* Regular EOCD immediately follows central directory. */ assert(p == eocd_record); /* Verify local header of file entry. */ p = local_header = buff; assertEqualMem(p, "PK\003\004", 4); /* Signature */ assertEqualInt(i2(p + 4), zip_version); /* Version needed to extract */ assertEqualInt(i2(p + 6), 8); /* Flags */ assertEqualInt(i2(p + 8), zip_compression); /* Compression method */ assertEqualInt(i2(p + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(p + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ assertEqualInt(i4(p + 14), 0); /* CRC-32 */ /* assertEqualInt(i4(p + 18), sizeof(file_data)); */ /* Compressed size */ /* assertEqualInt(i4(p + 22), sizeof(file_data)); */ /* Uncompressed size not stored because we're using length-at-end. */ assertEqualInt(i2(p + 26), strlen(file_name)); /* Pathname length */ assertEqualInt(i2(p + 28), 37); /* Extra field length */ assertEqualMem(p + 30, file_name, strlen(file_name)); /* Pathname */ p = extension_start = local_header + 30 + strlen(file_name); extension_end = extension_start + i2(local_header + 28); assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(p + 2), 5); /* size */ assertEqualInt(p[4], 1); /* 'UT' flags */ assertEqualInt(i4(p + 5), t); /* 'UT' mtime */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(p + 2), 11); /* size */ assertEqualInt(p[4], 1); /* 'ux' version */ assertEqualInt(p[5], 4); /* 'ux' uid size */ assertEqualInt(i4(p + 6), file_uid); /* 'Ux' UID */ assertEqualInt(p[10], 4); /* 'ux' gid size */ assertEqualInt(i4(p + 11), file_gid); /* 'Ux' GID */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x6c78); /* 'xl' experimental extension block */ assertEqualInt(i2(p + 2), 9); /* size */ assertEqualInt(p[4], 7); /* bitmap of fields in this block */ assertEqualInt(i2(p + 5) >> 8, 3); /* System & version made by */ assertEqualInt(i2(p + 7), 0); /* internal file attributes */ assertEqualInt(i4(p + 9) >> 16 & 01777, file_perm); /* external file attributes */ p += 4 + i2(p + 2); /* Just in case: Report any extra extensions. */ while (p < extension_end) { failure("Unexpected extension 0x%04X", i2(p)); assert(0); p += 4 + i2(p + 2); } /* Should have run exactly to end of extra data. */ assert(p == extension_end); /* Data descriptor should follow compressed data. */ while (p < central_header && memcmp(p, "PK\007\010", 4) != 0) ++p; assertEqualMem(p, "PK\007\010", 4); assertEqualInt(i4(p + 4), crc); /* CRC-32 */ /* assertEqualInt(i4(p + 8), ???); */ /* compressed size */ assertEqualInt(i4(p + 12), sizeof(file_data)); /* uncompressed size */ /* Central directory should immediately follow the only entry. */ assert(p + 16 == central_header); free(buff); } diff --git a/contrib/libarchive/libarchive/test/test_write_format_zip_file_zip64.c b/contrib/libarchive/libarchive/test/test_write_format_zip_file_zip64.c index c4161bc3a89b..6a00fe14c389 100644 --- a/contrib/libarchive/libarchive/test/test_write_format_zip_file_zip64.c +++ b/contrib/libarchive/libarchive/test/test_write_format_zip_file_zip64.c @@ -1,304 +1,295 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * Copyright (c) 2008 Anselm Strauss * 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 "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_format_zip.c 201247 2009-12-30 05:59:21Z kientzle $"); /* * Detailed byte-for-byte verification of the format of a zip archive * with a single file written to it that uses Zip64 extensions. */ static unsigned long bitcrc32(unsigned long c, void *_p, size_t s) { /* This is a drop-in replacement for crc32() from zlib. * Libarchive should be able to correctly generate * uncompressed zip archives (including correct CRCs) even * when zlib is unavailable, and this function helps us verify * that. Yes, this is very, very slow and unsuitable for * production use, but it's correct, compact, and works well * enough for this particular usage. Libarchive internally * uses a much more efficient implementation. */ const unsigned char *p = _p; int bitctr; if (p == NULL) return (0); for (; s > 0; --s) { c ^= *p++; for (bitctr = 8; bitctr > 0; --bitctr) { if (c & 1) c = (c >> 1); else c = (c >> 1) ^ 0xedb88320; c ^= 0x80000000; } } return (c); } /* Quick and dirty: Read 2-byte and 4-byte integers from Zip file. */ static unsigned i2(const unsigned char *p) { return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); } static unsigned i4(const unsigned char *p) { return (i2(p) | (i2(p + 2) << 16)); } /* We're only working with small values here; ignore the 4 high bytes. */ static unsigned i8(const unsigned char *p) { return (i4(p)); } DEFINE_TEST(test_write_format_zip_file_zip64) { struct archive *a; struct archive_entry *ae; time_t t = 1234567890; struct tm *tm; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; -#endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; #endif size_t used, buffsize = 1000000; unsigned long crc; int file_perm = 00644; int zip_version = 45; int zip_compression = 8; short file_uid = 10, file_gid = 20; unsigned char *buff, *buffend, *p; unsigned char *central_header, *local_header, *eocd, *eocd_record; unsigned char *extension_start, *extension_end; char file_data[] = {'1', '2', '3', '4', '5', '6', '7', '8'}; const char *file_name = "file"; #ifndef HAVE_ZLIB_H zip_compression = 0; #endif -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif 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 buff = malloc(buffsize); /* Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:zip64")); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:experimental")); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used)); assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, file_name); archive_entry_set_mode(ae, AE_IFREG | file_perm); archive_entry_set_size(ae, sizeof(file_data)); archive_entry_set_uid(ae, file_uid); archive_entry_set_gid(ae, file_gid); archive_entry_set_mtime(ae, t, 0); assertEqualInt(0, archive_write_header(a, ae)); archive_entry_free(ae); assertEqualInt(8, archive_write_data(a, file_data, sizeof(file_data))); assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); buffend = buff + used; dumpfile("constructed.zip", buff, used); /* Verify "End of Central Directory" record. */ /* Get address of end-of-central-directory record. */ eocd_record = p = buffend - 22; /* Assumes there is no zip comment field. */ failure("End-of-central-directory begins with PK\\005\\006 signature"); assertEqualMem(p, "PK\005\006", 4); failure("This must be disk 0"); assertEqualInt(i2(p + 4), 0); failure("Central dir must start on disk 0"); assertEqualInt(i2(p + 6), 0); failure("All central dir entries are on this disk"); assertEqualInt(i2(p + 8), i2(p + 10)); eocd = buff + i4(p + 12) + i4(p + 16); failure("no zip comment"); assertEqualInt(i2(p + 20), 0); /* Get address of first entry in central directory. */ central_header = p = buff + i4(buffend - 6); failure("Central file record at offset %d should begin with" " PK\\001\\002 signature", i4(buffend - 10)); /* Verify file entry in central directory. */ assertEqualMem(p, "PK\001\002", 4); /* Signature */ assertEqualInt(i2(p + 4), 3 * 256 + zip_version); /* Version made by */ assertEqualInt(i2(p + 6), zip_version); /* Version needed to extract */ assertEqualInt(i2(p + 8), 8); /* Flags */ assertEqualInt(i2(p + 10), zip_compression); /* Compression method */ assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ crc = bitcrc32(0, file_data, sizeof(file_data)); assertEqualInt(i4(p + 16), crc); /* CRC-32 */ /* assertEqualInt(i4(p + 20), sizeof(file_data)); */ /* Compressed size */ assertEqualInt(i4(p + 24), sizeof(file_data)); /* Uncompressed size */ assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */ /* assertEqualInt(i2(p + 30), 28); */ /* Extra field length: See below */ assertEqualInt(i2(p + 32), 0); /* File comment length */ assertEqualInt(i2(p + 34), 0); /* Disk number start */ assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ assertEqualInt(i4(p + 38) >> 16 & 01777, file_perm); /* External file attrs */ assertEqualInt(i4(p + 42), 0); /* Offset of local header */ assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */ p = extension_start = central_header + 46 + strlen(file_name); extension_end = extension_start + i2(central_header + 30); assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(p + 2), 5); /* 'UT' size */ assertEqualInt(p[4], 1); /* 'UT' flags */ assertEqualInt(i4(p + 5), t); /* 'UT' mtime */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(p + 2), 11); /* 'ux' size */ /* TODO: verify 'ux' contents */ p += 4 + i2(p + 2); /* Note: We don't expect to see zip64 extension in the central * directory, since the writer knows the actual full size by * the time it is ready to write the central directory and has * no reason to insert it then. Info-Zip seems to do the same * thing. */ /* Just in case: Report any extra extensions. */ while (p < extension_end) { failure("Unexpected extension 0x%04X", i2(p)); assert(0); p += 4 + i2(p + 2); } /* Should have run exactly to end of extra data. */ assert(p == extension_end); assert(p == eocd); /* After Central dir, we find Zip64 eocd and Zip64 eocd locator. */ assertEqualMem(p, "PK\006\006", 4); /* Zip64 eocd */ assertEqualInt(i8(p + 4), 44); /* We're using v1 Zip64 eocd */ assertEqualInt(i2(p + 12), 45); /* Written by Version 4.5 */ assertEqualInt(i2(p + 14), 45); /* Needs version 4.5 to extract */ assertEqualInt(i4(p + 16), 0); /* This is disk #0 */ assertEqualInt(i4(p + 20), 0); /* Dir starts on disk #0 */ assertEqualInt(i8(p + 24), 1); /* 1 entry on this disk */ assertEqualInt(i8(p + 32), 1); /* 1 entry total */ assertEqualInt(i8(p + 40), eocd - central_header); /* size of cd */ assertEqualInt(i8(p + 48), central_header - buff); /* start of cd */ p += 12 + i8(p + 4); assertEqualMem(p, "PK\006\007", 4); /* Zip64 eocd locator */ assertEqualInt(i4(p + 4), 0); /* Zip64 eocd is on disk #0 */ assertEqualInt(i8(p + 8), eocd - buff); /* Offset of Zip64 eocd */ assertEqualInt(i4(p + 16), 1); /* 1 disk */ p += 20; /* Regular EOCD immediately follows Zip64 records. */ assert(p == eocd_record); /* Verify local header of file entry. */ p = local_header = buff; assertEqualMem(p, "PK\003\004", 4); /* Signature */ assertEqualInt(i2(p + 4), zip_version); /* Version needed to extract */ assertEqualInt(i2(p + 6), 8); /* Flags */ assertEqualInt(i2(p + 8), zip_compression); /* Compression method */ assertEqualInt(i2(p + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ assertEqualInt(i2(p + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ assertEqualInt(i4(p + 14), 0); /* CRC-32 */ /* assertEqualInt(i4(p + 18), sizeof(file_data)); */ /* Compressed size */ /* assertEqualInt(i4(p + 22), sizeof(file_data)); */ /* Uncompressed size not stored because we're using length-at-end. */ assertEqualInt(i2(p + 26), strlen(file_name)); /* Pathname length */ assertEqualInt(i2(p + 28), 57); /* Extra field length */ assertEqualMem(p + 30, file_name, strlen(file_name)); /* Pathname */ p = extension_start = local_header + 30 + strlen(file_name); extension_end = extension_start + i2(local_header + 28); assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ assertEqualInt(i2(p + 2), 5); /* 'UT' size */ assertEqualInt(p[4], 1); /* 'UT' flags */ assertEqualInt(i4(p + 5), t); /* 'UT' mtime */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */ assertEqualInt(i2(p + 2), 11); /* 'ux' size */ assertEqualInt(p[4], 1); /* 'ux' version */ assertEqualInt(p[5], 4); /* 'ux' uid size */ assertEqualInt(i4(p + 6), file_uid); /* 'Ux' UID */ assertEqualInt(p[10], 4); /* 'ux' gid size */ assertEqualInt(i4(p + 11), file_gid); /* 'Ux' GID */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x0001); /* Zip64 extension header */ assertEqualInt(i2(p + 2), 16); /* size */ assertEqualInt(i8(p + 4), 8); /* uncompressed file size */ /* compressed file size we can't verify here */ p += 4 + i2(p + 2); assertEqualInt(i2(p), 0x6c78); /* 'xl' experimental extension header */ assertEqualInt(i2(p + 2), 9); /* size */ assertEqualInt(p[4], 7); /* bitmap of included fields */ assertEqualInt(i2(p + 5) >> 8, 3); /* system & version made by */ assertEqualInt(i2(p + 7), 0); /* internal file attributes */ assertEqualInt(i4(p + 9) >> 16 & 01777, file_perm); /* external file attributes */ p += 4 + i2(p + 2); /* Just in case: Report any extra extensions. */ while (p < extension_end) { failure("Unexpected extension 0x%04X", i2(p)); assert(0); p += 4 + i2(p + 2); } /* Should have run exactly to end of extra data. */ assert(p == extension_end); /* Data descriptor should follow compressed data. */ while (p < central_header && memcmp(p, "PK\007\010", 4) != 0) ++p; assertEqualMem(p, "PK\007\010", 4); assertEqualInt(i4(p + 4), crc); /* CRC-32 */ /* assertEqualInt(i8(p + 8), ???); */ /* compressed size */ assertEqualInt(i8(p + 16), sizeof(file_data)); /* uncompressed size */ /* Central directory should immediately follow the only entry. */ assert(p + 24 == central_header); free(buff); } diff --git a/contrib/libarchive/libarchive/xxhash.c b/contrib/libarchive/libarchive/xxhash.c index f96e9d93493e..beacd2391221 100644 --- a/contrib/libarchive/libarchive/xxhash.c +++ b/contrib/libarchive/libarchive/xxhash.c @@ -1,525 +1,529 @@ /* xxHash - Fast Hash algorithm Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ #include "archive_platform.h" #include #include #include "archive_xxhash.h" #ifdef HAVE_LIBLZ4 /*************************************** ** Tuning parameters ****************************************/ /* Unaligned memory access is automatically enabled for "common" CPU, such as x86. ** For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. ** If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. ** You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). */ #if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) # define XXH_USE_UNALIGNED_ACCESS 1 #endif /* XXH_ACCEPT_NULL_INPUT_POINTER : ** If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. ** When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. ** This option has a very small performance cost (only measurable on small inputs). ** By default, this option is disabled. To enable it, uncomment below define : ** #define XXH_ACCEPT_NULL_INPUT_POINTER 1 ** XXH_FORCE_NATIVE_FORMAT : ** By default, xxHash library provides endian-independent Hash values, based on little-endian convention. ** Results are therefore identical for little-endian and big-endian CPU. ** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. ** Should endian-independence be of no importance for your application, you may set the #define below to 1. ** It will improve speed for Big-endian CPU. ** This option has no impact on Little_Endian CPU. */ #define XXH_FORCE_NATIVE_FORMAT 0 /*************************************** ** Compiler Specific Options ****************************************/ /* Disable some Visual warning messages */ #ifdef _MSC_VER /* Visual Studio */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE __forceinline #else # ifdef __GNUC__ # define FORCE_INLINE inline __attribute__((always_inline)) # else # define FORCE_INLINE inline # endif #endif /*************************************** ** Includes & Memory related functions ****************************************/ #define XXH_malloc malloc #define XXH_free free #define XXH_memcpy memcpy static unsigned int XXH32 (const void*, unsigned int, unsigned int); static void* XXH32_init (unsigned int); static XXH_errorcode XXH32_update (void*, const void*, unsigned int); static unsigned int XXH32_digest (void*); /*static int XXH32_sizeofState(void);*/ static XXH_errorcode XXH32_resetState(void*, unsigned int); #define XXH32_SIZEOFSTATE 48 typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t; static unsigned int XXH32_intermediateDigest (void*); /*************************************** ** Basic Types ****************************************/ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif #if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS) # define _PACKED __attribute__ ((packed)) #else # define _PACKED #endif #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) # ifdef __IBMC__ # pragma pack(1) # else # pragma pack(push, 1) # endif #endif typedef struct _U32_S { U32 v; } _PACKED U32_S; #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) # pragma pack(pop) #endif /**************************************** ** Compiler-specific Functions and Macros *****************************************/ #define GCC_VERSION ((__GNUC__-0) * 100 + (__GNUC_MINOR__ - 0)) #if GCC_VERSION >= 409 __attribute__((__no_sanitize_undefined__)) +#else +# if defined(__clang__) +__attribute__((no_sanitize("undefined"))) +# endif #endif #if defined(_MSC_VER) static __inline U32 A32(const void * x) #else static inline U32 A32(const void* x) #endif { return (((const U32_S *)(x))->v); } /* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ #if defined(_MSC_VER) # define XXH_rotl32(x,r) _rotl(x,r) #else # define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) #endif #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap32 _byteswap_ulong #elif GCC_VERSION >= 403 # define XXH_swap32 __builtin_bswap32 #else static inline U32 XXH_swap32 (U32 x) { return ((x << 24) & 0xff000000 ) | ((x << 8) & 0x00ff0000 ) | ((x >> 8) & 0x0000ff00 ) | ((x >> 24) & 0x000000ff );} #endif /*************************************** ** Constants ****************************************/ #define PRIME32_1 2654435761U #define PRIME32_2 2246822519U #define PRIME32_3 3266489917U #define PRIME32_4 668265263U #define PRIME32_5 374761393U /*************************************** ** Architecture Macros ****************************************/ typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; #ifndef XXH_CPU_LITTLE_ENDIAN /* It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch */ static const int one = 1; # define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&one)) #endif /*************************************** ** Macros ****************************************/ #define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } /* use only *after* variable declarations */ /***************************** ** Memory reads ******************************/ typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; static FORCE_INLINE U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); else return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr); } static FORCE_INLINE U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } /***************************** ** Simple Hash Functions ******************************/ static FORCE_INLINE U32 XXH32_endian_align(const void* input, unsigned int len, U32 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* bEnd = p + len; U32 h32; #define XXH_get32bits(p) XXH_readLE32_align((const U32*)p, endian, align) #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; } #endif if (len>=16) { const BYTE* const limit = bEnd - 16; U32 v1 = seed + PRIME32_1 + PRIME32_2; U32 v2 = seed + PRIME32_2; U32 v3 = seed + 0; U32 v4 = seed - PRIME32_1; do { v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; } while (p<=limit); h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); } else { h32 = seed + PRIME32_5; } h32 += (U32) len; while (p<=bEnd-4) { h32 += XXH_get32bits(p) * PRIME32_3; h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; p+=4; } while (p> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } U32 XXH32(const void* input, unsigned int len, U32 seed) { #if 0 // Simple version, good for code maintenance, but unfortunately slow for small inputs void* state = XXH32_init(seed); XXH32_update(state, input, len); return XXH32_digest(state); #else XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; # if !defined(XXH_USE_UNALIGNED_ACCESS) if ((((size_t)input) & 3) == 0) /* Input is aligned, let's leverage the speed advantage */ { if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); else return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); } # endif if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); else return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); #endif } /***************************** ** Advanced Hash Functions ******************************/ struct XXH_state32_t { U64 total_len; U32 seed; U32 v1; U32 v2; U32 v3; U32 v4; int memsize; char memory[16]; }; #if 0 static int XXH32_sizeofState(void) { XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t)); /* A compilation error here means XXH32_SIZEOFSTATE is not large enough */ return sizeof(struct XXH_state32_t); } #endif static XXH_errorcode XXH32_resetState(void* state_in, U32 seed) { struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; state->seed = seed; state->v1 = seed + PRIME32_1 + PRIME32_2; state->v2 = seed + PRIME32_2; state->v3 = seed + 0; state->v4 = seed - PRIME32_1; state->total_len = 0; state->memsize = 0; return XXH_OK; } static void* XXH32_init (U32 seed) { void* state = XXH_malloc (sizeof(struct XXH_state32_t)); XXH32_resetState(state, seed); return state; } static FORCE_INLINE XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian) { struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (input==NULL) return XXH_ERROR; #endif state->total_len += len; if (state->memsize + len < 16) /* fill in tmp buffer */ { XXH_memcpy(state->memory + state->memsize, input, len); state->memsize += len; return XXH_OK; } if (state->memsize) /* some data left from previous update */ { XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize); { const U32* p32 = (const U32*)state->memory; state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++; state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++; state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++; } p += 16-state->memsize; state->memsize = 0; } if (p <= bEnd-16) { const BYTE* const limit = bEnd - 16; U32 v1 = state->v1; U32 v2 = state->v2; U32 v3 = state->v3; U32 v4 = state->v4; do { v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; } while (p<=limit); state->v1 = v1; state->v2 = v2; state->v3 = v3; state->v4 = v4; } if (p < bEnd) { XXH_memcpy(state->memory, p, bEnd-p); state->memsize = (int)(bEnd-p); } return XXH_OK; } static XXH_errorcode XXH32_update (void* state_in, const void* input, unsigned int len) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_update_endian(state_in, input, len, XXH_littleEndian); else return XXH32_update_endian(state_in, input, len, XXH_bigEndian); } static FORCE_INLINE U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian) { struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; const BYTE * p = (const BYTE*)state->memory; BYTE* bEnd = (BYTE*)state->memory + state->memsize; U32 h32; if (state->total_len >= 16) { h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); } else { h32 = state->seed + PRIME32_5; } h32 += (U32) state->total_len; while (p<=bEnd-4) { h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3; h32 = XXH_rotl32(h32, 17) * PRIME32_4; p+=4; } while (p> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } static U32 XXH32_intermediateDigest (void* state_in) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian); else return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian); } static U32 XXH32_digest (void* state_in) { U32 h32 = XXH32_intermediateDigest(state_in); XXH_free(state_in); return h32; } const struct archive_xxhash __archive_xxhash = { XXH32, XXH32_init, XXH32_update, XXH32_digest }; #else /* * Define an empty version of the struct if we aren't using the LZ4 library. */ const struct archive_xxhash __archive_xxhash = { NULL, NULL, NULL, NULL }; #endif /* HAVE_LIBLZ4 */ diff --git a/contrib/libarchive/libarchive_fe/passphrase.c b/contrib/libarchive/libarchive_fe/passphrase.c index edf72d147182..1cae6a7bc80a 100644 --- a/contrib/libarchive/libarchive_fe/passphrase.c +++ b/contrib/libarchive/libarchive_fe/passphrase.c @@ -1,333 +1,339 @@ /*- * 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 * 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. */ /* $OpenBSD: readpassphrase.c,v 1.27 2019/01/25 00:19:25 millert Exp $ */ /* * Copyright (c) 2000-2002, 2007, 2010 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ #include "lafe_platform.h" __FBSDID("$FreeBSD$"); #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_READPASSPHRASE_H #include #endif #include "err.h" #include "passphrase.h" #ifndef HAVE_READPASSPHRASE #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ #define RPP_ECHO_ON 0x01 /* Leave echo on. */ #define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ #define RPP_FORCELOWER 0x04 /* Force input to lower case. */ #define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ #if defined(_WIN32) && !defined(__CYGWIN__) #include static char * readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) { HANDLE hStdin, hStdout; DWORD mode, rbytes; BOOL success; (void)flags; hStdin = GetStdHandle(STD_INPUT_HANDLE); if (hStdin == INVALID_HANDLE_VALUE) return (NULL); hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout == INVALID_HANDLE_VALUE) return (NULL); success = GetConsoleMode(hStdin, &mode); if (!success) return (NULL); mode &= ~ENABLE_ECHO_INPUT; mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; success = SetConsoleMode(hStdin, mode); if (!success) return (NULL); success = WriteFile(hStdout, prompt, (DWORD)strlen(prompt), NULL, NULL); if (!success) return (NULL); success = ReadFile(hStdin, buf, (DWORD)bufsiz - 1, &rbytes, NULL); if (!success) return (NULL); WriteFile(hStdout, "\r\n", 2, NULL, NULL); buf[rbytes] = '\0'; /* Remove trailing carriage return(s). */ if (rbytes > 2 && buf[rbytes - 2] == '\r' && buf[rbytes - 1] == '\n') buf[rbytes - 2] = '\0'; return (buf); } #else /* _WIN32 && !__CYGWIN__ */ #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #ifndef _PATH_TTY #define _PATH_TTY "/dev/tty" #endif #ifdef TCSASOFT # define _T_FLUSH (TCSAFLUSH|TCSASOFT) #else # define _T_FLUSH (TCSAFLUSH) #endif /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ #if !defined(_POSIX_VDISABLE) && defined(VDISABLE) # define _POSIX_VDISABLE VDISABLE #endif #define M(a,b) (a > b ? a : b) #define MAX_SIGNO M(M(M(SIGALRM, SIGHUP), \ M(SIGINT, SIGPIPE)), \ M(M(SIGQUIT, SIGTERM), \ M(M(SIGTSTP, SIGTTIN), SIGTTOU))) static volatile sig_atomic_t signo[MAX_SIGNO + 1]; static void handler(int s) { assert(s <= MAX_SIGNO); signo[s] = 1; } static char * readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) { ssize_t nr; int input, output, save_errno, i, need_restart; char ch, *p, *end; struct termios term, oterm; +#ifdef HAVE_SIGACTION struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; struct sigaction savetstp, savettin, savettou, savepipe; +#endif /* I suppose we could alloc on demand in this case (XXX). */ if (bufsiz == 0) { errno = EINVAL; return(NULL); } restart: for (i = 0; i <= MAX_SIGNO; i++) signo[i] = 0; nr = -1; save_errno = 0; need_restart = 0; /* * Read and write to /dev/tty if available. If not, read from * stdin and write to stderr unless a tty is required. */ if ((flags & RPP_STDIN) || (input = output = open(_PATH_TTY, O_RDWR)) == -1) { if (flags & RPP_REQUIRE_TTY) { errno = ENOTTY; return(NULL); } input = STDIN_FILENO; output = STDERR_FILENO; } /* * Turn off echo if possible. * If we are using a tty but are not the foreground pgrp this will * generate SIGTTOU, so do it *before* installing the signal handlers. */ if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { memcpy(&term, &oterm, sizeof(term)); if (!(flags & RPP_ECHO_ON)) term.c_lflag &= ~(ECHO | ECHONL); #ifdef VSTATUS if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) term.c_cc[VSTATUS] = _POSIX_VDISABLE; #endif (void)tcsetattr(input, _T_FLUSH, &term); } else { memset(&term, 0, sizeof(term)); term.c_lflag |= ECHO; memset(&oterm, 0, sizeof(oterm)); oterm.c_lflag |= ECHO; } +#ifdef HAVE_SIGACTION /* * Catch signals that would otherwise cause the user to end * up with echo turned off in the shell. Don't worry about * things like SIGXCPU and SIGVTALRM for now. */ sigemptyset(&sa.sa_mask); sa.sa_flags = 0; /* don't restart system calls */ sa.sa_handler = handler; /* Keep this list in sync with MAX_SIGNO! */ (void)sigaction(SIGALRM, &sa, &savealrm); (void)sigaction(SIGHUP, &sa, &savehup); (void)sigaction(SIGINT, &sa, &saveint); (void)sigaction(SIGPIPE, &sa, &savepipe); (void)sigaction(SIGQUIT, &sa, &savequit); (void)sigaction(SIGTERM, &sa, &saveterm); (void)sigaction(SIGTSTP, &sa, &savetstp); (void)sigaction(SIGTTIN, &sa, &savettin); (void)sigaction(SIGTTOU, &sa, &savettou); +#endif if (!(flags & RPP_STDIN)) { int r = write(output, prompt, strlen(prompt)); (void)r; } end = buf + bufsiz - 1; p = buf; while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { if (p < end) { if ((flags & RPP_SEVENBIT)) ch &= 0x7f; if (isalpha((unsigned char)ch)) { if ((flags & RPP_FORCELOWER)) ch = (char)tolower((unsigned char)ch); if ((flags & RPP_FORCEUPPER)) ch = (char)toupper((unsigned char)ch); } *p++ = ch; } } *p = '\0'; save_errno = errno; if (!(term.c_lflag & ECHO)) { int r = write(output, "\n", 1); (void)r; } /* Restore old terminal settings and signals. */ if (memcmp(&term, &oterm, sizeof(term)) != 0) { const int sigttou = signo[SIGTTOU]; /* Ignore SIGTTOU generated when we are not the fg pgrp. */ while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && errno == EINTR && !signo[SIGTTOU]) continue; signo[SIGTTOU] = sigttou; } +#ifdef HAVE_SIGACTION (void)sigaction(SIGALRM, &savealrm, NULL); (void)sigaction(SIGHUP, &savehup, NULL); (void)sigaction(SIGINT, &saveint, NULL); (void)sigaction(SIGQUIT, &savequit, NULL); (void)sigaction(SIGPIPE, &savepipe, NULL); (void)sigaction(SIGTERM, &saveterm, NULL); (void)sigaction(SIGTSTP, &savetstp, NULL); (void)sigaction(SIGTTIN, &savettin, NULL); (void)sigaction(SIGTTOU, &savettou, NULL); +#endif if (input != STDIN_FILENO) (void)close(input); /* * If we were interrupted by a signal, resend it to ourselves * now that we have restored the signal handlers. */ for (i = 0; i <= MAX_SIGNO; i++) { if (signo[i]) { kill(getpid(), i); switch (i) { case SIGTSTP: case SIGTTIN: case SIGTTOU: need_restart = 1; } } } if (need_restart) goto restart; if (save_errno) errno = save_errno; return(nr == -1 ? NULL : buf); } #endif /* _WIN32 && !__CYGWIN__ */ #endif /* HAVE_READPASSPHRASE */ char * lafe_readpassphrase(const char *prompt, char *buf, size_t bufsiz) { char *p; p = readpassphrase(prompt, buf, bufsiz, RPP_ECHO_OFF); if (p == NULL) { switch (errno) { case EINTR: break; default: lafe_errc(1, errno, "Couldn't read passphrase"); break; } } return (p); } diff --git a/contrib/libarchive/tar/bsdtar.1 b/contrib/libarchive/tar/bsdtar.1 index b57835adbef2..1b78fbc04943 100644 --- a/contrib/libarchive/tar/bsdtar.1 +++ b/contrib/libarchive/tar/bsdtar.1 @@ -1,1342 +1,1355 @@ .\" Copyright (c) 2003-2007 Tim Kientzle .\" Copyright (c) 2017 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 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 January 31, 2020 +.Dd December 1, 2022 .Dt TAR 1 .Os .Sh NAME .Nm tar .Nd manipulate tape archives .Sh SYNOPSIS .Nm .Op Ar bundled-flags Ao args Ac .Op Ao Ar file Ac | Ao Ar pattern Ac ... .Nm .Brq Fl c .Op Ar options .Op Ar files | Ar directories .Nm .Brq Fl r | Fl u .Fl f Ar archive-file .Op Ar options .Op Ar files | Ar directories .Nm .Brq Fl t | Fl x .Op Ar options .Op Ar patterns .Sh DESCRIPTION .Nm creates and manipulates streaming archive files. This implementation can extract from tar, pax, cpio, zip, jar, ar, xar, rpm, 7-zip, and ISO 9660 cdrom images and can create tar, pax, cpio, ar, zip, 7-zip, and shar archives. .Pp The first synopsis form shows a .Dq bundled option word. This usage is provided for compatibility with historical implementations. See COMPATIBILITY below for details. .Pp The other synopsis forms show the preferred usage. The first option to .Nm is a mode indicator from the following list: .Bl -tag -compact -width indent .It Fl c Create a new archive containing the specified items. The long option form is .Fl Fl create . .It Fl r Like .Fl c , but new entries are appended to the archive. Note that this only works on uncompressed archives stored in regular files. The .Fl f option is required. The long option form is .Fl Fl append . .It Fl t List archive contents to stdout. The long option form is .Fl Fl list . .It Fl u Like .Fl r , but new entries are added only if they have a modification date newer than the corresponding entry in the archive. Note that this only works on uncompressed archives stored in regular files. The .Fl f option is required. The long form is .Fl Fl update . .It Fl x Extract to disk from the archive. If a file with the same name appears more than once in the archive, each copy will be extracted, with later copies overwriting (replacing) earlier copies. The long option form is .Fl Fl extract . .El .Pp In .Fl c , .Fl r , or .Fl u mode, each specified file or directory is added to the archive in the order specified on the command line. By default, the contents of each directory are also archived. .Pp In extract or list mode, the entire command line is read and parsed before the archive is opened. The pathnames or patterns on the command line indicate which items in the archive should be processed. Patterns are shell-style globbing patterns as documented in .Xr tcsh 1 . .Sh OPTIONS Unless specifically stated otherwise, options are applicable in all operating modes. .Bl -tag -width indent .It Cm @ Ns Pa archive (c and r modes only) The specified archive is opened and the entries in it will be appended to the current archive. As a simple example, .Dl Nm Fl c Fl f Pa - Pa newfile Cm @ Ns Pa original.tar writes a new archive to standard output containing a file .Pa newfile and all of the entries from .Pa original.tar . In contrast, .Dl Nm Fl c Fl f Pa - Pa newfile Pa original.tar creates a new archive with only two entries. Similarly, .Dl Nm Fl czf Pa - Fl Fl format Cm pax Cm @ Ns Pa - reads an archive from standard input (whose format will be determined automatically) and converts it into a gzip-compressed pax-format archive on stdout. In this way, .Nm can be used to convert archives from one format to another. .It Fl a , Fl Fl auto-compress (c mode only) Use the archive suffix to decide a set of the format and the compressions. As a simple example, .Dl Nm Fl a Fl cf Pa archive.tgz source.c source.h creates a new archive with restricted pax format and gzip compression, .Dl Nm Fl a Fl cf Pa archive.tar.bz2.uu source.c source.h creates a new archive with restricted pax format and bzip2 compression and uuencode compression, .Dl Nm Fl a Fl cf Pa archive.zip source.c source.h creates a new archive with zip format, .Dl Nm Fl a Fl jcf Pa archive.tgz source.c source.h ignores the .Dq -j option, and creates a new archive with restricted pax format and gzip compression, .Dl Nm Fl a Fl jcf Pa archive.xxx source.c source.h if it is unknown suffix or no suffix, creates a new archive with restricted pax format and bzip2 compression. .It Fl Fl acls (c, r, u, x modes only) Archive or extract POSIX.1e or NFSv4 ACLs. This is the reverse of .Fl Fl no-acls and the default behavior in c, r, and u modes (except on Mac OS X) or if .Nm is run in x mode as root. On Mac OS X this option translates extended ACLs to NFSv4 ACLs. To store extended ACLs the .Fl Fl mac-metadata option is preferred. .It Fl B , Fl Fl read-full-blocks Ignored for compatibility with other .Xr tar 1 implementations. .It Fl b Ar blocksize , Fl Fl block-size Ar blocksize Specify the block size, in 512-byte records, for tape drive I/O. As a rule, this argument is only needed when reading from or writing to tape drives, and usually not even then as the default block size of 20 records (10240 bytes) is very common. .It Fl C Ar directory , Fl Fl cd Ar directory , Fl Fl directory Ar directory In c and r mode, this changes the directory before adding the following files. In x mode, change directories after opening the archive but before extracting entries from the archive. .It Fl Fl chroot (x mode only) .Fn chroot to the current directory after processing any .Fl C options and before extracting any files. .It Fl Fl clear-nochange-fflags (x mode only) Before removing file system objects to replace them, clear platform-specific file attributes or file flags that might prevent removal. .It Fl Fl exclude Ar pattern Do not process files or directories that match the specified pattern. Note that exclusions take precedence over patterns or filenames specified on the command line. .It Fl Fl exclude-vcs Do not process files or directories internally used by the version control systems .Sq Arch , .Sq Bazaar , .Sq CVS , .Sq Darcs , .Sq Mercurial , .Sq RCS , .Sq SCCS , .Sq SVN and .Sq git . .It Fl Fl fflags (c, r, u, x modes only) Archive or extract platform-specific file attributes or file flags. This is the reverse of .Fl Fl no-fflags and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. .It Fl Fl format Ar format (c, r, u mode only) Use the specified format for the created archive. Supported formats include .Dq cpio , .Dq pax , .Dq shar , and .Dq ustar . Other formats may also be supported; see .Xr libarchive-formats 5 for more information about currently-supported formats. In r and u modes, when extending an existing archive, the format specified here must be compatible with the format of the existing archive on disk. .It Fl f Ar file , Fl Fl file Ar file Read the archive from or write the archive to the specified file. The filename can be .Pa - for standard input or standard output. The default varies by system; on .Fx , the default is .Pa /dev/sa0 ; on Linux, the default is .Pa /dev/st0 . .It Fl Fl gid Ar id Use the provided group id number. On extract, this overrides the group id in the archive; the group name in the archive will be ignored. On create, this overrides the group id read from disk; if .Fl Fl gname is not also specified, the group name will be set to match the group id. .It Fl Fl gname Ar name Use the provided group name. On extract, this overrides the group name in the archive; if the provided group name does not exist on the system, the group id (from the archive or from the .Fl Fl gid option) will be used instead. On create, this sets the group name that will be stored in the archive; the name will not be verified against the system group database. .It Fl H (c and r modes only) Symbolic links named on the command line will be followed; the target of the link will be archived, not the link itself. .It Fl h (c and r modes only) Synonym for .Fl L . .It Fl I Synonym for .Fl T . .It Fl Fl help Show usage. .It Fl Fl hfsCompression (x mode only) Mac OS X specific (v10.6 or later). Compress extracted regular files with HFS+ compression. .It Fl Fl ignore-zeros An alias of .Fl Fl options Cm read_concatenated_archives for compatibility with GNU tar. .It Fl Fl include Ar pattern Process only files or directories that match the specified pattern. Note that exclusions specified with .Fl Fl exclude take precedence over inclusions. If no inclusions are explicitly specified, all entries are processed by default. The .Fl Fl include option is especially useful when filtering archives. For example, the command .Dl Nm Fl c Fl f Pa new.tar Fl Fl include='*foo*' Cm @ Ns Pa old.tgz creates a new archive .Pa new.tar containing only the entries from .Pa old.tgz containing the string .Sq foo . .It Fl J , Fl Fl xz (c mode only) Compress the resulting archive with .Xr xz 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes XZ compression automatically when reading archives. .It Fl j , Fl Fl bzip , Fl Fl bzip2 , Fl Fl bunzip2 (c mode only) Compress the resulting archive with .Xr bzip2 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes bzip2 compression automatically when reading archives. .It Fl k , Fl Fl keep-old-files (x mode only) Do not overwrite existing files. In particular, if a file appears more than once in an archive, later copies will not overwrite earlier copies. .It Fl Fl keep-newer-files (x mode only) Do not overwrite existing files that are newer than the versions appearing in the archive being extracted. .It Fl L , Fl Fl dereference (c and r modes only) All symbolic links will be followed. Normally, symbolic links are archived as such. With this option, the target of the link will be archived instead. .It Fl l , Fl Fl check-links (c and r modes only) Issue a warning message unless all links to each file are archived. .It Fl Fl lrzip (c mode only) Compress the resulting archive with .Xr lrzip 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes lrzip compression automatically when reading archives. .It Fl Fl lz4 (c mode only) Compress the archive with lz4-compatible compression before writing it. In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes lz4 compression automatically when reading archives. .It Fl Fl zstd (c mode only) Compress the archive with zstd-compatible compression before writing it. In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes zstd compression automatically when reading archives. .It Fl Fl lzma (c mode only) Compress the resulting archive with the original LZMA algorithm. In extract or list modes, this option is ignored. Use of this option is discouraged and new archives should be created with .Fl Fl xz instead. Note that this .Nm tar implementation recognizes LZMA compression automatically when reading archives. .It Fl Fl lzop (c mode only) Compress the resulting archive with .Xr lzop 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes LZO compression automatically when reading archives. .It Fl m , Fl Fl modification-time (x mode only) Do not extract modification time. By default, the modification time is set to the time stored in the archive. .It Fl Fl mac-metadata (c, r, u and x mode only) Mac OS X specific. Archive or extract extended ACLs and extended file attributes using .Xr copyfile 3 in AppleDouble format. This is the reverse of .Fl Fl no-mac-metadata . and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. .It Fl n , Fl Fl norecurse , Fl Fl no-recursion Do not operate recursively on the content of directories. .It Fl Fl newer Ar date (c, r, u modes only) Only include files and directories newer than the specified date. This compares ctime entries. .It Fl Fl newer-mtime Ar date (c, r, u modes only) Like .Fl Fl newer , except it compares mtime entries instead of ctime entries. .It Fl Fl newer-than Pa file (c, r, u modes only) Only include files and directories newer than the specified file. This compares ctime entries. .It Fl Fl newer-mtime-than Pa file (c, r, u modes only) Like .Fl Fl newer-than , except it compares mtime entries instead of ctime entries. .It Fl Fl nodump (c and r modes only) Honor the nodump file flag by skipping this file. .It Fl Fl nopreserveHFSCompression (x mode only) Mac OS X specific (v10.6 or later). Do not compress extracted regular files which were compressed with HFS+ compression before archived. By default, compress the regular files again with HFS+ compression. .It Fl Fl null (use with .Fl I or .Fl T ) Filenames or patterns are separated by null characters, not by newlines. This is often used to read filenames output by the .Fl print0 option to .Xr find 1 . .It Fl Fl no-acls (c, r, u, x modes only) Do not archive or extract POSIX.1e or NFSv4 ACLs. This is the reverse of .Fl Fl acls and the default behavior if .Nm is run as non-root in x mode (on Mac OS X as any user in c, r, u and x modes). .It Fl Fl no-fflags (c, r, u, x modes only) Do not archive or extract file attributes or file flags. This is the reverse of .Fl Fl fflags and the default behavior if .Nm is run as non-root in x mode. .It Fl Fl no-mac-metadata (x mode only) Mac OS X specific. Do not archive or extract ACLs and extended file attributes using .Xr copyfile 3 in AppleDouble format. This is the reverse of .Fl Fl mac-metadata . and the default behavior if .Nm is run as non-root in x mode. .It Fl Fl no-read-sparse (c, r, u modes only) Do not read sparse file information from disk. This is the reverse of .Fl Fl read-sparse . .It Fl Fl no-safe-writes (x mode only) Do not create temporary files and use .Xr rename 2 to replace the original ones. This is the reverse of .Fl Fl safe-writes . .It Fl Fl no-same-owner (x mode only) Do not extract owner and group IDs. This is the reverse of .Fl Fl same-owner and the default behavior if .Nm is run as non-root. .It Fl Fl no-same-permissions (x mode only) Do not extract full permissions (SGID, SUID, sticky bit, file attributes or file flags, extended file attributes and ACLs). This is the reverse of .Fl p and the default behavior if .Nm is run as non-root. .It Fl Fl no-xattrs (c, r, u, x modes only) Do not archive or extract extended file attributes. This is the reverse of .Fl Fl xattrs and the default behavior if .Nm is run as non-root in x mode. .It Fl Fl numeric-owner This is equivalent to .Fl Fl uname .Qq .Fl Fl gname .Qq . On extract, it causes user and group names in the archive to be ignored in favor of the numeric user and group ids. On create, it causes user and group names to not be stored in the archive. .It Fl O , Fl Fl to-stdout (x, t modes only) In extract (-x) mode, files will be written to standard out rather than being extracted to disk. In list (-t) mode, the file listing will be written to stderr rather than the usual stdout. .It Fl o (x mode) Use the user and group of the user running the program rather than those specified in the archive. Note that this has no significance unless .Fl p is specified, and the program is being run by the root user. In this case, the file modes and flags from the archive will be restored, but ACLs or owner information in the archive will be discarded. .It Fl o (c, r, u mode) A synonym for .Fl Fl format Ar ustar .It Fl Fl older Ar date (c, r, u modes only) Only include files and directories older than the specified date. This compares ctime entries. .It Fl Fl older-mtime Ar date (c, r, u modes only) Like .Fl Fl older , except it compares mtime entries instead of ctime entries. .It Fl Fl older-than Pa file (c, r, u modes only) Only include files and directories older than the specified file. This compares ctime entries. .It Fl Fl older-mtime-than Pa file (c, r, u modes only) Like .Fl Fl older-than , except it compares mtime entries instead of ctime entries. .It Fl Fl one-file-system (c, r, and u modes) Do not cross mount points. .It Fl Fl options Ar options Select optional behaviors for particular modules. The argument is a text string containing comma-separated keywords and values. These are passed to the modules that handle particular formats to control how those formats will behave. Each option has one of the following forms: .Bl -tag -compact -width indent .It Ar key=value The key will be set to the specified value in every module that supports it. Modules that do not support this key will ignore it. .It Ar key The key will be enabled in every module that supports it. This is equivalent to .Ar key Ns Cm =1 . .It Ar !key The key will be disabled in every module that supports it. .It Ar module:key=value , Ar module:key , Ar module:!key As above, but the corresponding key and value will be provided only to modules whose name matches .Ar module . .El .Pp The complete list of supported modules and keys for create and append modes is in .Xr archive_write_set_options 3 and for extract and list modes in .Xr archive_read_set_options 3 . .Pp Examples of supported options: .Bl -tag -compact -width indent .It Cm iso9660:joliet Support Joliet extensions. This is enabled by default, use .Cm !joliet or .Cm iso9660:!joliet to disable. .It Cm iso9660:rockridge Support Rock Ridge extensions. This is enabled by default, use .Cm !rockridge or .Cm iso9660:!rockridge to disable. .It Cm gzip:compression-level A decimal integer from 1 to 9 specifying the gzip compression level. .It Cm gzip:timestamp Store timestamp. This is enabled by default, use .Cm !timestamp or .Cm gzip:!timestamp to disable. .It Cm lrzip:compression Ns = Ns Ar type Use .Ar type as compression method. Supported values are bzip2, gzip, lzo (ultra fast), and zpaq (best, extremely slow). .It Cm lrzip:compression-level A decimal integer from 1 to 9 specifying the lrzip compression level. .It Cm lz4:compression-level A decimal integer from 1 to 9 specifying the lzop compression level. .It Cm lz4:stream-checksum Enable stream checksum. This is by default, use .Cm lz4:!stream-checksum to disable. .It Cm lz4:block-checksum Enable block checksum (Disabled by default). .It Cm lz4:block-size A decimal integer from 4 to 7 specifying the lz4 compression block size (7 is set by default). .It Cm lz4:block-dependence Use the previous block of the block being compressed for a compression dictionary to improve compression ratio. .It Cm zstd:compression-level A decimal integer specifying the zstd compression level. Supported values depend on the library version, common values are from 1 to 22. .It Cm zstd:threads Specify the number of worker threads to use. Setting threads to a special value 0 makes .Xr zstd 1 use as many threads as there are CPU cores on the system. +.It Cm zstd:frame-per-file +Start a new compression frame at the beginning of each file in the +archive. +.It Cm zstd:min-frame-size Ns = Ns Ar N +In combination with +.Cm zstd:frame-per-file , +do not start a new compression frame unless the current frame is at least +.Ar N +bytes. +.It Cm zstd:max-frame-size Ns = Ns Ar N +Start a new compression frame as soon as the current frame exceeds +.Ar N +bytes. .It Cm lzop:compression-level A decimal integer from 1 to 9 specifying the lzop compression level. .It Cm xz:compression-level A decimal integer from 0 to 9 specifying the xz compression level. .It Cm xz:threads Specify the number of worker threads to use. Setting threads to a special value 0 makes .Xr xz 1 use as many threads as there are CPU cores on the system. .It Cm mtree: Ns Ar keyword The mtree writer module allows you to specify which mtree keywords will be included in the output. Supported keywords include: .Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , .Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , .Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname . The default is equivalent to: .Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . .It Cm mtree:all Enables all of the above keywords. You can also use .Cm mtree:!all to disable all keywords. .It Cm mtree:use-set Enable generation of .Cm /set lines in the output. .It Cm mtree:indent Produce human-readable output by indenting options and splitting lines to fit into 80 columns. .It Cm zip:compression Ns = Ns Ar type Use .Ar type as compression method. Supported values are store (uncompressed) and deflate (gzip algorithm). .It Cm zip:encryption Enable encryption using traditional zip encryption. .It Cm zip:encryption Ns = Ns Ar type Use .Ar type as encryption type. Supported values are zipcrypt (traditional zip encryption), aes128 (WinZip AES-128 encryption) and aes256 (WinZip AES-256 encryption). .It Cm read_concatenated_archives Ignore zeroed blocks in the archive, which occurs when multiple tar archives have been concatenated together. Without this option, only the contents of the first concatenated archive would be read. This option is comparable to the .Fl i , Fl Fl ignore-zeros option of GNU tar. .El If a provided option is not supported by any module, that is a fatal error. .It Fl P , Fl Fl absolute-paths Preserve pathnames. By default, absolute pathnames (those that begin with a / character) have the leading slash removed both when creating archives and extracting from them. Also, .Nm will refuse to extract archive entries whose pathnames contain .Pa .. or whose target directory would be altered by a symlink. This option suppresses these behaviors. .It Fl p , Fl Fl insecure , Fl Fl preserve-permissions (x mode only) Preserve file permissions. Attempt to restore the full permissions, including file modes, file attributes or file flags, extended file attributes and ACLs, if available, for each item extracted from the archive. This is the reverse of .Fl Fl no-same-permissions and the default if .Nm is being run as root. It can be partially overridden by also specifying .Fl Fl no-acls , .Fl Fl no-fflags , .Fl Fl no-mac-metadata or .Fl Fl no-xattrs . .It Fl Fl passphrase Ar passphrase The .Pa passphrase is used to extract or create an encrypted archive. Currently, zip is the only supported format that supports encryption. You shouldn't use this option unless you realize how insecure use of this option is. .It Fl Fl posix (c, r, u mode only) Synonym for .Fl Fl format Ar pax .It Fl q , Fl Fl fast-read (x and t mode only) Extract or list only the first archive entry that matches each pattern or filename operand. Exit as soon as each specified pattern or filename has been matched. By default, the archive is always read to the very end, since there can be multiple entries with the same name and, by convention, later entries overwrite earlier entries. This option is provided as a performance optimization. .It Fl Fl read-sparse (c, r, u modes only) Read sparse file information from disk. This is the reverse of .Fl Fl no-read-sparse and the default behavior. .It Fl S (x mode only) Extract files as sparse files. For every block on disk, check first if it contains only NULL bytes and seek over it otherwise. This works similar to the conv=sparse option of dd. .It Fl s Ar pattern Modify file or archive member names according to .Pa pattern . The pattern has the format .Ar /old/new/ Ns Op ghHprRsS where .Ar old is a basic regular expression, .Ar new is the replacement string of the matched part, and the optional trailing letters modify how the replacement is handled. If .Ar old is not matched, the pattern is skipped. Within .Ar new , ~ is substituted with the match, \e1 to \e9 with the content of the corresponding captured group. The optional trailing g specifies that matching should continue after the matched part and stop on the first unmatched pattern. The optional trailing s specifies that the pattern applies to the value of symbolic links. The optional trailing p specifies that after a successful substitution the original path name and the new path name should be printed to standard error. Optional trailing H, R, or S characters suppress substitutions for hardlink targets, regular filenames, or symlink targets, respectively. Optional trailing h, r, or s characters enable substitutions for hardlink targets, regular filenames, or symlink targets, respectively. The default is .Ar hrs which applies substitutions to all names. In particular, it is never necessary to specify h, r, or s. .It Fl Fl safe-writes (x mode only) Extract files atomically. By default .Nm unlinks the original file with the same name as the extracted file (if it exists), and then creates it immediately under the same name and writes to it. For a short period of time, applications trying to access the file might not find it, or see incomplete results. If .Fl Fl safe-writes is enabled, .Nm first creates a unique temporary file, then writes the new contents to the temporary file, and finally renames the temporary file to its final name atomically using .Xr rename 2 . This guarantees that an application accessing the file, will either see the old contents or the new contents at all times. .It Fl Fl same-owner (x mode only) Extract owner and group IDs. This is the reverse of .Fl Fl no-same-owner and the default behavior if .Nm is run as root. .It Fl Fl strip-components Ar count Remove the specified number of leading path elements. Pathnames with fewer elements will be silently skipped. Note that the pathname is edited after checking inclusion/exclusion patterns but before security checks. .It Fl T Ar filename , Fl Fl files-from Ar filename In x or t mode, .Nm will read the list of names to be extracted from .Pa filename . In c mode, .Nm will read names to be archived from .Pa filename . The special name .Dq -C on a line by itself will cause the current directory to be changed to the directory specified on the following line. Names are terminated by newlines unless .Fl Fl null is specified. Note that .Fl Fl null also disables the special handling of lines containing .Dq -C . Note: If you are generating lists of files using .Xr find 1 , you probably want to use .Fl n as well. .It Fl Fl totals (c, r, u modes only) After archiving all files, print a summary to stderr. .It Fl U , Fl Fl unlink , Fl Fl unlink-first (x mode only) Unlink files before creating them. This can be a minor performance optimization if most files already exist, but can make things slower if most files do not already exist. This flag also causes .Nm to remove intervening directory symlinks instead of reporting an error. See the SECURITY section below for more details. .It Fl Fl uid Ar id Use the provided user id number and ignore the user name from the archive. On create, if .Fl Fl uname is not also specified, the user name will be set to match the user id. .It Fl Fl uname Ar name Use the provided user name. On extract, this overrides the user name in the archive; if the provided user name does not exist on the system, it will be ignored and the user id (from the archive or from the .Fl Fl uid option) will be used instead. On create, this sets the user name that will be stored in the archive; the name is not verified against the system user database. .It Fl Fl use-compress-program Ar program Pipe the input (in x or t mode) or the output (in c mode) through .Pa program instead of using the builtin compression support. .It Fl v , Fl Fl verbose Produce verbose output. In create and extract modes, .Nm will list each file name as it is read from or written to the archive. In list mode, .Nm will produce output similar to that of .Xr ls 1 . An additional .Fl v option will also provide ls-like details in create and extract mode. .It Fl Fl version Print version of .Nm and .Nm libarchive , and exit. .It Fl w , Fl Fl confirmation , Fl Fl interactive Ask for confirmation for every action. .It Fl X Ar filename , Fl Fl exclude-from Ar filename Read a list of exclusion patterns from the specified file. See .Fl Fl exclude for more information about the handling of exclusions. .It Fl Fl xattrs (c, r, u, x modes only) Archive or extract extended file attributes. This is the reverse of .Fl Fl no-xattrs and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. .It Fl y (c mode only) Compress the resulting archive with .Xr bzip2 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes bzip2 compression automatically when reading archives. .It Fl Z , Fl Fl compress , Fl Fl uncompress (c mode only) Compress the resulting archive with .Xr compress 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes compress compression automatically when reading archives. .It Fl z , Fl Fl gunzip , Fl Fl gzip (c mode only) Compress the resulting archive with .Xr gzip 1 . In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes gzip compression automatically when reading archives. .El .Sh ENVIRONMENT The following environment variables affect the execution of .Nm : .Bl -tag -width indent .It Ev TAR_READER_OPTIONS The default options for format readers and compression readers. The .Fl Fl options option overrides this. .It Ev TAR_WRITER_OPTIONS The default options for format writers and compression writers. The .Fl Fl options option overrides this. .It Ev LANG The locale to use. See .Xr environ 7 for more information. .It Ev TAPE The default device. The .Fl f option overrides this. Please see the description of the .Fl f option above for more details. .It Ev TZ The timezone to use when displaying dates. See .Xr environ 7 for more information. .El .Sh EXIT STATUS .Ex -std .Sh EXAMPLES The following creates a new archive called .Ar file.tar.gz that contains two files .Ar source.c and .Ar source.h : .Dl Nm Fl czf Pa file.tar.gz Pa source.c Pa source.h .Pp To view a detailed table of contents for this archive: .Dl Nm Fl tvf Pa file.tar.gz .Pp To extract all entries from the archive on the default tape drive: .Dl Nm Fl x .Pp To examine the contents of an ISO 9660 cdrom image: .Dl Nm Fl tf Pa image.iso .Pp To move file hierarchies, invoke .Nm as .Dl Nm Fl cf Pa - Fl C Pa srcdir \&. | Nm Fl xpf Pa - Fl C Pa destdir or more traditionally .Dl cd srcdir \&; Nm Fl cf Pa - \&. | ( cd destdir \&; Nm Fl xpf Pa - ) .Pp In create mode, the list of files and directories to be archived can also include directory change instructions of the form .Cm -C Ns Pa foo/baz and archive inclusions of the form .Cm @ Ns Pa archive-file . For example, the command line .Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm -C Ns Pa /tmp Pa foo2 will create a new archive .Pa new.tar . .Nm will read the file .Pa foo1 from the current directory and add it to the output archive. It will then read each entry from .Pa old.tgz and add those entries to the output archive. Finally, it will switch to the .Pa /tmp directory and add .Pa foo2 to the output archive. .Pp An input file in .Xr mtree 5 format can be used to create an output archive with arbitrary ownership, permissions, or names that differ from existing data on disk: .Bd -literal -offset indent $ cat input.mtree #mtree usr/bin uid=0 gid=0 mode=0755 type=dir usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls $ tar -cvf output.tar @input.mtree .Ed .Pp The .Fl Fl newer and .Fl Fl newer-mtime switches accept a variety of common date and time specifications, including .Dq 12 Mar 2005 7:14:29pm , .Dq 2005-03-12 19:14 , .Dq 5 minutes ago , and .Dq 19:14 PST May 1 . .Pp The .Fl Fl options argument can be used to control various details of archive generation or reading. For example, you can generate mtree output which only contains .Cm type , Cm time , and .Cm uid keywords: .Dl Nm Fl cf Pa file.tar Fl Fl format=mtree Fl Fl options='!all,type,time,uid' Pa dir or you can set the compression level used by gzip or xz compression: .Dl Nm Fl czf Pa file.tar Fl Fl options='compression-level=9' . For more details, see the explanation of the .Fn archive_read_set_options and .Fn archive_write_set_options API calls that are described in .Xr archive_read 3 and .Xr archive_write 3 . .Sh COMPATIBILITY The bundled-arguments format is supported for compatibility with historic implementations. It consists of an initial word (with no leading - character) in which each character indicates an option. Arguments follow as separate words. The order of the arguments must match the order of the corresponding characters in the bundled command word. For example, .Dl Nm Cm tbf 32 Pa file.tar specifies three flags .Cm t , .Cm b , and .Cm f . The .Cm b and .Cm f flags both require arguments, so there must be two additional items on the command line. The .Ar 32 is the argument to the .Cm b flag, and .Ar file.tar is the argument to the .Cm f flag. .Pp The mode options c, r, t, u, and x and the options b, f, l, m, o, v, and w comply with SUSv2. .Pp For maximum portability, scripts that invoke .Nm tar should use the bundled-argument format above, should limit themselves to the .Cm c , .Cm t , and .Cm x modes, and the .Cm b , .Cm f , .Cm m , .Cm v , and .Cm w options. .Pp Additional long options are provided to improve compatibility with other tar implementations. .Sh SECURITY Certain security issues are common to many archiving programs, including .Nm . In particular, carefully-crafted archives can request that .Nm extract files to locations outside of the target directory. This can potentially be used to cause unwitting users to overwrite files they did not intend to overwrite. If the archive is being extracted by the superuser, any file on the system can potentially be overwritten. There are three ways this can happen. Although .Nm has mechanisms to protect against each one, savvy users should be aware of the implications: .Bl -bullet -width indent .It Archive entries can have absolute pathnames. By default, .Nm removes the leading .Pa / character from filenames before restoring them to guard against this problem. .It Archive entries can have pathnames that include .Pa .. components. By default, .Nm will not extract files containing .Pa .. components in their pathname. .It Archive entries can exploit symbolic links to restore files to other directories. An archive can restore a symbolic link to another directory, then use that link to restore a file into that directory. To guard against this, .Nm checks each extracted path for symlinks. If the final path element is a symlink, it will be removed and replaced with the archive entry. If .Fl U is specified, any intermediate symlink will also be unconditionally removed. If neither .Fl U nor .Fl P is specified, .Nm will refuse to extract the entry. .El To protect yourself, you should be wary of any archives that come from untrusted sources. You should examine the contents of an archive with .Dl Nm Fl tf Pa filename before extraction. You should use the .Fl k option to ensure that .Nm will not overwrite any existing files or the .Fl U option to remove any pre-existing files. You should generally not extract archives while running with super-user privileges. Note that the .Fl P option to .Nm disables the security checks above and allows you to extract an archive while preserving any absolute pathnames, .Pa .. components, or symlinks to other directories. .Sh SEE ALSO .Xr bzip2 1 , .Xr compress 1 , .Xr cpio 1 , .Xr gzip 1 , .Xr mt 1 , .Xr pax 1 , .Xr shar 1 , .Xr xz 1 , .Xr libarchive 3 , .Xr libarchive-formats 5 , .Xr tar 5 .Sh STANDARDS There is no current POSIX standard for the tar command; it appeared in .St -p1003.1-96 but was dropped from .St -p1003.1-2001 . The options supported by this implementation were developed by surveying a number of existing tar implementations as well as the old POSIX specification for tar and the current POSIX specification for pax. .Pp The ustar and pax interchange file formats are defined by .St -p1003.1-2001 for the pax command. .Sh HISTORY A .Nm tar command appeared in Seventh Edition Unix, which was released in January, 1979. There have been numerous other implementations, many of which extended the file format. John Gilmore's .Nm pdtar public-domain implementation (circa November, 1987) was quite influential, and formed the basis of GNU tar. GNU tar was included as the standard system tar in .Fx beginning with .Fx 1.0 . .Pp This is a complete re-implementation based on the .Xr libarchive 3 library. It was first released with .Fx 5.4 in May, 2005. .Sh BUGS This program follows .St -p1003.1-96 for the definition of the .Fl l option. Note that GNU tar prior to version 1.15 treated .Fl l as a synonym for the .Fl Fl one-file-system option. .Pp The .Fl C Pa dir option may differ from historic implementations. .Pp All archive output is written in correctly-sized blocks, even if the output is being compressed. Whether or not the last output block is padded to a full block size varies depending on the format and the output device. For tar and cpio formats, the last block of output is padded to a full block size if the output is being written to standard output or to a character or block device such as a tape drive. If the output is being written to a regular file, the last block will not be padded. Many compressors, including .Xr gzip 1 and .Xr bzip2 1 , complain about the null padding when decompressing an archive created by .Nm , although they still extract it correctly. .Pp The compression and decompression is implemented internally, so there may be insignificant differences between the compressed output generated by .Dl Nm Fl czf Pa - file and that generated by .Dl Nm Fl cf Pa - file | Nm gzip .Pp The default should be to read and write archives to the standard I/O paths, but tradition (and POSIX) dictates otherwise. .Pp The .Cm r and .Cm u modes require that the archive be uncompressed and located in a regular file on disk. Other archives can be modified using .Cm c mode with the .Pa @archive-file extension. .Pp To archive a file called .Pa @foo or .Pa -foo you must specify it as .Pa ./@foo or .Pa ./-foo , respectively. .Pp In create mode, a leading .Pa ./ is always removed. A leading .Pa / is stripped unless the .Fl P option is specified. .Pp There needs to be better support for file selection on both create and extract. .Pp There is not yet any support for multi-volume archives. .Pp Converting between dissimilar archive formats (such as tar and cpio) using the .Cm @ Ns Pa - convention can cause hard link information to be lost. (This is a consequence of the incompatible ways that different archive formats store hardlink information.) diff --git a/contrib/libarchive/tar/test/test_option_lzma.c b/contrib/libarchive/tar/test/test_option_lzma.c index a618ff8a3403..ab6f13f91744 100644 --- a/contrib/libarchive/tar/test/test_option_lzma.c +++ b/contrib/libarchive/tar/test/test_option_lzma.c @@ -1,60 +1,61 @@ /*- * Copyright (c) 2003-2007 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. * 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_option_lzma) { char *p; int r; size_t s; /* Create a file. */ assertMakeFile("f", 0644, "a"); /* Archive it with lzma compression. */ r = systemf("%s -cf - --lzma f >archive.out 2>archive.err", testprog); p = slurpfile(&s, "archive.err"); p[s] = '\0'; if (r != 0) { if (strstr(p, "Unsupported compression") != NULL) { skipping("This version of bsdtar was compiled " "without lzma support"); + free(p); return; } failure("--lzma option is broken"); assertEqualInt(r, 0); goto done; } free(p); /* Check that the archive file has an lzma signature. */ p = slurpfile(&s, "archive.out"); assert(s > 2); assertEqualMem(p, "\x5d\00\00", 3); done: free(p); } diff --git a/contrib/libarchive/tar/util.c b/contrib/libarchive/tar/util.c index 231c53e2cf6b..37f0d54f4b08 100644 --- a/contrib/libarchive/tar/util.c +++ b/contrib/libarchive/tar/util.c @@ -1,771 +1,762 @@ /*- * 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 "bsdtar_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include /* Linux doesn't define mode_t, etc. in sys/stat.h. */ #endif #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCTYPE_H #include #else /* If we don't have wctype, we need to hack up some version of iswprint(). */ #define iswprint isprint #endif #include "bsdtar.h" #include "err.h" #include "passphrase.h" static size_t bsdtar_expand_char(char *, size_t, size_t, char); static const char *strip_components(const char *path, int elements); #if defined(_WIN32) && !defined(__CYGWIN__) #define read _read #endif /* TODO: Hack up a version of mbtowc for platforms with no wide * character support at all. I think the following might suffice, * but it needs careful testing. * #if !HAVE_MBTOWC * #define mbtowc(wcp, p, n) ((*wcp = *p), 1) * #endif */ /* * Print a string, taking care with any non-printable characters. * * Note that we use a stack-allocated buffer to receive the formatted * string if we can. This is partly performance (avoiding a call to * malloc()), partly out of expedience (we have to call vsnprintf() * before malloc() anyway to find out how big a buffer we need; we may * as well point that first call at a small local buffer in case it * works), but mostly for safety (so we can use this to print messages * about out-of-memory conditions). */ void safe_fprintf(FILE *f, const char *fmt, ...) { char fmtbuff_stack[256]; /* Place to format the printf() string. */ char outbuff[256]; /* Buffer for outgoing characters. */ char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ int fmtbuff_length; int length, n; va_list ap; const char *p; unsigned i; wchar_t wc; char try_wc; /* Use a stack-allocated buffer if we can, for speed and safety. */ fmtbuff_heap = NULL; fmtbuff_length = sizeof(fmtbuff_stack); fmtbuff = fmtbuff_stack; /* Try formatting into the stack buffer. */ va_start(ap, fmt); length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); /* If the result was too large, allocate a buffer on the heap. */ while (length < 0 || length >= fmtbuff_length) { if (length >= fmtbuff_length) fmtbuff_length = length+1; else if (fmtbuff_length < 8192) fmtbuff_length *= 2; else if (fmtbuff_length < 1000000) fmtbuff_length += fmtbuff_length / 4; else { length = fmtbuff_length; fmtbuff_heap[length-1] = '\0'; break; } free(fmtbuff_heap); fmtbuff_heap = malloc(fmtbuff_length); /* Reformat the result into the heap buffer if we can. */ if (fmtbuff_heap != NULL) { fmtbuff = fmtbuff_heap; va_start(ap, fmt); length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); } else { /* Leave fmtbuff pointing to the truncated * string in fmtbuff_stack. */ fmtbuff = fmtbuff_stack; length = sizeof(fmtbuff_stack) - 1; break; } } /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit * more portable, so we use that here instead. */ if (mbtowc(NULL, NULL, 1) == -1) { /* Reset the shift state. */ /* mbtowc() should never fail in practice, but * handle the theoretical error anyway. */ free(fmtbuff_heap); return; } /* Write data, expanding unprintable characters. */ p = fmtbuff; i = 0; try_wc = 1; while (*p != '\0') { /* Convert to wide char, test if the wide * char is printable in the current locale. */ if (try_wc && (n = mbtowc(&wc, p, length)) != -1) { length -= n; if (iswprint(wc) && wc != L'\\') { /* Printable, copy the bytes through. */ while (n-- > 0) outbuff[i++] = *p++; } else { /* Not printable, format the bytes. */ while (n-- > 0) i += (unsigned)bsdtar_expand_char( outbuff, sizeof(outbuff), i, *p++); } } else { /* After any conversion failure, don't bother * trying to convert the rest. */ i += (unsigned)bsdtar_expand_char(outbuff, sizeof(outbuff), i, *p++); try_wc = 0; } /* If our output buffer is full, dump it and keep going. */ if (i > (sizeof(outbuff) - 128)) { outbuff[i] = '\0'; fprintf(f, "%s", outbuff); i = 0; } } outbuff[i] = '\0'; fprintf(f, "%s", outbuff); /* If we allocated a heap-based formatting buffer, free it now. */ free(fmtbuff_heap); } /* * Render an arbitrary sequence of bytes into printable ASCII characters. */ static size_t bsdtar_expand_char(char *buff, size_t buffsize, size_t offset, char c) { size_t i = offset; if (isprint((unsigned char)c) && c != '\\') buff[i++] = c; else { buff[i++] = '\\'; switch (c) { case '\a': buff[i++] = 'a'; break; case '\b': buff[i++] = 'b'; break; case '\f': buff[i++] = 'f'; break; case '\n': buff[i++] = 'n'; break; #if '\r' != '\n' /* On some platforms, \n and \r are the same. */ case '\r': buff[i++] = 'r'; break; #endif case '\t': buff[i++] = 't'; break; case '\v': buff[i++] = 'v'; break; case '\\': buff[i++] = '\\'; break; default: snprintf(buff + i, buffsize - i, "%03o", 0xFF & (int)c); i += 3; } } return (i - offset); } int yes(const char *fmt, ...) { char buff[32]; char *p; ssize_t l; va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, " (y/N)? "); fflush(stderr); l = read(2, buff, sizeof(buff) - 1); if (l < 0) { fprintf(stderr, "Keyboard read failed\n"); exit(1); } if (l == 0) return (0); buff[l] = 0; for (p = buff; *p != '\0'; p++) { if (isspace((unsigned char)*p)) continue; switch(*p) { case 'y': case 'Y': return (1); case 'n': case 'N': return (0); default: return (0); } } return (0); } /*- * The logic here for -C

attempts to avoid * chdir() as long as possible. For example: * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") * "-C /foo -C bar file" needs chdir("/foo/bar") * "-C /foo -C bar /file1" does not need chdir() * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 * * The only correct way to handle this is to record a "pending" chdir * request and combine multiple requests intelligently until we * need to process a non-absolute file. set_chdir() adds the new dir * to the pending list; do_chdir() actually executes any pending chdir. * * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. * */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) { #if defined(_WIN32) && !defined(__CYGWIN__) if (newdir[0] == '/' || newdir[0] == '\\' || /* Detect this type, for example, "C:\" or "C:/" */ (((newdir[0] >= 'a' && newdir[0] <= 'z') || (newdir[0] >= 'A' && newdir[0] <= 'Z')) && newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { #else if (newdir[0] == '/') { #endif /* The -C /foo -C /bar case; dump first one. */ free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } if (bsdtar->pending_chdir == NULL) /* Easy case: no previously-saved dir. */ bsdtar->pending_chdir = strdup(newdir); else { /* The -C /foo -C bar case; concatenate */ char *old_pending = bsdtar->pending_chdir; size_t old_len = strlen(old_pending); size_t new_len = old_len + strlen(newdir) + 2; bsdtar->pending_chdir = malloc(new_len); if (old_pending[old_len - 1] == '/') old_pending[old_len - 1] = '\0'; if (bsdtar->pending_chdir != NULL) snprintf(bsdtar->pending_chdir, new_len, "%s/%s", old_pending, newdir); free(old_pending); } if (bsdtar->pending_chdir == NULL) lafe_errc(1, errno, "No memory"); } void do_chdir(struct bsdtar *bsdtar) { if (bsdtar->pending_chdir == NULL) return; if (chdir(bsdtar->pending_chdir) != 0) { lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } static const char * strip_components(const char *p, int elements) { /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif elements--; break; case '\0': /* Path is too short, skip it. */ return (NULL); } } /* Skip any / characters. This handles short paths that have * additional / termination. This also handles the case where * the logic above stops in the middle of a duplicate // * sequence (which would otherwise get converted to an * absolute path). */ for (;;) { switch (*p) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif ++p; break; case '\0': return (NULL); default: return (p); } } } static void warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading '%c' from member names", c[0]); bsdtar->warned_lead_slash = 1; } } static void warn_strip_drive_letter(struct bsdtar *bsdtar) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; } } /* * Convert absolute path to non-absolute path by skipping leading * absolute path prefixes. */ static const char* strip_absolute_path(struct bsdtar *bsdtar, const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; warn_strip_drive_letter(bsdtar); } /* Remove multiple leading slashes and Windows drive letters. */ do { rp = p; if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; warn_strip_drive_letter(bsdtar); } /* Remove leading "/../", "/./", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" for next pass. */ } else if (p[1] == '.' && (p[2] == '/' || p[2] == '\\')) { p += 2; /* Remove "/.", leave "/" for next pass. */ } else p += 1; /* Remove "/". */ warn_strip_leading_char(bsdtar, rp); } } while (rp != p); return (p); } /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * * Note: The rewrites are applied uniformly to pathnames and hardlink * names but not to symlink bodies. This is deliberate: Symlink * bodies are not necessarily filenames. Even when they are, they * need to be interpreted relative to the directory containing them, * so simple rewrites like this are rarely appropriate. * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); const char *original_name = name; const char *hardlinkname = archive_entry_hardlink(entry); const char *original_hardlinkname = hardlinkname; #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; /* Apply user-specified substitution to pathname. */ r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_pathname(entry, subst_name); if (*subst_name == '\0') { free(subst_name); return -1; } else free(subst_name); name = archive_entry_pathname(entry); original_name = name; } /* Apply user-specified substitution to hardlink target. */ if (hardlinkname != NULL) { r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } hardlinkname = archive_entry_hardlink(entry); original_hardlinkname = hardlinkname; } /* Apply user-specified substitution to symlink body. */ if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_symlink(entry, subst_name); free(subst_name); } } #endif /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); if (hardlinkname != NULL) { hardlinkname = strip_components(hardlinkname, bsdtar->strip_components); if (hardlinkname == NULL) return (1); } } if ((bsdtar->flags & OPTFLAG_ABSOLUTE_PATHS) == 0) { /* By default, don't write or restore absolute pathnames. */ name = strip_absolute_path(bsdtar, name); if (*name == '\0') name = "."; if (hardlinkname != NULL) { hardlinkname = strip_absolute_path(bsdtar, hardlinkname); if (*hardlinkname == '\0') return (1); } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } /* Replace name in archive_entry. */ if (name != original_name) { archive_entry_copy_pathname(entry, name); } if (hardlinkname != original_hardlinkname) { archive_entry_copy_hardlink(entry, hardlinkname); } return (0); } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are quite a headache. Hence the * following simple utility function. */ const char * tar_i64toa(int64_t n0) { static char buff[24]; uint64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); } while (n /= 10); if (n0 < 0) *--p = '-'; return p; } /* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" * * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" * TODO: After this works, push it down into libarchive. * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. * * Note: This is currently only used within write.c, so should * not handle \ path separators. */ int pathcmp(const char *a, const char *b) { /* Skip leading './' */ if (a[0] == '.' && a[1] == '/' && a[2] != '\0') a += 2; if (b[0] == '.' && b[1] == '/' && b[2] != '\0') b += 2; /* Find the first difference, or return (0) if none. */ while (*a == *b) { if (*a == '\0') return (0); a++; b++; } /* * If one ends in '/' and the other one doesn't, * they're the same. */ if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') return (0); if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') return (0); /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); } #define PPBUFF_SIZE 1024 const char * passphrase_callback(struct archive *a, void *_client_data) { struct bsdtar *bsdtar = (struct bsdtar *)_client_data; (void)a; /* UNUSED */ if (bsdtar->ppbuff == NULL) { bsdtar->ppbuff = malloc(PPBUFF_SIZE); if (bsdtar->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", bsdtar->ppbuff, PPBUFF_SIZE); } void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { snprintf(tmp, sizeof(tmp), "%lu ", (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { snprintf(tmp, sizeof(tmp), "%lu", (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(tmp, sizeof(tmp), "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); #define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &tim) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&tim, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = tim; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&tim); #endif strftime(tmp, sizeof(tmp), fmt, ltime); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); } diff --git a/contrib/libarchive/test_utils/test_main.c b/contrib/libarchive/test_utils/test_main.c index 74250ad748c2..e4b884ee3c44 100644 --- a/contrib/libarchive/test_utils/test_main.c +++ b/contrib/libarchive/test_utils/test_main.c @@ -1,4245 +1,4236 @@ /* * 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, + h = CreateFileA(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); vsnprintf(msgbuff, sizeof(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]; static 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); vsnprintf(buff, sizeof(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, + h = CreateFileA(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); } #ifdef HAVE_FCHMOD if (0 != fchmod(fd, mode)) #else if (0 != chmod(path, mode)) #endif { 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 --help %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); vsnprintf(buff, sizeof(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); vsnprintf(filename, sizeof(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; snprintf(buff, sizeof(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. */ 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; snprintf(buff, sizeof(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. */ struct test_list_t { void (*func)(void); const char *name; int failures; }; /* 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 }, static 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. */ snprintf(logfilename, sizeof(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. */ #if defined(PATH_MAX) && !defined(__GLIBC__) 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; } /* Filter tests against a glob pattern. Returns non-zero if test matches * pattern, zero otherwise. A '^' at the beginning of the pattern negates * the return values (i.e. returns zero for a match, non-zero otherwise. */ static int test_filter(const char *pattern, const char *test) { int retval = 0; int negate = 0; const char *p = pattern; const char *t = test; if (p[0] == '^') { negate = 1; p++; } while (1) { if (p[0] == '\\') p++; else if (p[0] == '*') { while (p[0] == '*') p++; if (p[0] == '\\') p++; if ((t = strchr(t, p[0])) == 0) break; } if (p[0] != t[0]) break; if (p[0] == '\0') { retval = 1; break; } p++; t++; } return (negate) ? !retval : retval; } static int get_test_set(int *test_set, int limit, const char *test) { int start, end; int idx = 0; if (test == NULL) { /* Default: Run all tests. */ for (;idx < limit; idx++) test_set[idx] = idx; return (limit); } if (*test >= '0' && *test <= '9') { const char *vp = test; start = 0; while (*vp >= '0' && *vp <= '9') { start *= 10; start += *vp - '0'; ++vp; } if (*vp == '\0') { end = start; } else if (*vp == '-') { ++vp; if (*vp == '\0') { end = limit - 1; } else { end = 0; while (*vp >= '0' && *vp <= '9') { end *= 10; end += *vp - '0'; ++vp; } } } else return (-1); if (start < 0 || end >= limit || start > end) return (-1); while (start <= end) test_set[idx++] = start++; } else { for (start = 0; start < limit; ++start) { const char *name = tests[start].name; if (test_filter(test, name)) test_set[idx++] = start; } } return ((idx == 0)?-1:idx); } 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; int testprogdir_len; -#ifdef PROGRAM +#ifdef PROGRAM int tmp2_len; #endif time_t now; struct tm *tmptr; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; -#endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; #endif 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. */ #if defined(PATH_MAX) && !defined(__GLIBC__) 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]; testprogdir_len = strlen(progname) + 1; if ((testprogdir = (char *)malloc(testprogdir_len)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strncpy(testprogdir, progname, testprogdir_len); 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) { tmp2_len = strlen(testprogdir) + 1 + strlen(PROGRAM) + 1; if ((tmp2 = (char *)malloc(tmp2_len)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strncpy(tmp2, testprogdir, tmp2_len); strncat(tmp2, "/", tmp2_len); strncat(tmp2, PROGRAM, tmp2_len); testprogfile = tmp2; } { char *testprg; int testprg_len; #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_len = strlen(testprogfile) + 3; testprg = malloc(testprg_len); strncpy(testprg, "\"", testprg_len); strncat(testprg, testprogfile, testprg_len); strncat(testprg, "\"", testprg_len); 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++) { -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tmptr = localtime_s(&tmbuf, &now) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tmptr = localtime_r(&now, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = now; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tmptr = NULL; - else - tmptr = &tmbuf; #else tmptr = localtime(&now); #endif strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", tmptr); 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); 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); } diff --git a/contrib/libarchive/unzip/CMakeLists.txt b/contrib/libarchive/unzip/CMakeLists.txt new file mode 100644 index 000000000000..13b983d89db7 --- /dev/null +++ b/contrib/libarchive/unzip/CMakeLists.txt @@ -0,0 +1,37 @@ +############################################ +# +# How to build bsdunzip +# +############################################ +IF(ENABLE_UNZIP) + + SET(bsdunzip_SOURCES + bsdunzip.c + bsdunzip_platform.h + ../libarchive_fe/err.c + ../libarchive_fe/err.h + ../libarchive_fe/lafe_platform.h + ../libarchive_fe/passphrase.c + ../libarchive_fe/passphrase.h + ) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../libarchive_fe) + + # bsdunzip documentation + SET(bsdunzip_MANS bsdunzip.1) + + # How to build bsdunzip + ADD_EXECUTABLE(bsdunzip ${bsdunzip_SOURCES}) + IF(ENABLE_UNZIP_SHARED) + TARGET_LINK_LIBRARIES(bsdunzip archive ${ADDITIONAL_LIBS}) + ELSE(ENABLE_UNZIP_SHARED) + TARGET_LINK_LIBRARIES(bsdunzip archive_static ${ADDITIONAL_LIBS}) + SET_TARGET_PROPERTIES(bsdunzip PROPERTIES COMPILE_DEFINITIONS + LIBARCHIVE_STATIC) + ENDIF(ENABLE_UNZIP_SHARED) + + # Installation rules + INSTALL(TARGETS bsdunzip RUNTIME DESTINATION bin) + INSTALL_MAN(${bsdunzip_MANS}) +ENDIF(ENABLE_UNZIP) + +add_subdirectory(test) diff --git a/contrib/libarchive/unzip/bsdunzip.1 b/contrib/libarchive/unzip/bsdunzip.1 new file mode 100644 index 000000000000..3c656ebc46e2 --- /dev/null +++ b/contrib/libarchive/unzip/bsdunzip.1 @@ -0,0 +1,216 @@ +.\"- +.\" Copyright (c) 2007-2008 Dag-Erling Smørgrav +.\" 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 January 2, 2023 +.Dt BSDUNZIP 1 +.Os +.Sh NAME +.Nm bsdunzip +.Nd extract files from a ZIP archive +.Sh SYNOPSIS +.Nm +.Op Fl aCcfjLlnopqtuvy +.Op Fl d Ar dir +.Op Fl x Ar pattern +.Op Fl P Ar password +.Ar zipfile +.Op Ar member ... +.Sh DESCRIPTION +.\" ... +The following options are available: +.Bl -tag -width Fl +.It Fl a +When extracting a text file, convert DOS-style line endings to +Unix-style line endings. +.It Fl C +Match file names case-insensitively. +.It Fl c +Extract to stdout/screen. +When extracting files from the zipfile, they are written to stdout. +This is similar to +.Fl p , +but does not suppress normal output. +.It Fl d Ar dir +Extract files into the specified directory rather than the current +directory. +.It Fl f +Update existing. +Extract only files from the zipfile if a file with the same name +already exists on disk and is older than the former. +Otherwise, the file is silently skipped. +.It Fl j +Ignore directories stored in the zipfile; instead, extract all files +directly into the extraction directory. +.It Fl L +Convert the names of the extracted files and directories to lowercase. +.It Fl l +List, rather than extract, the contents of the zipfile. +.It Fl n +No overwrite. +When extracting a file from the zipfile, if a file with the same name +already exists on disk, the file is silently skipped. +.It Fl o +Overwrite. +When extracting a file from the zipfile, if a file with the same name +already exists on disk, the existing file is replaced with the file +from the zipfile. +.It Fl p +Extract to stdout. +When extracting files from the zipfile, they are written to stdout. +The normal output is suppressed as if +.Fl q +was specified. +.It Fl P Ar password +Extract encrypted files using a password. +Putting a password on the command line using this option can be +insecure. +.It Fl q +Quiet: print less information while extracting. +.It Fl t +Test: do not extract anything, but verify the checksum of every file +in the archive. +.It Fl u +Update. +When extracting a file from the zipfile, if a file with the same name +already exists on disk, the existing file is replaced with the file +from the zipfile if and only if the latter is newer than the former. +Otherwise, the file is silently skipped. +.It Fl v +List verbosely, rather than extract, the contents of the zipfile. +This differs from +.Fl l +by using the long listing. +Note that most of the data is currently fake and does not reflect the +content of the archive. +.It Fl x Ar pattern +Exclude files matching the pattern +.Ar pattern . +.It Fl y +Print four digit years in listings instead of two. +.It Fl Z Ar mode +Emulate +.Xr zipinfo 1L +mode. +Enabling +.Xr zipinfo 1L +mode changes the way in which additional arguments are parsed. +Currently only +.Xr zipinfo 1L +mode 1 is supported, which lists the file names one per line. +.It Ar [member ...] +Optional list of members to extract from the zipfile. +Can include patterns, e.g. +.Ar 'memberdir/*' +will extract all files and dirs below memberdir. +.El +.Pp +Note that only one of +.Fl n , +.Fl o , +and +.Fl u +may be specified. +If specified filename is +.Qq - , +then data is read from +.Va stdin . +.Sh ENVIRONMENT +If the +.Ev UNZIP_DEBUG +environment variable is defined, the +.Fl q +command-line option has no effect, and additional debugging +information will be printed to +.Va stderr . +.Sh COMPATIBILITY +The +.Nm +utility aims to be sufficiently compatible with other implementations +to serve as a drop-in replacement in the context of the +.Xr ports 7 +system. +No attempt has been made to replicate functionality which is not +required for that purpose. +.Pp +For compatibility reasons, command-line options will be recognized if +they are listed not only before but also after the name of the +zipfile. +.Pp +Normally, the +.Fl a +option should only affect files which are marked as text files in the +zipfile's central directory. +Since the +.Xr archive 3 +library does not provide access to that information, it is not available +to the +.Nm +utility. +Instead, the +.Nm +utility will assume that a file is a text file if no non-ASCII +characters are present within the first block of data decompressed for +that file. +If non-ASCII characters appear in subsequent blocks of data, a warning +will be issued. +.Pp +The +.Nm +utility is only able to process ZIP archives handled by +.Xr libarchive 3 . +Depending on the installed version of +.Xr libarchive 3 , +this may or may not include self-extracting or ZIPX archives. +.Sh SEE ALSO +.Xr libarchive 3 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 8.0 . +.Sh AUTHORS +The +.Nm +utility and this manual page were written by +.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . +It uses the +.Xr archive 3 +library developed by +.An Tim Kientzle Aq Mt kientzle@FreeBSD.org . +.Sh CAVEATS +The +.Nm +utility performs two scans of the command-line for arguments before +and after the archive name, so as to maintain compatibility with +Info-ZIP unzip. +As a result, the POSIX +.Ql -- +double-dash string used to separate options from arguments will need to +be repeated. +For example, to extract a "-a.jpg" from "-b.zip" with overwrite, one +would need to invoke +.Dl bsdunzip -o -- -a.jpg -- -b.zip diff --git a/contrib/libarchive/unzip/bsdunzip.c b/contrib/libarchive/unzip/bsdunzip.c new file mode 100644 index 000000000000..469c69fd6efb --- /dev/null +++ b/contrib/libarchive/unzip/bsdunzip.c @@ -0,0 +1,1186 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2009, 2010 Joerg Sonnenberger + * Copyright (c) 2007-2008 Dag-Erling Smørgrav + * 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 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$ + * + * This file would be much shorter if we didn't care about command-line + * compatibility with Info-ZIP's UnZip, which requires us to duplicate + * parts of libarchive in order to gain more detailed control of its + * behaviour for the purpose of implementing the -n, -o, -L and -a + * options. + */ + +#include "bsdunzip_platform.h" + +#ifdef HAVE_SYS_QUEUE_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_CTYPE_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_FNMATCH_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include "passphrase.h" +#include "err.h" + +/* command-line options */ +static int a_opt; /* convert EOL */ +static int C_opt; /* match case-insensitively */ +static int c_opt; /* extract to stdout */ +static const char *d_arg; /* directory */ +static int f_opt; /* update existing files only */ +static int j_opt; /* junk directories */ +static int L_opt; /* lowercase names */ +static int n_opt; /* never overwrite */ +static int o_opt; /* always overwrite */ +static int p_opt; /* extract to stdout, quiet */ +static char *P_arg; /* passphrase */ +static int q_opt; /* quiet */ +static int t_opt; /* test */ +static int u_opt; /* update */ +static int v_opt; /* verbose/list */ +static const char *y_str = ""; /* 4 digit year */ +static int Z1_opt; /* zipinfo mode list files only */ + +/* debug flag */ +static int unzip_debug; + +/* zipinfo mode */ +static int zipinfo_mode; + +/* running on tty? */ +static int tty; + +/* convenience macro */ +/* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */ +#define ac(call) \ + do { \ + int acret = (call); \ + if (acret != ARCHIVE_OK) \ + errorx("%s", archive_error_string(a)); \ + } while (0) + +/* + * Indicates that last info() did not end with EOL. This helps error() et + * al. avoid printing an error message on the same line as an incomplete + * informational message. + */ +static int noeol; + +/* for an interactive passphrase input */ +static char *passphrase_buf; + +/* fatal error message + errno */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + if (noeol) + fprintf(stdout, "\n"); + fflush(stdout); + fprintf(stderr, "unzip: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(errno)); + exit(EXIT_FAILURE); +} + +/* fatal error message, no errno */ +static void +errorx(const char *fmt, ...) +{ + va_list ap; + + if (noeol) + fprintf(stdout, "\n"); + fflush(stdout); + fprintf(stderr, "unzip: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +/* non-fatal error message + errno */ +static void +warning(const char *fmt, ...) +{ + va_list ap; + + if (noeol) + fprintf(stdout, "\n"); + fflush(stdout); + fprintf(stderr, "unzip: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(errno)); +} + +/* non-fatal error message, no errno */ +static void +warningx(const char *fmt, ...) +{ + va_list ap; + + if (noeol) + fprintf(stdout, "\n"); + fflush(stdout); + fprintf(stderr, "unzip: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +/* informational message (if not -q) */ +static void +info(const char *fmt, ...) +{ + va_list ap; + + if (q_opt && !unzip_debug) + return; + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + + if (*fmt == '\0') + noeol = 1; + else + noeol = fmt[strlen(fmt) - 1] != '\n'; +} + +/* debug message (if unzip_debug) */ +static void +debug(const char *fmt, ...) +{ + va_list ap; + + if (!unzip_debug) + return; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fflush(stderr); + + if (*fmt == '\0') + noeol = 1; + else + noeol = fmt[strlen(fmt) - 1] != '\n'; +} + +/* duplicate a path name, possibly converting to lower case */ +static char * +pathdup(const char *path) +{ + char *str; + size_t i, len; + + if (path == NULL || path[0] == '\0') + return (NULL); + + len = strlen(path); + while (len && path[len - 1] == '/') + len--; + if ((str = malloc(len + 1)) == NULL) { + errno = ENOMEM; + error("malloc()"); + } + if (L_opt) { + for (i = 0; i < len; ++i) + str[i] = tolower((unsigned char)path[i]); + } else { + memcpy(str, path, len); + } + str[len] = '\0'; + + return (str); +} + +/* concatenate two path names */ +static char * +pathcat(const char *prefix, const char *path) +{ + char *str; + size_t prelen, len; + + prelen = prefix ? strlen(prefix) + 1 : 0; + len = strlen(path) + 1; + if ((str = malloc(prelen + len)) == NULL) { + errno = ENOMEM; + error("malloc()"); + } + if (prefix) { + memcpy(str, prefix, prelen); /* includes zero */ + str[prelen - 1] = '/'; /* splat zero */ + } + memcpy(str + prelen, path, len); /* includes zero */ + + return (str); +} + +/* + * Pattern lists for include / exclude processing + */ +struct pattern { + STAILQ_ENTRY(pattern) link; + char pattern[]; +}; + +STAILQ_HEAD(pattern_list, pattern); +static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include); +static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude); + +/* + * Add an entry to a pattern list + */ +static void +add_pattern(struct pattern_list *list, const char *pattern) +{ + struct pattern *entry; + size_t len; + + debug("adding pattern '%s'\n", pattern); + len = strlen(pattern); + if ((entry = malloc(sizeof *entry + len + 1)) == NULL) { + errno = ENOMEM; + error("malloc()"); + } + memcpy(entry->pattern, pattern, len + 1); + STAILQ_INSERT_TAIL(list, entry, link); +} + +/* + * Match a string against a list of patterns + */ +static int +match_pattern(struct pattern_list *list, const char *str) +{ + struct pattern *entry; + + STAILQ_FOREACH(entry, list, link) { +#ifdef HAVE_FNMATCH + if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0) + return (1); +#else +#error "Unsupported platform: fnmatch() is required" +#endif + } + return (0); +} + +/* + * Verify that a given pathname is in the include list and not in the + * exclude list. + */ +static int +accept_pathname(const char *pathname) +{ + + if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname)) + return (0); + if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname)) + return (0); + return (1); +} + +/* + * Create the specified directory with the specified mode, taking certain + * precautions on they way. + */ +static void +make_dir(const char *path, int mode) +{ + struct stat sb; + + if (lstat(path, &sb) == 0) { + if (S_ISDIR(sb.st_mode)) + return; + /* + * Normally, we should either ask the user about removing + * the non-directory of the same name as a directory we + * wish to create, or respect the -n or -o command-line + * options. However, this may lead to a later failure or + * even compromise (if this non-directory happens to be a + * symlink to somewhere unsafe), so we don't. + */ + + /* + * Don't check unlink() result; failure will cause mkdir() + * to fail later, which we will catch. + */ + (void)unlink(path); + } + if (mkdir(path, mode) != 0 && errno != EEXIST) + error("mkdir('%s')", path); +} + +/* + * Ensure that all directories leading up to (but not including) the + * specified path exist. + * + * XXX inefficient + modifies the file in-place + */ +static void +make_parent(char *path) +{ + struct stat sb; + char *sep; + + sep = strrchr(path, '/'); + if (sep == NULL || sep == path) + return; + *sep = '\0'; + if (lstat(path, &sb) == 0) { + if (S_ISDIR(sb.st_mode)) { + *sep = '/'; + return; + } + unlink(path); + } + make_parent(path); + mkdir(path, 0755); + *sep = '/'; + +#if 0 + for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { + /* root in case of absolute d_arg */ + if (sep == path) + continue; + *sep = '\0'; + make_dir(path, 0755); + *sep = '/'; + } +#endif +} + +/* + * Extract a directory. + */ +static void +extract_dir(struct archive *a, struct archive_entry *e, const char *path) +{ + int mode; + + /* + * Dropbox likes to create '/' directory entries, just ignore + * such junk. + */ + if (*path == '\0') + return; + + mode = archive_entry_mode(e) & 0777; + if (mode == 0) + mode = 0755; + + /* + * Some zipfiles contain directories with weird permissions such + * as 0644 or 0444. This can cause strange issues such as being + * unable to extract files into the directory we just created, or + * the user being unable to remove the directory later without + * first manually changing its permissions. Therefore, we whack + * the permissions into shape, assuming that the user wants full + * access and that anyone who gets read access also gets execute + * access. + */ + mode |= 0700; + if (mode & 0040) + mode |= 0010; + if (mode & 0004) + mode |= 0001; + + info(" creating: %s/\n", path); + make_dir(path, mode); + ac(archive_read_data_skip(a)); +} + +static unsigned char buffer[8192]; +static char spinner[] = { '|', '/', '-', '\\' }; + +static int +handle_existing_file(char **path) +{ + size_t alen; + ssize_t len; + char buf[4]; + + for (;;) { + fprintf(stderr, + "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", + *path); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + clearerr(stdin); + printf("NULL\n(EOF or read error, " + "treating as \"[N]one\"...)\n"); + n_opt = 1; + return -1; + } + switch (*buf) { + case 'A': + o_opt = 1; + /* FALLTHROUGH */ + case 'y': + case 'Y': + (void)unlink(*path); + return 1; + case 'N': + n_opt = 1; + /* FALLTHROUGH */ + case 'n': + return -1; + case 'r': + case 'R': + printf("New name: "); + fflush(stdout); + free(*path); + *path = NULL; + alen = 0; + len = getline(path, &alen, stdin); + if ((*path)[len - 1] == '\n') + (*path)[len - 1] = '\0'; + return 0; + default: + break; + } + } +} + +/* + * Detect binary files by a combination of character white list and + * black list. NUL bytes and other control codes without use in text files + * result directly in switching the file to binary mode. Otherwise, at least + * one white-listed byte has to be found. + * + * Black-listed: 0..6, 14..25, 28..31 + * 0xf3ffc07f = 11110011111111111100000001111111b + * White-listed: 9..10, 13, >= 32 + * 0x00002600 = 00000000000000000010011000000000b + * + * See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion. + */ +#define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x)))) +#define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x)))) + +static int +check_binary(const unsigned char *buf, size_t len) +{ + int rv; + for (rv = 1; len--; ++buf) { + if (BYTE_IS_BINARY(*buf)) + return 1; + if (BYTE_IS_TEXT(*buf)) + rv = 0; + } + + return rv; +} + +/* + * Extract to a file descriptor + */ +static int +extract2fd(struct archive *a, char *pathname, int fd) +{ + int cr, text, warn; + ssize_t len; + unsigned char *p, *q, *end; + + text = a_opt; + warn = 0; + cr = 0; + + /* loop over file contents and write to fd */ + for (int n = 0; ; n++) { + if (fd != STDOUT_FILENO) + if (tty && (n % 4) == 0) + info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); + + len = archive_read_data(a, buffer, sizeof buffer); + + if (len < 0) + ac(len); + + /* left over CR from previous buffer */ + if (a_opt && cr) { + if (len == 0 || buffer[0] != '\n') + if (write(fd, "\r", 1) != 1) + error("write('%s')", pathname); + cr = 0; + } + + /* EOF */ + if (len == 0) + break; + end = buffer + len; + + /* + * Detect whether this is a text file. The correct way to + * do this is to check the least significant bit of the + * "internal file attributes" field of the corresponding + * file header in the central directory, but libarchive + * does not provide access to this field, so we have to + * guess by looking for non-ASCII characters in the + * buffer. Hopefully we won't guess wrong. If we do + * guess wrong, we print a warning message later. + */ + if (a_opt && n == 0) { + if (check_binary(buffer, len)) + text = 0; + } + + /* simple case */ + if (!a_opt || !text) { + if (write(fd, buffer, len) != len) + error("write('%s')", pathname); + continue; + } + + /* hard case: convert \r\n to \n (sigh...) */ + for (p = buffer; p < end; p = q + 1) { + for (q = p; q < end; q++) { + if (!warn && BYTE_IS_BINARY(*q)) { + warningx("%s may be corrupted due" + " to weak text file detection" + " heuristic", pathname); + warn = 1; + } + if (q[0] != '\r') + continue; + if (&q[1] == end) { + cr = 1; + break; + } + if (q[1] == '\n') + break; + } + if (write(fd, p, q - p) != q - p) + error("write('%s')", pathname); + } + } + + return text; +} + +/* + * Extract a regular file. + */ +static void +extract_file(struct archive *a, struct archive_entry *e, char **path) +{ + int mode; + struct timespec mtime; + struct stat sb; + struct timespec ts[2]; + int fd, check, text; + const char *linkname; + + mode = archive_entry_mode(e) & 0777; + if (mode == 0) + mode = 0644; + mtime.tv_sec = archive_entry_mtime(e); + mtime.tv_nsec = archive_entry_mtime_nsec(e); + + /* look for existing file of same name */ +recheck: + if (lstat(*path, &sb) == 0) { + if (u_opt || f_opt) { + /* check if up-to-date */ + if (S_ISREG(sb.st_mode) && ( +#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC + sb.st_mtimespec.tv_sec > mtime.tv_sec || + (sb.st_mtimespec.tv_sec == mtime.tv_sec && + sb.st_mtimespec.tv_nsec >= mtime.tv_nsec) +#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + sb.st_mtim.tv_sec > mtime.tv_sec || + (sb.st_mtim.tv_sec == mtime.tv_sec && + sb.st_mtim.tv_nsec >= mtime.tv_nsec) +#elif HAVE_STRUCT_STAT_ST_MTIME_N + sb.st_mtime > mtime.tv_sec || + (sb.st_mtime == mtime.tv_sec && + sb.st_mtime_n => mtime.tv_nsec) +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC + sb.st_mtime > mtime.tv_sec || + (sb.st_mtime == mtime.tv_sec && + sb.st_mtime_usec => mtime.tv_nsec / 1000) +#else + sb.st_mtime > mtime.tv_sec +#endif + )) + return; + (void)unlink(*path); + } else if (o_opt) { + /* overwrite */ + (void)unlink(*path); + } else if (n_opt) { + /* do not overwrite */ + return; + } else { + check = handle_existing_file(path); + if (check == 0) + goto recheck; + if (check == -1) + return; /* do not overwrite */ + } + } else { + if (f_opt) + return; + } + + ts[0].tv_sec = 0; + ts[0].tv_nsec = UTIME_NOW; + ts[1] = mtime; + + /* process symlinks */ + linkname = archive_entry_symlink(e); + if (linkname != NULL) { + if (symlink(linkname, *path) != 0) + error("symlink('%s')", *path); + info(" extracting: %s -> %s\n", *path, linkname); + if (lchmod(*path, mode) != 0) + warning("Cannot set mode for '%s'", *path); + /* set access and modification time */ + if (utimensat(AT_FDCWD, *path, ts, AT_SYMLINK_NOFOLLOW) != 0) + warning("utimensat('%s')", *path); + return; + } + + if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) + error("open('%s')", *path); + + info(" extracting: %s", *path); + + text = extract2fd(a, *path, fd); + + if (tty) + info(" \b\b"); + if (text) + info(" (text)"); + info("\n"); + + /* set access and modification time */ + if (futimens(fd, ts) != 0) + error("futimens('%s')", *path); + if (close(fd) != 0) + error("close('%s')", *path); +} + +/* + * Extract a zipfile entry: first perform some sanity checks to ensure + * that it is either a directory or a regular file and that the path is + * not absolute and does not try to break out of the current directory; + * then call either extract_dir() or extract_file() as appropriate. + * + * This is complicated a bit by the various ways in which we need to + * manipulate the path name. Case conversion (if requested by the -L + * option) happens first, but the include / exclude patterns are applied + * to the full converted path name, before the directory part of the path + * is removed in accordance with the -j option. Sanity checks are + * intentionally done earlier than they need to be, so the user will get a + * warning about insecure paths even for files or directories which + * wouldn't be extracted anyway. + */ +static void +extract(struct archive *a, struct archive_entry *e) +{ + char *pathname, *realpathname; + mode_t filetype; + char *p, *q; + + if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) { + warningx("skipping empty or unreadable filename entry"); + ac(archive_read_data_skip(a)); + return; + } + filetype = archive_entry_filetype(e); + + /* sanity checks */ + if (pathname[0] == '/' || + strncmp(pathname, "../", 3) == 0 || + strstr(pathname, "/../") != NULL) { + warningx("skipping insecure entry '%s'", pathname); + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + /* I don't think this can happen in a zipfile.. */ + if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { + warningx("skipping non-regular entry '%s'", pathname); + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + /* skip directories in -j case */ + if (S_ISDIR(filetype) && j_opt) { + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + /* apply include / exclude patterns */ + if (!accept_pathname(pathname)) { + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + /* apply -j and -d */ + if (j_opt) { + for (p = q = pathname; *p; ++p) + if (*p == '/') + q = p + 1; + realpathname = pathcat(d_arg, q); + } else { + realpathname = pathcat(d_arg, pathname); + } + + /* ensure that parent directory exists */ + make_parent(realpathname); + + if (S_ISDIR(filetype)) + extract_dir(a, e, realpathname); + else + extract_file(a, e, &realpathname); + + free(realpathname); + free(pathname); +} + +static void +extract_stdout(struct archive *a, struct archive_entry *e) +{ + char *pathname; + mode_t filetype; + + if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) { + warningx("skipping empty or unreadable filename entry"); + ac(archive_read_data_skip(a)); + return; + } + filetype = archive_entry_filetype(e); + + /* I don't think this can happen in a zipfile.. */ + if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { + warningx("skipping non-regular entry '%s'", pathname); + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + /* skip directories in -j case */ + if (S_ISDIR(filetype)) { + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + /* apply include / exclude patterns */ + if (!accept_pathname(pathname)) { + ac(archive_read_data_skip(a)); + free(pathname); + return; + } + + if (c_opt) + info("x %s\n", pathname); + + (void)extract2fd(a, pathname, STDOUT_FILENO); + + free(pathname); +} + +/* + * Print the name of an entry to stdout. + */ +static void +list(struct archive *a, struct archive_entry *e) +{ + char buf[20]; + time_t mtime; + struct tm *tm; + + mtime = archive_entry_mtime(e); + tm = localtime(&mtime); + if (*y_str) + strftime(buf, sizeof(buf), "%m-%d-%G %R", tm); + else + strftime(buf, sizeof(buf), "%m-%d-%g %R", tm); + + if (!zipinfo_mode) { + if (v_opt == 1) { + printf(" %8ju %s %s\n", + (uintmax_t)archive_entry_size(e), + buf, archive_entry_pathname(e)); + } else if (v_opt == 2) { + printf("%8ju Stored %7ju 0%% %s %08x %s\n", + (uintmax_t)archive_entry_size(e), + (uintmax_t)archive_entry_size(e), + buf, + 0U, + archive_entry_pathname(e)); + } + } else { + if (Z1_opt) + printf("%s\n",archive_entry_pathname(e)); + } + ac(archive_read_data_skip(a)); +} + +/* + * Extract to memory to check CRC + */ +static int +test(struct archive *a, struct archive_entry *e) +{ + ssize_t len; + int error_count; + + error_count = 0; + if (S_ISDIR(archive_entry_filetype(e))) + return 0; + + info(" testing: %s\t", archive_entry_pathname(e)); + while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0) + /* nothing */; + if (len < 0) { + info(" %s\n", archive_error_string(a)); + ++error_count; + } else { + info(" OK\n"); + } + + /* shouldn't be necessary, but it doesn't hurt */ + ac(archive_read_data_skip(a)); + + return error_count; +} + +/* + * Callback function for reading passphrase. + * Originally from cpio.c and passphrase.c, libarchive. + */ +#define PPBUFF_SIZE 1024 +static const char * +passphrase_callback(struct archive *a, void *_client_data) +{ + char *p; + + (void)a; /* UNUSED */ + (void)_client_data; /* UNUSED */ + + if (passphrase_buf == NULL) { + passphrase_buf = malloc(PPBUFF_SIZE); + if (passphrase_buf == NULL) { + errno = ENOMEM; + error("malloc()"); + } + } + + p = lafe_readpassphrase("\nEnter password: ", passphrase_buf, + PPBUFF_SIZE); + + if (p == NULL && errno != EINTR) + error("Error reading password"); + + return p; +} + +/* + * Main loop: open the zipfile, iterate over its contents and decide what + * to do with each entry. + */ +static void +unzip(const char *fn) +{ + struct archive *a; + struct archive_entry *e; + int ret; + uintmax_t total_size, file_count, error_count; + + if ((a = archive_read_new()) == NULL) + error("archive_read_new failed"); + + ac(archive_read_support_format_zip(a)); + + if (P_arg) + archive_read_add_passphrase(a, P_arg); + else + archive_read_set_passphrase_callback(a, NULL, + &passphrase_callback); + + ac(archive_read_open_filename(a, fn, 8192)); + + if (!zipinfo_mode) { + if (!p_opt && !q_opt) + printf("Archive: %s\n", fn); + if (v_opt == 1) { + printf(" Length %sDate Time Name\n", y_str); + printf(" -------- %s---- ---- ----\n", y_str); + } else if (v_opt == 2) { + printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str); + printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str); + } + } + + total_size = 0; + file_count = 0; + error_count = 0; + for (;;) { + ret = archive_read_next_header(a, &e); + if (ret == ARCHIVE_EOF) + break; + ac(ret); + if (!zipinfo_mode) { + if (t_opt) + error_count += test(a, e); + else if (v_opt) + list(a, e); + else if (p_opt || c_opt) + extract_stdout(a, e); + else + extract(a, e); + } else { + if (Z1_opt) + list(a, e); + } + + total_size += archive_entry_size(e); + ++file_count; + } + + if (zipinfo_mode) { + if (v_opt == 1) { + printf(" -------- %s-------\n", y_str); + printf(" %8ju %s%ju file%s\n", + total_size, y_str, file_count, file_count != 1 ? "s" : ""); + } else if (v_opt == 2) { + printf("-------- ------- --- %s-------\n", y_str); + printf("%8ju %7ju 0%% %s%ju file%s\n", + total_size, total_size, y_str, file_count, + file_count != 1 ? "s" : ""); + } + } + + ac(archive_read_free(a)); + + if (passphrase_buf != NULL) { + memset(passphrase_buf, 0, PPBUFF_SIZE); + free(passphrase_buf); + } + + if (t_opt) { + if (error_count > 0) { + errorx("%ju checksum error(s) found.", error_count); + } + else { + printf("No errors detected in compressed data of %s.\n", + fn); + } + } +} + +static void +usage(void) +{ + + fprintf(stderr, +"Usage: unzip [-aCcfjLlnopqtuvyZ1] [-d dir] [-x pattern] [-P password] zipfile\n" +" [member ...]\n"); + exit(EXIT_FAILURE); +} + +static int +getopts(int argc, char *argv[]) +{ + int opt; + + optreset = optind = 1; + while ((opt = getopt(argc, argv, "aCcd:fjLlnopP:qtuvx:yZ1")) != -1) + switch (opt) { + case '1': + Z1_opt = 1; + break; + case 'a': + a_opt = 1; + break; + case 'C': + C_opt = 1; + break; + case 'c': + c_opt = 1; + break; + case 'd': + d_arg = optarg; + break; + case 'f': + f_opt = 1; + break; + case 'j': + j_opt = 1; + break; + case 'L': + L_opt = 1; + break; + case 'l': + if (v_opt == 0) + v_opt = 1; + break; + case 'n': + n_opt = 1; + break; + case 'o': + o_opt = 1; + q_opt = 1; + break; + case 'p': + p_opt = 1; + break; + case 'P': + P_arg = optarg; + break; + case 'q': + q_opt = 1; + break; + case 't': + t_opt = 1; + break; + case 'u': + u_opt = 1; + break; + case 'v': + v_opt = 2; + break; + case 'x': + add_pattern(&exclude, optarg); + break; + case 'y': + y_str = " "; + break; + case 'Z': + zipinfo_mode = 1; + break; + default: + usage(); + } + + return (optind); +} + +int +main(int argc, char *argv[]) +{ + const char *zipfile; + int nopts; + + if (isatty(STDOUT_FILENO)) + tty = 1; + + if (getenv("UNZIP_DEBUG") != NULL) + unzip_debug = 1; + for (int i = 0; i < argc; ++i) + debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); + +#ifdef __GLIBC__ + /* Prevent GNU getopt(3) from rearranging options. */ + setenv("POSIXLY_CORRECT", "", 1); +#endif + /* + * Info-ZIP's unzip(1) expects certain options to come before the + * zipfile name, and others to come after - though it does not + * enforce this. For simplicity, we accept *all* options both + * before and after the zipfile name. + */ + nopts = getopts(argc, argv); + + /* + * When more of the zipinfo mode options are implemented, this + * will need to change. + */ + if (zipinfo_mode && !Z1_opt) { + printf("Zipinfo mode needs additional options\n"); + exit(EXIT_FAILURE); + } + + if (argc <= nopts) + usage(); + zipfile = argv[nopts++]; + + if (strcmp(zipfile, "-") == 0) + zipfile = NULL; /* STDIN */ + + while (nopts < argc && *argv[nopts] != '-') + add_pattern(&include, argv[nopts++]); + + nopts--; /* fake argv[0] */ + nopts += getopts(argc - nopts, argv + nopts); + + /* There may be residual arguments if we encountered -- */ + while (nopts < argc) + add_pattern(&include, argv[nopts++]); + + if (n_opt + o_opt + u_opt > 1) + errorx("-n, -o and -u are contradictory"); + + unzip(zipfile); + + exit(EXIT_SUCCESS); +} diff --git a/contrib/libarchive/unzip/bsdunzip_platform.h b/contrib/libarchive/unzip/bsdunzip_platform.h new file mode 100644 index 000000000000..5aff5f208eab --- /dev/null +++ b/contrib/libarchive/unzip/bsdunzip_platform.h @@ -0,0 +1,83 @@ +/*- + * 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: src/usr.bin/tar/bsdtar_platform.h,v 1.26 2008/12/06 07:37:14 kientzle Exp $ + */ + +/* + * This header is the first thing included in any of the bsdtar + * source files. As far as possible, platform-specific issues should + * be dealt with here and not within individual source files. + */ + +#ifndef BSDUNZIP_PLATFORM_H_INCLUDED +#define BSDUNZIP_PLATFORM_H_INCLUDED + +#if defined(PLATFORM_CONFIG_H) +/* Use hand-built config.h in environments that need it. */ +#include PLATFORM_CONFIG_H +#else +/* Not having a config.h of some sort is a serious problem. */ +#include "config.h" +#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 + +#ifdef HAVE_LIBARCHIVE +/* If we're using the platform libarchive, include system headers. */ +#include +#include +#else +/* Otherwise, include user headers. */ +#include "archive.h" +#include "archive_entry.h" +#endif + +#ifndef HAVE_GETOPT_OPTRESET +/* + * If platform doesn't use optreset for resetting getopt, declare it so + * C source doesn't have to know this platform-specific difference + */ +int optreset; +#endif + +/* How to mark functions that don't return. */ +/* This facilitates use of some newer static code analysis tools. */ +#undef __LA_DEAD +#if defined(__GNUC__) && (__GNUC__ > 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) +#define __LA_DEAD __attribute__((__noreturn__)) +#else +#define __LA_DEAD +#endif + +#endif /* !BSDUNZIP_PLATFORM_H_INCLUDED */ diff --git a/contrib/libarchive/unzip/test/CMakeLists.txt b/contrib/libarchive/unzip/test/CMakeLists.txt new file mode 100644 index 000000000000..bdb3445954f1 --- /dev/null +++ b/contrib/libarchive/unzip/test/CMakeLists.txt @@ -0,0 +1,80 @@ +############################################ +# +# How to build bsdunzip_test +# +############################################ +IF(ENABLE_UNZIP AND ENABLE_TEST) + SET(bsdunzip_test_SOURCES + ../../libarchive_fe/err.c + ../../test_utils/test_utils.c + ../../test_utils/test_main.c + test.h + test_0.c + test_basic.c + test_glob.c + test_singlefile.c + test_C.c + test_p.c + test_d.c + test_j.c + test_L.c + test_n.c + test_o.c + test_q.c + test_t.c + test_t_bad.c + test_x.c + test_Z1.c + test_P_encryption.c + ) + + # + # Register target + # + ADD_EXECUTABLE(bsdunzip_test ${bsdunzip_test_SOURCES}) + IF(ENABLE_ACL) + SET(TEST_ACL_LIBS "") + IF(HAVE_LIBACL) + LIST(APPEND TEST_ACL_LIBS ${ACL_LIBRARY}) + ENDIF(HAVE_LIBACL) + IF(HAVE_LIBRICHACL) + LIST(APPEND TEST_ACL_LIBS ${RICHACL_LIBRARY}) + ENDIF(HAVE_LIBRICHACL) + TARGET_LINK_LIBRARIES(bsdunzip_test ${TEST_ACL_LIBS}) + ENDIF(ENABLE_ACL) + SET_PROPERTY(TARGET bsdunzip_test PROPERTY COMPILE_DEFINITIONS LIST_H) + + # + # Generate list.h by grepping DEFINE_TEST() lines out of the C sources. + # + GENERATE_LIST_H(${CMAKE_CURRENT_BINARY_DIR}/list.h + ${CMAKE_CURRENT_LIST_FILE} ${bsdunzip_test_SOURCES}) + SET_PROPERTY(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_BINARY_DIR}) + + # list.h has a line DEFINE_TEST(testname) for every + # test. We can use that to define the tests for cmake by + # defining a DEFINE_TEST macro and reading list.h in. + MACRO (DEFINE_TEST _testname) + ADD_TEST( + NAME bsdunzip_${_testname} + COMMAND bsdunzip_test -vv + -p $ + -r ${CMAKE_CURRENT_SOURCE_DIR} + ${_testname}) + ENDMACRO (DEFINE_TEST _testname) + + INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/unzip/test) + + # Experimental new test handling + ADD_CUSTOM_TARGET(run_bsdunzip_test + COMMAND bsdunzip_test -p $ + -r ${CMAKE_CURRENT_SOURCE_DIR} + -vv) + ADD_DEPENDENCIES(run_bsdunzip_test bsdunzip) + ADD_DEPENDENCIES(run_all_tests run_bsdunzip_test) +ENDIF(ENABLE_UNZIP AND ENABLE_TEST) + diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test.h similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test.h index ebb06702d0c5..8da017f68e16 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test.h @@ -1,53 +1,40 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2003-2017 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_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) -{ - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} +/* Every test program should #include "test.h" as the first thing. */ -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif +#define KNOWNREF "test_basic.zip.uu" +#define ENVBASE "BSDUNZIP" /* Prefix for environment variables. */ +#define PROGRAM "bsdunzip" /* Name of program being tested. */ +#define PROGRAM_ALIAS "unzip" /* Generic alias for program */ +#undef LIBRARY /* Not testing a library. */ +#undef EXTRA_DUMP /* How to dump extra data */ +#undef EXTRA_ERRNO /* How to dump errno */ +/* How to generate extra version info. */ +#define EXTRA_VERSION (systemf("%s -v", testprog) ? "" : "") -#endif +#include "test_common.h" diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_0.c similarity index 64% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_0.c index ebb06702d0c5..41279d388b1f 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_0.c @@ -1,53 +1,58 @@ /*- * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. +/* + * This first test does basic sanity checks on the environment. For + * most of these, we just exit on failure. + */ +#if !defined(_WIN32) || defined(__CYGWIN__) +#define DEV_NULL "/dev/null" +#else +#define DEV_NULL "NUL" #endif -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +DEFINE_TEST(test_0) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + struct stat st; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + failure("File %s does not exist?!", testprog); + if (!assertEqualInt(0, stat(testprogfile, &st))) { + fprintf(stderr, + "\nFile %s does not exist; aborting test.\n\n", + testprog); + exit(1); + } -#endif + failure("%s is not executable?!", testprog); + if (!assert((st.st_mode & 0111) != 0)) { + fprintf(stderr, + "\nFile %s not executable; aborting test.\n\n", + testprog); + exit(1); + } + + /* TODO: Ensure that our reference files are available. */ +} diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_C.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_C.c index ebb06702d0c5..fc11b970c2f3 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_C.c @@ -1,53 +1,41 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test C arg - match case-insensitive */ +DEFINE_TEST(test_C) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s -C %s test_basic/caps >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents CAPS\n", "test_basic/CAPS"); +} diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_L.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_L.c index ebb06702d0c5..4815cb2b139d 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_L.c @@ -1,53 +1,44 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test L arg - make names lowercase */ +DEFINE_TEST(test_L) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s -L %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "test_basic/a"); + assertTextFileContents("contents b\n", "test_basic/b"); + assertTextFileContents("contents c\n", "test_basic/c"); + assertTextFileContents("contents CAPS\n", "test_basic/caps"); +} diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_P_encryption.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_P_encryption.c index ebb06702d0c5..beabbaa646ee 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_P_encryption.c @@ -1,53 +1,41 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test P arg - password protected */ +DEFINE_TEST(test_P) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_encrypted.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s -P password %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("plaintext\n", "encrypted/file.txt"); +} diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_Z1.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_Z1.c index ebb06702d0c5..58dc750030dd 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_Z1.c @@ -1,53 +1,40 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test -Z1 arg - List filenames */ +DEFINE_TEST(test_Z1) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); + extract_reference_file(reffile); + r = systemf("%s -Z1 %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertTextFileContents("test_basic/\ntest_basic/a\ntest_basic/b\ntest_basic/c\ntest_basic/CAPS\n", "test.out"); + assertEmptyFile("test.err"); } -#endif - -#endif diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_basic.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_basic.c index ebb06702d0c5..e997755e7e57 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_basic.c @@ -1,53 +1,44 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* This test just does a basic zip decompression */ +DEFINE_TEST(test_basic) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "test_basic/a"); + assertTextFileContents("contents b\n", "test_basic/b"); + assertTextFileContents("contents c\n", "test_basic/c"); + assertTextFileContents("contents CAPS\n", "test_basic/CAPS"); +} diff --git a/contrib/libarchive/unzip/test/test_basic.zip.uu b/contrib/libarchive/unzip/test/test_basic.zip.uu new file mode 100644 index 000000000000..b55aca950327 --- /dev/null +++ b/contrib/libarchive/unzip/test/test_basic.zip.uu @@ -0,0 +1,25 @@ +begin 644 test_basic.zip +M4$L#!!0``````,J0MU8````````````````+`"``=&5S=%]B87-I8R]55`T` +M!]PX;63U.6UDW#AM9'5X"P`!!.@#```$Z`,``%!+`P04``@`"``)C;=6```` +M```````+````#``@`'1E`L``03H`P``!.@#``!+SL\K2 -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test d arg - extract to target dir */ +DEFINE_TEST(test_d) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s -d foobar %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "foobar/test_basic/a"); + assertTextFileContents("contents b\n", "foobar/test_basic/b"); + assertTextFileContents("contents c\n", "foobar/test_basic/c"); + assertTextFileContents("contents CAPS\n", "foobar/test_basic/CAPS"); +} diff --git a/contrib/libarchive/unzip/test/test_encrypted.zip.uu b/contrib/libarchive/unzip/test/test_encrypted.zip.uu new file mode 100644 index 000000000000..6aabeb9f5786 --- /dev/null +++ b/contrib/libarchive/unzip/test/test_encrypted.zip.uu @@ -0,0 +1,13 @@ +begin 644 test_encrypted.zip +M4$L#!!0``````'*1MU8````````````````*`"``96YC`L``03H`P``!.@#```!F0<``@!!10,(`*_-)-RYPDYFJ$Q9+L< +M'#C?XVBR9/=H?7U\LC!A^8<6[&CO#PM02P<(`````"@````*````4$L!`A0# +M%```````7!T +M960O9FEL92YT>'155`T`!Q@Z;608.FUD&#IM9'5X"P`!!.@#```$Z`,```&9 +?!P`"`$%%`P@`4$L%!@`````"``(`PP```-L````````` +` +end diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_glob.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_glob.c index ebb06702d0c5..b334ce4bd37d 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_glob.c @@ -1,53 +1,44 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test that the glob works */ +DEFINE_TEST(test_glob) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s %s test_*/[ab] >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "test_basic/a"); + assertTextFileContents("contents b\n", "test_basic/b"); + assertFileNotExists("test_basic/c"); + assertFileNotExists("test_basic/CAPS"); +} diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_j.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_j.c index ebb06702d0c5..a449e02644b5 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_j.c @@ -1,53 +1,44 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test j arg - don't make directories */ +DEFINE_TEST(test_j) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s -j %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "a"); + assertTextFileContents("contents b\n", "b"); + assertTextFileContents("contents c\n", "c"); + assertTextFileContents("contents CAPS\n", "CAPS"); +} diff --git a/contrib/libarchive/cpio/test/test_option_J_upper.c b/contrib/libarchive/unzip/test/test_n.c similarity index 63% copy from contrib/libarchive/cpio/test/test_option_J_upper.c copy to contrib/libarchive/unzip/test/test_n.c index 1d7d05131b3d..4e893f04b7ae 100644 --- a/contrib/libarchive/cpio/test/test_option_J_upper.c +++ b/contrib/libarchive/unzip/test/test_n.c @@ -1,59 +1,48 @@ -/*- - * Copyright (c) 2003-2009 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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_option_J_upper) +/* Test n arg - don't overrite existing files */ +DEFINE_TEST(test_n) { - char *p; + const char *reffile = "test_basic.zip"; int r; - size_t s; - /* Create a file. */ - assertMakeFile("f", 0644, "a"); + assertMakeDir("test_basic", 0755); + assertMakeFile("test_basic/a", 0644, "orig a\n"); + assertMakeFile("test_basic/b", 0644, "orig b\n"); - /* Archive it with xz compression. */ - r = systemf("echo f | %s -o -J >archive.out 2>archive.err", - testprog); - p = slurpfile(&s, "archive.err"); - p[s] = '\0'; - if (r != 0) { - if (strstr(p, "compression not available") != NULL) { - skipping("This version of bsdcpio was compiled " - "without xz support"); - return; - } - failure("-J option is broken"); - assertEqualInt(r, 0); - goto done; - } - free(p); - /* Check that the archive file has an xz signature. */ - p = slurpfile(&s, "archive.out"); - assert(s > 2); - assertEqualMem(p, "\3757zXZ", 5); -done: - free(p); + extract_reference_file(reffile); + r = systemf("%s -n %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); + + assertTextFileContents("orig a\n", "test_basic/a"); + assertTextFileContents("orig b\n", "test_basic/b"); + assertTextFileContents("contents c\n", "test_basic/c"); + assertTextFileContents("contents CAPS\n", "test_basic/CAPS"); } diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_not_exist.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_not_exist.c index ebb06702d0c5..aa660dc646e5 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_not_exist.c @@ -1,53 +1,36 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test non existant file */ +DEFINE_TEST(test_not_exist) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; + int r; + r = systemf("%s nonexist.zip >test.out 2>test.err", testprog); + assert(r != 0); + assertEmptyFile("test.out"); + assertNonEmptyFile("test.err"); } - -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif - -#endif diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_o.c similarity index 63% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_o.c index ebb06702d0c5..af0c4128686f 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_o.c @@ -1,53 +1,47 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif +/* Test o arg - overrite existing files */ +DEFINE_TEST(test_o) +{ + const char *reffile = "test_basic.zip"; + int r; -#include -#include + assertMakeDir("test_basic", 0755); + assertMakeFile("test_basic/a", 0644, "orig a\n"); + assertMakeFile("test_basic/b", 0644, "orig b\n"); -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) -{ - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + extract_reference_file(reffile); + r = systemf("%s -o %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertEmptyFile("test.err"); -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); + assertTextFileContents("contents a\n", "test_basic/a"); + assertTextFileContents("contents b\n", "test_basic/b"); + assertTextFileContents("contents c\n", "test_basic/c"); + assertTextFileContents("contents CAPS\n", "test_basic/CAPS"); } -#endif - -#endif diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_p.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_p.c index ebb06702d0c5..f34a5eae8e13 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_p.c @@ -1,53 +1,39 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test p arg - Print to stdout */ +DEFINE_TEST(test_p) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); + extract_reference_file(reffile); + r = systemf("%s -p %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertTextFileContents("contents a\ncontents b\ncontents c\ncontents CAPS\n", "test.out"); + assertEmptyFile("test.err"); } -#endif - -#endif diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_q.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_q.c index ebb06702d0c5..9a532c888366 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_q.c @@ -1,53 +1,44 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test q arg - Quiet */ +DEFINE_TEST(test_q) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s -q %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "test_basic/a"); + assertTextFileContents("contents b\n", "test_basic/b"); + assertTextFileContents("contents c\n", "test_basic/c"); + assertTextFileContents("contents CAPS\n", "test_basic/CAPS"); +} diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_singlefile.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_singlefile.c index ebb06702d0c5..70c376eb3932 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_singlefile.c @@ -1,53 +1,41 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Ensure single-file zips work */ +DEFINE_TEST(test_singlefile) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_singlefile.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("hello\n", "file.txt"); +} diff --git a/contrib/libarchive/unzip/test/test_singlefile.zip.uu b/contrib/libarchive/unzip/test/test_singlefile.zip.uu new file mode 100644 index 000000000000..589e08f12396 --- /dev/null +++ b/contrib/libarchive/unzip/test/test_singlefile.zip.uu @@ -0,0 +1,8 @@ +begin 644 test_singlefile.zip +M4$L#!!0`"``(`&"6MU8```````````8````(`"``9FEL92YT>'155`T`!U1# +M;6140VUD5$-M9'5X"P`!!.@#```$Z`,``,M(S -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test t arg - Test zip contents */ +DEFINE_TEST(test_t) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); + extract_reference_file(reffile); + r = systemf("%s -t %s >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); } -#endif - -#endif diff --git a/contrib/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libarchive/unzip/test/test_t_bad.c similarity index 65% copy from contrib/libarchive/libarchive/archive_openssl_evp_private.h copy to contrib/libarchive/unzip/test/test_t_bad.c index ebb06702d0c5..f9afbb40dae2 100644 --- a/contrib/libarchive/libarchive/archive_openssl_evp_private.h +++ b/contrib/libarchive/unzip/test/test_t_bad.c @@ -1,53 +1,39 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle +/* + * Copyright (c) 2023 Adrian Vovk * 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. + * 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" -#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED -#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test t arg - Test zip contents, but fail! */ +DEFINE_TEST(test_t_bad) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_t_bad.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); + extract_reference_file(reffile); + r = systemf("%s -t %s >test.out 2>test.err", testprog, reffile); + assert(r != 0); + assertNonEmptyFile("test.out"); + assertNonEmptyFile("test.err"); } -#endif - -#endif diff --git a/contrib/libarchive/unzip/test/test_t_bad.zip.uu b/contrib/libarchive/unzip/test/test_t_bad.zip.uu new file mode 100644 index 000000000000..ae6ad07925e6 --- /dev/null +++ b/contrib/libarchive/unzip/test/test_t_bad.zip.uu @@ -0,0 +1,25 @@ +begin 644 test_t_bad.zip +M4$L#!!0``````,J0MU8````````````````+`"``=&5S=%]B87-I8R]55`T` +M!]PX;63U.6UDW#AM9'5X"P`!!.@#```$Z`,``%!+`P04``@`"``)C;=6```` +M```````+````#``@`'1E`L``03H`P``!.@#``!+SL\K2 -#include - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include /* malloc, free */ -#include /* memset */ -static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +/* Test x arg - Exclude paths */ +DEFINE_TEST(test_x) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX)); - return ctx; -} + const char *reffile = "test_basic.zip"; + int r; -static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} -#endif + extract_reference_file(reffile); + r = systemf("%s %s -x test_basic/c >test.out 2>test.err", testprog, reffile); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertEmptyFile("test.err"); -#endif + assertTextFileContents("contents a\n", "test_basic/a"); + assertTextFileContents("contents b\n", "test_basic/b"); + assertFileNotExists("test_basic/c"); + assertTextFileContents("contents CAPS\n", "test_basic/CAPS"); +} diff --git a/lib/libarchive/tests/Makefile b/lib/libarchive/tests/Makefile index 3f18236fd455..2fe9bbcc7c2d 100644 --- a/lib/libarchive/tests/Makefile +++ b/lib/libarchive/tests/Makefile @@ -1,669 +1,677 @@ .include PACKAGE= tests WARNS?= 3 _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test TEST_METADATA.functional_test+= timeout="600" 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.test_utils.c+= -Wno-cast-align # 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_directory.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_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_filter.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_tar_invalid_pax_size.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_short_writes.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_fixup.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_entry_size_unset.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: # Fails with `test_read_disk_directory_traversals.c:1094: File at has atime 886622, 1443306049 seconds ago` BROKEN_TESTS+= test_read_disk_directory_traversals .if 0 # test_fuzz.c is not a real test, but rather a simple fuzz-test using random(). # Since this is not a regression/unit test, we don't include it by default. # If you would still like to include it, comment out the `.if 0`. TEST_SCRCS+= test_fuzz.c ${PACKAGE}FILES+= test_fuzz.cab.uu ${PACKAGE}FILES+= test_fuzz.lzh.uu ${PACKAGE}FILES+= test_fuzz_1.iso.Z.uu # Non-deterministic failures: # (Times out?) [and] crashes BROKEN_TESTS+= test_fuzz_rar .endif # https://bugs.freebsd.org/240683 BROKEN_TESTS+= test_write_filter_zstd # 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_directory_1.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_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_deflate_arm64.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_lzma2_arm.7z.uu +${PACKAGE}FILES+= test_read_format_7zip_lzma2_arm64.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_solid_zstd.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_symbolic_name.7z.uu +${PACKAGE}FILES+= test_read_format_7zip_zstd.7z.uu +${PACKAGE}FILES+= test_read_format_7zip_zstd_arm.7z.uu +${PACKAGE}FILES+= test_read_format_7zip_zstd_bcj.7z.uu +${PACKAGE}FILES+= test_read_format_7zip_zstd_nobcj.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_filter.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_bad_window_sz_in_mltarc_file.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_decode_number_out_of_bounds_read.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_sfx.exe.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_rar5_window_buf_and_size_desync.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_tar_invalid_pax_size.tar.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_deflate.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_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_format_zip_zstd.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_zstd_multi.zipx.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